1

As an example, I have the following array:

$groups = [
    'group1' => [
        'a' => 'able',
        'b' => 'baker',
        'd' => 'dog'
    ],
    'group2' => [
        'a' => 'able',
        'c' => 'charlie',
        'd' => 'dog'
    ],
    'group3' => [
        'c' => 'charlie',
        'e' => 'easy'
    ]
]

I would like to remove any duplicate items completely; from the example above, I would like the following result:

[
    'group1' => [
        'b' => 'baker'
    ],
    'group2' => [
    ],
    'group3' => [
        'e' => 'easy'
    ]
]

My current code is as follows:

foreach ($groups as $group_id => &$group_things) {
    foreach ($group_things as $thing_id => $thing) {
        foreach ($groups as $search_group_id => &$search_things) {
            if ($search_group_id == $group_id) {
                continue;
            }

            if (array_key_exists($thing_id, $search_things)) {
                unset(
                    $group_things[$thing_id],
                    $search_things[$thing_id]
                );

                $ungrouped_things[$thing_id] = $thing;
            }
        }
    }
}

This works, but has been roundly admonished by my colleagues. Is there a more elegant / less loopy way forward?

Simon Brahan
  • 2,016
  • 1
  • 14
  • 22
  • 4
    Your colleagues need to relax. This works fine and I see nothing ill about it. It's probably possible to refactor using some number of PHP's built-in `array_*()` functions (`array_merge(),array_count_values()`) or `RecursiveIterator` but this is clear and readable. – Michael Berkowski Dec 10 '14 at 14:09
  • It seems good. As a notice, you can use `array_unique()` to filter out one-dimensional array out of duplicates. – Forien Dec 10 '14 at 14:19
  • @Forien: array_unique keeps 1 instance of the duplicates, but as far as I can see, he'd like to remove the items completely, which has at least a 2nd instance. – Tibor B. Dec 10 '14 at 14:24
  • 1
    @TiborB. Yes, it was only as a side-note that there exists something like that. – Forien Dec 10 '14 at 14:26
  • I really intrested it how your colleagues solve it. – vaso123 Dec 10 '14 at 14:27
  • Maybe `array_diff` could be one solution, but i do not really see how can we prevent the loops. – vaso123 Dec 10 '14 at 14:28
  • possible duplicate of [How to remove duplicate values from a multi-dimensional array in PHP](http://stackoverflow.com/questions/307674/how-to-remove-duplicate-values-from-a-multi-dimensional-array-in-php) – Jeremiah Winsley Dec 12 '14 at 23:11

2 Answers2

0

Not sure if there's a super easy way to do this i.e. as a one-liner. But you could shorten up your code a bit and make it more modular, which might score you points:

function values($array) {
    return iterator_to_array(
        new RecursiveIteratorIterator(
            new RecursiveArrayIterator($array)
        ),false
    );
}

function duplicates($array) {
    return array_keys(
        array_filter(
            array_count_values(values($array)),
            function($v) {
                return $v>1;
            }
        )
    );
}

Then all you need to do is the following each time you need to filter a 2x array:

$duplicates = duplicates($groups);

foreach($groups as &$group) {
    $group = array_diff($group,$duplicates);
}

FYI: I'm passing $group here by reference because it's sexy, but not necessarily a good idea in for loops.

Also, if you're getting this data from a database, it may be a better idea to do this work in a query statement.

bluemoonballoon
  • 299
  • 1
  • 2
  • 8
  • Not sure it's any clearer than the existing solution, but I didn't know about the recursive iterator stuff or `array_count_values` so I'll call it a win. – Simon Brahan Dec 16 '14 at 11:19
0
foreach($groups as &$group)
  foreach($groups as &$group2)
  {
      if ($group != $group2) {
         $tmp = array_diff_assoc($group2, $group);
         $group = array_diff_assoc($group, $group2);
         $group2 = $tmp;
      }
  }

print_r($groups);

Result:

Array
(
    [group1] => Array
        (
            [b] => baker
        )

    [group2] => Array
        (
        )

    [group3] => Array
        (
            [e] => easy
        )

)

ps: it won't work with more than 2 duplicates, but this code will do it

foreach($groups as &$group)
{
  $tmp = $group;

  foreach($groups as &$group2)
  {
      if ($group != $group2) {
         $tmp    = array_diff_assoc($tmp, $group2);
         $group2 = array_diff_assoc($group2, $group);
      }
  }

  $group = $tmp;
}  

print_r($groups);
Cheery
  • 16,063
  • 42
  • 57