【问题标题】:array_unique for objects?对象的array_unique?
【发布时间】:2011-01-26 11:04:09
【问题描述】:

有没有类似 array_unique 对象的方法?我有一堆包含我合并的“角色”对象的数组,然后我想取出重复项:)

【问题讨论】:

标签: php arrays methods


【解决方案1】:

array_unique 使用 SORT_REGULAR 处理对象数组:

class MyClass {
    public $prop;
}

$foo = new MyClass();
$foo->prop = 'test1';

$bar = $foo;

$bam = new MyClass();
$bam->prop = 'test2';

$test = array($foo, $bar, $bam);

print_r(array_unique($test, SORT_REGULAR));

将打印:

Array (
    [0] => MyClass Object
        (
            [prop] => test1
        )

    [2] => MyClass Object
        (
            [prop] => test2
        )
)

在此处查看实际操作:http://3v4l.org/VvonH#v529

警告:它将使用“==”比较,而不是严格比较(“===”)。

因此,如果您想删除对象数组中的重复项,请注意它会比较每个对象的属性,而不是比较对象身份(实例)。

【讨论】:

  • 这个答案比公认的答案要好得多。但是,该示例没有显示比较值 (==) 或身份 (===) 之间的差异,因为 $bam->prop = 'test2'; (应该是 'test1' 以展示差异)。有关示例,请参阅 codepad.viper-7.com/8NxWhG
  • @vishal 看看官方文档:php.net/manual/en/function.array-unique.php
  • 你救了我兄弟,TnQ 非常好:*
  • 应该是公认的答案。这也比使用 __toString() 快得多。比较结果见:sandbox.onlinephpfunctions.com/code/…
  • 注意用array_unique()比较对象,该函数将应用深度比较,如果它涉及太多递归,可能会导致您的服务器崩溃-我正在查看您的Doctrine实体。更好地确定是什么让您的对象独一无二并用它索引您的对象。例如,如果您的对象有一个字符串标识符,则使用该标识符作为键构建一个数组。
【解决方案2】:

嗯,array_unique()比较元素的字符串值:

注意:两个元素被认为相等当且仅当(string) $elem1 === (string) $elem2,即当字符串表示相同时,将使用第一个元素。

因此,请确保在您的类中实现 __toString() 方法,并且它为相同的角色输出相同的值,例如

class Role {
    private $name;

    //.....

    public function __toString() {
        return $this->name;
    }

}

如果两个角色具有相同的名称,这将认为它们是相等的。

【讨论】:

  • @Jacob 因为array_unique__toString() 都没有比较任何东西。 __toString() 定义对象实例在字符串上下文中使用时的行为方式,array_unique 返回删除重复值的输入数组。它只是在内部使用比较。
  • @Jacob Relkin:它不是比较器。它是对象的字符串表示形式。我认为他们使用它,因为您可以将任何类型、对象等转换为字符串。但是对象上的字符串方法本身不仅由该函数使用。例如。 echo $object 也使用__toString 方法。
  • 向所有对象添加__toString() 方法要比向array_unique 添加SORT_REGULAR 标志要痛苦得多,请参阅Matthieu Napoli 他的回答。除了__toString() 方法还有许多其他用例用于对象比较,所以这甚至可能是不可能的。
【解决方案3】:

这个答案使用in_array(),因为 PHP 5 中comparing objects 的性质允许我们这样做。利用此对象比较行为要求数组包含对象,但这里似乎就是这种情况。

$merged = array_merge($arr, $arr2);
$final  = array();

foreach ($merged as $current) {
    if ( ! in_array($current, $final)) {
        $final[] = $current;
    }
}

var_dump($final);

【讨论】:

  • 效果很好,它可能比另一个更快(不知道),但我会使用你的,因为我不必为它做一个额外的功能:D
  • 在比较对象时,它们必须具有相同数量的字段并且必须是相同的键/值对才能被认为是相同的对吗?我要说的是....如果我有 2 个对象,其中一个对象有一个额外的字段,这些对象将不会被视为“相同”
  • in_array 应该使用$strict 参数!否则,您使用“==”而不是“===”来比较对象。更多信息:fr2.php.net/manual/fr/function.in-array.php
  • Not 在这里使用 strict 参数是经过深思熟虑的选择。我想找到“相等”的对象,不一定是对象的同一个实例。这在答案中提到的链接中进行了解释,该链接说,“使用比较运算符(==)时,对象变量以简单的方式进行比较,即:如果两个对象实例具有相同的属性,则它们相等和值,并且是同一类的实例。"
  • 像魅力一样工作!非常感谢
