如何在一个键值不同的php数组中查找和删除几乎重复的行?

Posted

技术标签:

【中文标题】如何在一个键值不同的php数组中查找和删除几乎重复的行?【英文标题】:How find and delete almost duplicate rows in php array, which differ in one key value? 【发布时间】:2018-02-05 18:06:36 【问题描述】:

我有以下数组(数组很大):

Array ( 
   [0] => Array ( 
              [id] => 1 
              [timestamp] => 1503050400
              [name] =>  Event A
              [value] =>  )
   [1] => Array ( 
              [id] => 2 
              [timestamp] => 1503446400
              [name] => Event B 
              [value] =>  )
   [2] => Array ( 
              [id] => 2 
              [timestamp] => 1503446400
              [name] => Event B 
              [value] => 71 )
   [3] => Array ( 
              [id] => 3 
              [timestamp] => 1503720000
              [name] => Event C
              [value] => 12 )
   [4] => Array ( 
              [id] => 3 
              [timestamp] => 1503720000
              [name] => Event C 
              [value] =>  )

              ...
)

如您所见,一些数组键(行)具有相同的 ID、时间戳和名称,但值不同。 我想查找并取消设置($array[$key]) 满足以下条件的行:

如果数组有相同名称、ID 和时间戳的键,删除这些键,但只留下 Value != null

看起来像这样:

foreach ($array as $key => $row) 
  if ( ... ) 
    unset($array[$key]);
  
else 


print_r($array);

输出应该是:

Array ( 
   [0] => Array ( 
              [id] => 1 
              [timestamp] => 1503050400
              [name] =>  Event A
              [value] =>  )
   [2] => Array ( 
              [id] => 2 
              [timestamp] => 1503446400
              [name] => Event B 
              [value] => 71 )
   [3] => Array ( 
              [id] => 3 
              [timestamp] => 1503720000
              [name] => Event C
              [value] => 12 )

              ...
)

【问题讨论】:

为什么不只过滤那些有空value的项目? 因为这是一个动态数组,调度中有一个Id,Name,Timestamp和空值,当事件发生时,新行被添加值,但旧的空值保留。 如果(对于相同的idtimestampname)它们都具有null 值,则应该删除哪个项目? 数组键(行)应该被移除。有哪只!重复的 id、时间戳、名称但值为 null 【参考方案1】:

您可以使用array_reduce()array_filter()

<?php

$data = array(
    array(
        'id' => 1,
        'timestamp' => 1503050400,
        'name' => 'Event A',
        'value' => null,
    ),
    array(
        'id' => 2,
        'timestamp' => 1503446400,
        'name' => 'Event B',
        'value' => null,
    ),
    array(
        'id' => 2,
        'timestamp' => 1503446400,
        'name' => 'Event B',
        'value' => 71,
    ),
    array(
        'id' => 3,
        'timestamp' => 1503720000,
        'name' => 'Event C',
        'value' => 12,
    ),
    array(
        'id' => 3,
        'timestamp' => 1503720000,
        'name' => 'Event C',
        'value' => null,
    ),
);

/**
 * Reduce the array of items to an array of buckets, where
 * each bucket contains elements with the same
 *
 * - id
 * - timestamp
 * - name
 *
 * so that we can than take a look at the contents of the
 * individual buckets.
 */
$buckets = array_reduce(
    $data,
    function (array $carry, array $item) 
        /**
         * create an index from
         *
         * - id
         * - timestamp
         * - name
         */
        $index = serialize(array(
            'id' => $item['id'],
            'timestamp' => $item['timestamp'],
            'name' => $item['name'],
        ));

        /**
         * initialize empty bucket if we don't have one yet for this index
         */
        if (!array_key_exists($index, $carry)) 
            $carry[$index] = array();
        

        /**
         * add item to bucket
         */
        $carry[$index][] = $item;

        return $carry;
    ,
    array()
);

/**
 * Reduce the content of the buckets to elements that match the requirements.
 */
$filtered = array_reduce(
    $buckets,
    function (array $carry, array $items) 
        /**
         * if we have only one item in the bucket, let's take it
         */
        if (1 === count($items)) 
            $carry[] = array_shift($items);

            return $carry;
        

        /**
         * find all items where the value is not null
         */
        $withoutNullValue = array_filter($items, function (array $item) 
            return array_key_exists('value', $item) && null !== $item['value'];
        );

        /**
         * if we have any items where the value is not null, take all of them
         */
        if (0 < count($withoutNullValue)) 
            $carry = array_merge(
                $carry,
                $withoutNullValue
            );

            return $carry;
        

        /**
         * if all of the items have a value of null, let's just take the first
         */
        $carry[] = array_shift($items);

        return $carry;
    ,
    array()
);

var_dump($filtered);

参考见:

http://php.net/manual/en/function.array-reduce.php http://php.net/manual/en/function.array-filter.php

例如,请参阅:

https://3v4l.org/Toq75

【讨论】:

太棒了,它有效!非常感谢,@localheinz!如此复杂,我认为应该容易得多。 很高兴你喜欢它,@Anton_Dev!问题是我们需要迭代两次:1)填充桶和 2)然后检查它们。在我们将它们扔进桶之前,我们无法真正判断数组中的任何项目是否可以保留或需要离开。【参考方案2】:
foreach ($array as $key => $row) 
  if ($row[value]) 
    unset($array[$key]);
  
else 
  $row[result] = $row[value];
  unset($row[value]);
  

print_r($array);

【讨论】:

以上是关于如何在一个键值不同的php数组中查找和删除几乎重复的行?的主要内容,如果未能解决你的问题,请参考以下文章

php数组元素值的修改和删除问题

javascript 根据键值在数组中查找和删除对象

php:如何获取键值的索引数组[重复]

如何在数组中查找一个值并使用 PHP 数组函数将其删除?

PHP 一个字符串,如何提取其中相同的字符?

PHP获取数组中重复值的键值