How find and delete almost duplicate rows in php array, which differ in one key value?

296 Views Asked by At

I have the following array (array is large):

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] =>  )

              ...
)

As you can see, some array keys (rows) have same ID, Timestamp, and Name, but different Value. I would like to find and unset($array[$key]) rows which meet the following conditions:

if array has keys with the same Name, Id and Timestamp, delete this keys, but leave only with Value != null

Looking something like this:

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

}
print_r($array);

The output should be:

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 )

              ...
)
2

There are 2 best solutions below

2
On BEST ANSWER

You can use array_reduce() and 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);

For reference, see:

For an example, see:

0
On
foreach ($array as $key => $row) {
  if ($row[value]) {
    unset($array[$key]);
  }
else {
  $row[result] = $row[value];
  unset($row[value]);
  }
}
print_r($array);