在 PHP 中,如何删除对象数组中的重复项,其中重复项定义为具有相同值的键值对的子集

我有一个形式的数组:


class anim {

    public $qs;

    public $dp;

    public $cg;

    public $timestamp;

}

$animArray = array();


$myAnim = new anim();

$myAnim->qs = "fred";

$myAnim->dp = "shorts";

$myAnim->cg = "dino";

$myAnim->timestamp = 1590157029399;

$animArray[] = $myAnim;


$myAnim = new anim();

$myAnim->qs = "barney";

$myAnim->dp = "tshirt";

$myAnim->cg = "bird";

$myAnim->timestamp = 1590133656330;

$animArray[] = $myAnim;


$myAnim = new anim();

$myAnim->qs = "fred";

$myAnim->dp = "tshirt";

$myAnim->cg = "bird";

$myAnim->timestamp = 1590117032286;

$animArray[] = $myAnim;

如何创建一个仅包含 $animArray 的非重复项(以及找到重复项的最新条目)的新数组,其中重复项定义为:


其中一个$myAnim->dp与另一个数组元素的值相同$myAnim->dp,并且$myAnim->cg第一个和$myAnim->cg第二个的值彼此相同。


在上面的示例中,只有第一个元素根据该定义是唯一的。


我希望有一个优雅的解决方案。我已经阅读了 PHP 手册中的所有数组函数,但看不到它是如何实现的。


我可以遍历每个数组元素,检查是否$myAnim->dp与另一个数组元素的值相同$myAnim->dp,将匹配项保存到一个新数组中,然后遍历该新数组,检查它是否与该新数组中的任何其他元素$myAnim->cg匹配。$myAnim->cg


一个更优雅的解决方案将允许我更改键值对的哪些组合来确定是否存在重复项,而无需重新编写很多代码。


这样的解决方案存在吗?


感谢您帮助这个新手:)


ABOUTYOU
浏览 229回答 3
3回答

婷婷同学_

