如何在数组数组上使用 array_unique?
Posted
技术标签:
【中文标题】如何在数组数组上使用 array_unique?【英文标题】:How do I use array_unique on an array of arrays? 【发布时间】:2011-02-03 09:52:53 【问题描述】:我有一个数组
Array(
[0] => Array
(
[0] => 33
[user_id] => 33
[1] => 3
[frame_id] => 3
)
[1] => Array
(
[0] => 33
[user_id] => 33
[1] => 3
[frame_id] => 3
)
[2] => Array
(
[0] => 33
[user_id] => 33
[1] => 8
[frame_id] => 8
)
[3] => Array
(
[0] => 33
[user_id] => 33
[1] => 3
[frame_id] => 3
)
[4] => Array
(
[0] => 33
[user_id] => 33
[1] => 3
[frame_id] => 3
)
)
如您所见,密钥 0 与 1、3 和 4 相同。而密钥 2 则完全不同。
在它们上运行array_unique函数时,唯一的左边是
Array (
[0] => Array
(
[0] => 33
[user_id] => 33
[1] => 3
[frame_id] => 3
)
)
知道为什么 array_unique 没有按预期工作吗?
【问题讨论】:
【参考方案1】:这是@ryeguy's answer的改进版:
<?php
$arr = array(
array('user_id' => 33, 'tmp_id' => 3),
array('user_id' => 33, 'tmp_id' => 4),
array('user_id' => 33, 'tmp_id' => 5)
);
# $arr = array_intersect_key($arr, array_unique(array_map('serialize', $arr)));
$arr = array_intersect_key($arr, array_unique(array_map(function ($el)
return $el['user_id'];
, $arr)));
//result:
array
0 =>
array
'user_id' => int 33
'tmp_id' => int 3
首先,它不会进行不必要的序列化。其次,有时属性可能不同,即使 id 相同。
这里的诀窍是array_unique()
保留了密钥:
$ php -r 'var_dump(array_unique([1, 2, 2, 3]));'
array(3)
[0]=>
int(1)
[1]=>
int(2)
[3]=>
int(3)
这让我们array_intersect_key()
留下想要的元素。
我遇到过Google Places API。我将几个请求的结果与不同类型的对象(想想标签)结合起来。但是我得到了重复,因为一个对象可能被分为几个类别(类型)。而serialize
的方法不起作用,因为attrs不同,即photo_reference
和reference
。可能这些就像临时 ID。
【讨论】:
没想到你可以改进,但你做到了! @x-yuri:你能解释一下为什么 array_intersect_key 会这样工作吗?将简单数组 Array ( [0] => 33 ) 与数组数组相交,即 $arr = array( array('user_id' => 33, 'tmp_id' => 3), ... );?即使存在类型不匹配(和其他级别),它也会以某种方式匹配这 33 个 @Mat90var_dump(array_intersect([33], [['user_id' => 33, 'tmp_id' => 3]]));
?它给了我一个空数组。但是这样一来,var_dump(array_intersect(['Array'], [[]]));
,就有了匹配,因为...因为它是 php :) 好吧,我不是认真的。开玩笑的,因为它compares the elements的方式。如果你举一个匹配的例子,我可能会说出原因。您可能想要指定您的版本。此外,尝试提高错误报告级别,如果不是***别。这可能会有所帮助。
@x-yuri,这是您的示例步骤:array_map(...) 产生(使用 print_r):Array ([0] => 33 [1] => 33 [2] = > 33 ), array_unique(..): Array ( [0] => 33 ) 和 print_r($arr): Array ( [0] => Array ( [user_id] => 33 [tmp_id] => 3 ) [1 ] => Array ( [user_id] => 33 [tmp_id] => 4 ) [2] => Array ( [user_id] => 33 [tmp_id] => 5 ) ) 那么最终将是 Array ( [0] = > 数组([user_id] => 33 [tmp_id] => 3))。这里发生了怎样的魔法?它以某种方式决定进行更深入的比较(我认为字符串比较会出错,因为第二个以 Array [vs 33] 作为第一个元素)?
@Mat90 我想我现在明白是什么让你感到困惑了。我已经更新了答案,请查看。请注意array_unique()
返回的数组的键不是0, 1, 2
。他们是0, 1, 3
。这让我们 array_intersect_key()
返回所需的元素。【参考方案2】:
`
$arr = array(
array('user_id' => 33, 'tmp_id' => 3),
array('user_id' => 33, 'tmp_id' => 4),
array('user_id' => 33, 'tmp_id' => 3),
array('user_id' => 33, 'tmp_id' => 4),
);
$arr1 = array_unique($arr,SORT_REGULAR);
echo "<pre>";
print_r($arr1);
echo "</pre>";
Array(
[0] => Array(
[user_id] => 33
[tmp_id] => 3
)
[1] => Array(
[user_id] => 33
[tmp_id] => 4
)
)
`
【讨论】:
请在您的代码中附上一些解释。【参考方案3】:function array_unique_recursive($array)
$array = array_unique($array, SORT_REGULAR);
foreach ($array as $key => $elem)
if (is_array($elem))
$array[$key] = array_unique_recursive($elem);
return $array;
难道这不是诀窍吗?
【讨论】:
【参考方案4】:快速解答 (TL;DR)
可以使用 foreach 从 PHP 的 AssociativeArrays 数组中提取不同的值 这是一种简单的方法详细解答
上下文
PHP 5.3 PHP Array of AssociativeArrays(表格复合数据变量) 此复合变量的替代名称是 ArrayOfDictionary (AOD)问题
场景: DeveloperMarsher 有一个 PHP 表格复合变量 DeveloperMarsher 希望提取特定名称-值对的不同值 在下面的示例中,DeveloperMarsher 希望为每个不同的fname
名称-值对获取行
解决方案
例子01;; DeveloperMarsher 以如下所示的表格数据变量开始
$aodtable = json_decode('[ “fname”:“本垒打” "lname": "辛普森" , “fname”:“本垒打” "lname": "杰克逊" , “fname”:“本垒打” "lname": "约翰逊" , “fname”:“巴特” "lname": "约翰逊" , “fname”:“巴特” "lname": "杰克逊" , “fname”:“巴特” "lname": "辛普森" , “fname”:“弗雷德” "lname": "燧石" ]',真的);例子01;; DeveloperMarsher 可以使用跟踪所见值的 foreach 循环提取不同的值
$sgfield = 'fname'; $bgnocase = 真; // $targfield = $sgfield; $ddseen = 数组(); $vout = 数组(); foreach ($aodtable as $datarow) if( (boolean) $bgnocase == true ) @$datarow[$targfield] = @strtolower($datarow[$targfield]); if( (string) @$ddseen[ $datarow[$targfield] ] == '' ) $rowout = array_intersect_key($datarow, array_flip(array_keys($datarow))); $ddseen[ $datarow[$targfield] ] = $datarow[$targfield]; $vout[] = 数组($rowout); //;; 打印 var_export( $vout, true );输出结果
大批 ( 0 => 大批 ( 0 => 大批 ( 'fname' => '本垒打', 'lname' => '辛普森', ), ), 1 => 大批 ( 0 => 大批 ( 'fname' => '巴特', 'lname' => '约翰逊', ), ), 2 => 大批 ( 0 => 大批 ( 'fname' => '弗雷德', 'lname' => '燧石', ), ), )陷阱
此解决方案不会聚合不属于 DISTINCT 操作的字段 从任意选择的不同行返回任意名称-值对 输出的任意排序顺序 任意处理字母大小写(大写 A 与小写 a 不同吗?)另见
php array_intersect_key php 数组翻转【讨论】:
【参考方案5】:array_unique()
在 PHP 5.2.9 及更高版本中仅支持多维数组。
相反,您可以创建数组的哈希并检查它的唯一性。
$hashes = array();
foreach($array as $val)
$hashes[md5(serialize($val))] = $val;
array_unique($hashes);
【讨论】:
序列化的字符串应该足以比较字符串。当使用带有长序列化字符串的 md5 时,可能会发生冲突。另外,我会在唯一之前和之后使用带有 un/serialize 的 array_map。 @Gordon 冲突是如此不可能,以至于根本不值得担心。 @ryeguy 取决于您的应用程序。并且也不会改变它首先是不必要的。 没有必要使用这个概念调用array_unique
,因为在构建$hashes
时这已经是唯一的了,最好使用array_values
来获取基于整数的数组。【参考方案6】:
这是因为array_unique
使用字符串比较来比较项目。来自docs:
注意:考虑两个元素 当且仅当(字符串)$elem1 相等 === (字符串) $elem2.换句话说:当字符串表示相同时。 将使用第一个元素。
数组的字符串表示就是单词Array
,不管它的内容是什么。
您可以通过以下方式做您想做的事情:
$arr = array(
array('user_id' => 33, 'frame_id' => 3),
array('user_id' => 33, 'frame_id' => 3),
array('user_id' => 33, 'frame_id' => 8)
);
$arr = array_intersect_key($arr, array_unique(array_map('serialize', $arr)));
//result:
array
0 =>
array
'user_id' => int 33
'user' => int 3
2 =>
array
'user_id' => int 33
'user' => int 8
它是这样工作的:
每个数组项都被序列化。这 将是唯一的基于数组的 内容。
这个结果通过array_unique
运行,
所以只有具有唯一性的数组
留下签名。
array_intersect_key
将占用
唯一项目的键来自
map/unique 函数(因为源数组的键被保留)和拉
它们来自您的原始来源
数组。
【讨论】:
希望我可以 +1 两次。漂亮。 您能否快速详细说明这种方法有多复杂。例如,如果数组“arr”中有 n 个项目,并且每个项目有 m 个属性。我只是想知道它是否适合我的应用程序,如果有大约 10 到 50 个项目,每个项目大约有 5 到 15 个属性。 @ryeguy 我想这会让我所有的希望和梦想成真! @PascalKlein - 这是一个很好的问题,但我很失望它没有得到回答。该解决方案有效,但序列化数组的每个成员对于具有大型子阵列的大型阵列/阵列的扩展性较差。但是,根据您的具体情况,这可能是唯一可行的解决方案。在我的情况下,我可以通过做出一些假设来简化(如果 $a['id'] === $b['id'] 然后假设 $a === $b),但在其他方面模仿这个逻辑。也就是说,我用我自己的回调替换了'serialize'
,它只返回$arg['id']
,这比serialize
快得多。
请注意,有时json_encode
可能更快,所以也要检查一下。【参考方案7】:
array_unique deos 不能递归工作,所以它只是认为“这都是Array
s,让我们杀死除一个之外的所有......我们走吧!”
【讨论】:
以上是关于如何在数组数组上使用 array_unique?的主要内容,如果未能解决你的问题,请参考以下文章