如何在数组数组上使用 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_referencereference。可能这些就像临时 ID。

【讨论】:

没想到你可以改进,但你做到了! @x-yuri:你能解释一下为什么 array_intersect_key 会这样工作吗?将简单数组 Array ( [0] => 33 ) 与数组数组相交,即 $arr = array( array('user_id' => 33, 'tmp_id' => 3), ... );?即使存在类型不匹配(和其他级别),它也会以某种方式匹配这 33 个 @Mat90 var_dump(array_intersect([33], [['user_id' =&gt; 33, 'tmp_id' =&gt; 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 不能递归工作,所以它只是认为“这都是Arrays,让我们杀死除一个之外的所有......我们走吧!”

【讨论】:

以上是关于如何在数组数组上使用 array_unique?的主要内容,如果未能解决你的问题,请参考以下文章

PHP使用array_unique对二维数组去重处理

php数组的重复值如何过滤掉

array_unique 显示错误数组到字符串的转换

如何从 PHP 中的数组中删除重复值

php [PHP] array_unique在一个多维数组中

数组去重