虽然没有内置的东西可以直接开箱即用,但也不需要大量代码来处理任意数量的属性以考虑唯一性。通过跟踪查找数组中的每个唯一属性,我们可以构建一个数组,其中叶节点(即那些本身不是数组的节点)是对象。为此,我们&在数组中保留对当前级别的引用 ( ),然后继续为每个属性构建我们的查找数组。function find_uniques($list, $properties) {    $lookup = [];    $unique = [];    $last_idx = count($properties) - 1;    // Build our lookup array - the leaf nodes will be the items themselves,    // located on a level that matches the number of properties to look at    // to consider a duplicate    foreach ($list as $item) {        $current = &$lookup;        foreach ($properties as $idx => $property) {            // last level, keep object for future reference            if ($idx == $last_idx) {                $current[$item->$property] = $item;                break;            } else if (!isset($current[$item->$property])) {                // otherwise, if not already set, create empty array                $current[$item->$property] = [];            }            // next iteration starts on this level as its current level            $current = &$current[$item->$property];        }    }    // awr only calls the callback for leaf nodes - i.e. our items.    array_walk_recursive($lookup, function ($item) use (&$unique) {        $unique[] = $item;    });    return $unique;}使用上面的数据调用,并且要求是唯一的并且返回重复项的最后一个元素,我们得到以下结果:var_dump(find_uniques($animArray, ['dp', 'cg']));array(2) {  [0] =>  class anim#1 (4) {    public $qs =>    string(4) "fred"    public $dp =>    string(6) "shorts"    public $cg =>    string(4) "dino"    public $timestamp =>    int(1590157029399)  }  [1] =>  class anim#3 (4) {    public $qs =>    string(4) "fred"    public $dp =>    string(6) "tshirt"    public $cg =>    string(4) "bird"    public $timestamp =>    int(1590117032286)  }}在您的示例中映射到 element[0]和 element [2]。如果您想要保留第一个对象以备重复,请添加一个 isset 以在属性值已被看到时终止内部循环:foreach ($properties as $idx => $property) {    if ($idx == $last_idx) {        if (isset($current[$item->$property])) {            break;        }        $current[$item->$property] = $item;    } else {        $current[$item->$property] = [];    }    // next iteration starts on this level as its current level    $current = &$current[$item->$property];}重要的是要注意,这是在假设您要检查唯一性的数组本身不包含数组的情况下编写的(因为我们正在查找属性,并且因为我们正在使用查找->任何array_walk_recursive不是大批)。

holdtom

这很有趣:array_multisort(array_column($animArray, 'timestamp'), SORT_DESC, $animArray);$result = array_intersect_key($animArray,          array_unique(array_map(function($v) { return $v->dp.'-'.$v->cg; }, $animArray)));首先,提取timestamp并对该数组进行降序排序,从而对原始数组进行排序。dp然后,映射以使用和组合创建一个新数组cg。接下来,使组合数组唯一,这将保留遇到的第一个重复项(这就是我们降序排序的原因)。最后,得到原始数组的键和唯一键的交集。在具有动态属性的函数中:function array_unique_custom($array, $props) {    array_multisort(array_column($array, 'timestamp'), SORT_DESC, $array);    $result = array_intersect_key($array,              array_unique(array_map(function($v) use ($props) {                  return implode('-', array_map(function($p) use($v) { return $v->$p; }, $props));;              },              $array)));    return $result;}$result = array_unique_custom($animArray, ['dp', 'cg']);另一种选择是将其升序排序,然后构建一个以 adp和cg组合为键的数组,这将保留最后一个副本:array_multisort(array_column($animArray, 'timestamp'), SORT_ASC, $animArray);foreach($animArray as $v) {    $result[$v->dp.'-'.$v->cg] = $v;}在具有动态属性的函数中:function array_unique_custom($array, $props) {    array_multisort(array_column($array, 'timestamp'), SORT_ASC, $array);    foreach($array as $v) {        $key = implode(array_map(function($p) use($v) { return $v->$p; }, $props));        $result[$key] = $v;    }    return $result;}$result = array_unique_custom($animArray, ['dp', 'cg']);

浮云间

//Create an array with dp and cg values only$new_arr = [];foreach($animArray as $key=>$item) {    $new_arr[] = $item->dp.','.$item->cg;}$cvs = array_count_values($new_arr);$final_array = [];foreach($cvs as $cvs_key=>$occurences) {    if ($occurences == 1) {        $filter_key = array_keys($new_arr, $cvs_key)[0];                 $final_array[$filter_key] = $animArray[$filter_key];        }}最终结果将是(根据您的示例)$final_array:[0] => anim Object    (        [qs] => fred        [dp] => shorts        [cg] => dino        [timestamp] => 1590157029399    )一些解释://Create a new array based on your array of objects with the attributes dp and cg//with a comma  between them$new_arr = [];foreach($animArray as $key=>$item) {    $new_arr[] = $item->dp.','.$item->cg;}/*$new_arr now contains:    [0] => shorts,dino    [1] => tshirt,bird    [2] => tshirt,bird*///Use builtin-function array_count_values to get the nr of occurences for //each item in an array$cvs = array_count_values($new_arr);/*$cvs would contain:(    [shorts,dino] => 1    [tshirt,bird] => 2)*///Iterate through the $cvs array.//Where there are only one occurence (no duplicates)//create a final array $final_array$final_array = [];foreach($cvs as $cvs_key=>$occurences) {    if ($occurences == 1) {        /*        array_keys with second argument $csv_key searches for key with         with the key from $cvs-key        so basically search for:        shorts,dino and retrieve the key 0 (first element)                */        $filter_key = array_keys($new_arr, $cvs_key)[0];                 /*        Add a new item to the $final_array based on the key in        the original array $animArray        if you don't want the original key in the new array        you could just do $final_array[] instead of         $final_array[$filter_key]        */        $final_array[$filter_key] = $animArray[$filter_key];        }}你说你想要某种功能测试不同的属性。我相信它只是制作一个函数/方法,您将两个值传递给参数$attr1 ('dp'?), $attr2('cg'?)或类似的东西。更新我没有意识到你也想要最后一个值。这实际上似乎是一项更容易的任务。也许我遗漏了一些东西,但是想出一种与其他答案不同的方法很有趣 :-)//Create an array with dp and cg values only$new_arr = [];foreach($animArray as $key=>$item) {    $new_arr[] = $item->dp.','.$item->cg;}//Sort keys descending orderkrsort($new_arr); //Because of sending order of keys above, the unique values would return the //last item of the duplicates$new_arr2 = array_unique($new_arr); //Switch order of keys back to normal (ascending)ksort($new_arr2); //Create a new array based on the keys set in $new_arr2//$final_arr = [];foreach($new_arr2 as $key=>$item) {    $final_arr[] = $animArray[$key];}的输出$final_arr[]将是(在你的例子中)Array(    [0] => anim Object        (            [qs] => fred            [dp] => shorts            [cg] => dino            [timestamp] => 1590157029399        )    [1] => anim Object        (            [qs] => fred            [dp] => tshirt            [cg] => bird            [timestamp] => 1590117032286        ))
打开App,查看更多内容
随时随地看视频慕课网APP