【解决方案4】:

这是一种删除数组中重复对象的方法:

<?php
// Here is the array that you want to clean of duplicate elements.
$array = getLotsOfObjects();

// Create a temporary array that will not contain any duplicate elements
$new = array();

// Loop through all elements. serialize() is a string that will contain all properties
// of the object and thus two objects with the same contents will have the same
// serialized string. When a new element is added to the $new array that has the same
// serialized value as the current one, then the old value will be overridden.
foreach($array as $value) {
    $new[serialize($value)] = $value;
}

// Now $array contains all objects just once with their serialized version as string.
// We don't care about the serialized version and just extract the values.
$array = array_values($new);

【讨论】:

  • 这对我来说是最好的解决方案!我将此解决方案用于我的网站搜索引擎(合并来自数据库的 2 个查询结果)。首先,我有所有搜索词的结果,并将它们与一些搜索词的结果合并。有了这个解决方案,我首先得到了最重要的结果,然后添加了独特的其他解决方案..
【解决方案5】:

也可以先序列化:

$unique = array_map( 'unserialize', array_unique( array_map( 'serialize', $array ) ) );

从 PHP 5.2.9 开始,您可以只使用可选的sort_flag SORT_REGULAR

$unique = array_unique( $array, SORT_REGULAR );

【讨论】:

    【解决方案6】:

    如果要根据特定属性过滤对象,也可以使用它们的 array_filter 函数:

    //filter duplicate objects
    $collection = array_filter($collection, function($obj)
    {
        static $idList = array();
        if(in_array($obj->getId(),$idList)) {
            return false;
        }
        $idList []= $obj->getId();
        return true;
    });
    

    【讨论】:

      【解决方案7】:

      从这里:http://php.net/manual/en/function.array-unique.php#75307

      这也适用于对象和数组。

      <?php
      function my_array_unique($array, $keep_key_assoc = false)
      {
          $duplicate_keys = array();
          $tmp         = array();       
      
          foreach ($array as $key=>$val)
          {
              // convert objects to arrays, in_array() does not support objects
              if (is_object($val))
                  $val = (array)$val;
      
              if (!in_array($val, $tmp))
                  $tmp[] = $val;
              else
                  $duplicate_keys[] = $key;
          }
      
          foreach ($duplicate_keys as $key)
              unset($array[$key]);
      
          return $keep_key_assoc ? $array : array_values($array);
      }
      ?>
      

      【讨论】:

        【解决方案8】:

        如果您有一个对象的索引数组,并且您想通过比较每个对象中的特定属性来删除重复项,则可以使用下面的 remove_duplicate_models() 之类的函数。

        class Car {
            private $model;
        
            public function __construct( $model ) {
                $this->model = $model;
            }
        
            public function get_model() {
                return $this->model;
            }
        }
        
        $cars = [
            new Car('Mustang'),
            new Car('F-150'),
            new Car('Mustang'),
            new Car('Taurus'),
        ];
        
        function remove_duplicate_models( $cars ) {
            $models = array_map( function( $car ) {
                return $car->get_model();
            }, $cars );
        
            $unique_models = array_unique( $models );
        
            return array_values( array_intersect_key( $cars, $unique_models ) );
        }
        
        print_r( remove_duplicate_models( $cars ) );
        

        结果是:

        Array
        (
            [0] => Car Object
                (
                    [model:Car:private] => Mustang
                )
        
            [1] => Car Object
                (
                    [model:Car:private] => F-150
                )
        
            [2] => Car Object
                (
                    [model:Car:private] => Taurus
                )
        
        )
        

        【讨论】:

          【解决方案9】:

          如果您需要从数组中过滤重复的实例(即“===”比较)并且:

          • 你确定哪个数组只包含对象
          • 您不需要保留密钥

          是:

          //sample data
          $o1 = new stdClass;
          $o2 = new stdClass;
          $arr = [$o1,$o1,$o2];
          
          //algorithm
          $unique = [];
          foreach($arr as $o){
            $unique[spl_object_hash($o)]=$o;
          }
          $unique = array_values($unique);//optional - use if you want integer keys on output
          

          【讨论】:

            【解决方案10】:

            您还可以使用回调函数使数组唯一(例如,如果您想比较对象的属性或任何方法)。

            这是我为此目的使用的通用函数:

            /**
            * Remove duplicate elements from an array by comparison callback.
            *
            * @param array $array : An array to eliminate duplicates by callback
            * @param callable $callback : Callback accepting an array element returning the value to compare.
            * @param bool $preserveKeys : Add true if the keys should be perserved (note that if duplicates eliminated the first key is used).
            * @return array: An array unique by the given callback
            */
            function unique(array $array, callable $callback, bool $preserveKeys = false): array
            {
                $unique = array_intersect_key($array, array_unique(array_map($callback, $array)));
                return ($preserveKeys) ? $unique : array_values($unique);
            }
            

            示例用法:

            $myUniqueArray = unique($arrayToFilter,
                static function (ExamQuestion $examQuestion) {
                    return $examQuestion->getId();
                }
            );
            

            【讨论】:

              【解决方案11】:

              这是一个非常简单的解决方案:

              $ids = array();
              
              foreach ($relate->posts as $key => $value) {
                if (!empty($ids[$value->ID])) { unset($relate->posts[$key]); }
                else{ $ids[$value->ID] = 1; }
              }
              

              【讨论】:

                【解决方案12】:

                array_unique 通过将元素转换为字符串并进行比较来工作。除非您的对象唯一地转换为字符串,否则它们将无法与 array_unique 一起使用。

                相反,为您的对象实现一个有状态的比较函数,并使用array_filter 丢弃该函数已经看到的东西。

                【讨论】:

                • 我希望找到一个更优雅的解决方案(不需要回调的解决方案)。不过,感谢您的回答。
                • array_unique 与 SORT_REGULAR 作品一起使用,请参阅下面的答案。
                【解决方案13】:

                这是我比较具有简单属性的对象的方式,同时接收一个独特的集合:

                class Role {
                    private $name;
                
                    public function __construct($name) {
                        $this->name = $name;
                    }
                
                    public function getName() {
                        return $this->name;
                    }
                }
                
                $roles = [
                    new Role('foo'),
                    new Role('bar'),
                    new Role('foo'),
                    new Role('bar'),
                    new Role('foo'),
                    new Role('bar'),
                ];
                
                $roles = array_map(function (Role $role) {
                    return ['key' => $role->getName(), 'val' => $role];
                }, $roles);
                
                $roles = array_column($roles, 'val', 'key');
                
                var_dump($roles);
                

                将输出:

                array (size=2)
                  'foo' => 
                    object(Role)[1165]
                      private 'name' => string 'foo' (length=3)
                  'bar' => 
                    object(Role)[1166]
                      private 'name' => string 'bar' (length=3)
                

                【讨论】:

                  【解决方案14】:

                  如果您有对象数组并且想要过滤此集合以删除所有重复项,您可以使用带有匿名函数的 array_filter:

                  $myArrayOfObjects = $myCustomService->getArrayOfObjects();
                  
                  // This is temporary array
                  $tmp = [];
                  $arrayWithoutDuplicates = array_filter($myArrayOfObjects, function ($object) use (&$tmp) {
                      if (!in_array($object->getUniqueValue(), $tmp)) {
                          $tmp[] = $object->getUniqueValue();
                          return true;
                      }
                      return false;
                  });
                  

                  重要提示:请记住,您必须将 $tmp 数组作为引用传递给您的过滤器回调函数,否则它将不起作用

                  【讨论】:

                    猜你喜欢
                    • 2020-09-06
                    • 2021-02-25
                    • 2011-06-07
                    • 2013-11-27
                    • 2014-10-07
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多