【问题标题】:How do I use array_unique on an array of arrays?如何在数组数组上使用 array_unique?
【发布时间】: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 没有按预期工作吗?

【问题讨论】:

    标签: php arrays 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
        

        它是这样工作的:

        1. 每个数组项都被序列化。这 将是唯一的基于数组的 内容。

        2. 这个结果通过array_unique运行, 所以只有具有唯一性的数组 留下签名。

        3. 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,让我们杀死除一个之外的所有......我们走吧!”

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-07-09
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-12-15
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多