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

在PHP中,如何删除对象数组中的重复项,其中重复项定义为具有相同值的键值对的子集,php,arrays,duplicates,Php,Arrays,Duplicates,我有一个表单数组: 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 = 15901570

我有一个表单数组:

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
匹配

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

这样的解决方案存在吗


谢谢帮助新手:

< P>虽然没有内置的东西可以直接使用,但是没有足够的代码来处理任意数量的属性来考虑唯一性。通过跟踪查找数组中的每个唯一属性,我们可以构建一个以叶节点(即不是数组本身的节点)为对象的数组

我们通过在数组中保留对当前级别的引用(
&
),然后继续为每个属性构建查找数组来实现这一点

函数find_uniques($list,$properties){
$lookup=[];
$unique=[];
$last_idx=计数($properties)-1;
//构建我们的查找数组-叶节点将是项本身,
//位于与要查看的属性数量匹配的级别上
考虑重复
foreach($项目列表){
$current=&$lookup;
foreach($idx=>$property的属性){
//最后一级,保留对象以备将来参考
如果($idx==$last\u idx){
$current[$item->$property]=$item;
打破
}如果(!isset($current[$item->$property]),则为else{
//否则,如果尚未设置,则创建空数组
$current[$item->$property]=[];
}
//下一次迭代将在此级别上开始,作为其当前级别
$current=&$current[$item->$property];
}
}
//awr只调用叶节点(即我们的项)的回调。
数组\u walk\u递归($lookup,函数($item)use(&$unique){
$unique[]=$item;
});
返回$unique;
}
使用上面的数据调用,要求返回uniques和副本的最后一个元素,我们得到以下结果:

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)
  }
}
在您的示例中,它映射到元素
[0]
和元素
[2]
。如果您希望保留第一个对象作为重复对象,请添加一个isset,如果已看到属性值,则该isset将终止内部循环:

foreach($idx=>$property的属性){
如果($idx==$last\u idx){
if(isset($current[$item->$property])){
打破
}
$current[$item->$property]=$item;
}否则{
$current[$item->$property]=[];
}
//下一次迭代将在此级别上开始,作为其当前级别
$current=&$current[$item->$property];
}
需要注意的是,在编写本文时,假设要检查唯一性的数组本身不包含数组(因为我们使用
->
查找属性,并且使用
array\u walk\u recursive
查找任何非数组的内容)。

这很有趣:

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)));
  • 首先,提取
    时间戳
    并对该数组进行降序排序,从而对原始数组进行排序
  • 然后,映射以使用
    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']);
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']);

另一种选择是将其升序排序,然后构建一个以
dp
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_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']);
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']);
最终结果将是(根据您的示例)在
$final\u数组中:

[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'?)
或类似参数传递两个值


更新

我没有意识到你也想要最后一个值。这实际上似乎是一项更容易的任务。也许我遗漏了什么,但想出一个不同于其他答案的方法是很有趣的:-)

$final\u arr[]
的输出将是(在您的示例中)


在您的示例中,应该返回对象0和对象2,对吗?对象0是因为它是唯一的,对象2是因为它是最后一个副本?是的,没错,MatsLindh。这不是创建类的好方法。我希望这是为了证明你想要达到的目标:-)你放弃了吗?你有3个答案。我正在一个更大的数据集上审查和测试这些建议。请注意
Array
(
    [0] => anim Object
        (
            [qs] => fred
            [dp] => shorts
            [cg] => dino
            [timestamp] => 1590157029399
        )

    [1] => anim Object
        (
            [qs] => fred
            [dp] => tshirt
            [cg] => bird
            [timestamp] => 1590117032286
        )

)