1

I have an array that consists of an undetermined number of arrays, recursively (n levels deep). Each array might contain a name key. I want to create a unique list of those values.

Example

Suppose the array is:

$bigArray = array(
    'name'=>'one', 
    'something'=>array(
        'name'=>'two', 
        'subthing'=>array('name'=>'three')
    ), 
    'anotherthing'=>array('name'=>'one')
);

The expected result would be:

$uniques = array('one', 'two', 'three') // All the 'name' keys values and without duplicates.

Here's a fiddle of my attempt.

My approach was using array_walk_recursive passing a $uniques array as reference, and allowing the function to update that value:

$uniques = array();

function singleOut($item, $key, &$uniques) {
    if ($key == 'name' && !in_array($itm,$uniques,true) )
        $uniques[] = $item;
}

array_walk_recursive($bigArray, 'singleOut', $uniques);

However, it's not working for me.

mickmackusa
  • 43,625
  • 12
  • 83
  • 136
Alain Jacomet Forte
  • 4,575
  • 6
  • 29
  • 41

3 Answers3

0

You could use also array_unique on this one too. Example:

$uniques = array();
array_walk_recursive($bigArray, function($val, $key) use (&$uniques){
    if($key == 'name') {
        $uniques[] = $val;
    }
});

$uniques = array_unique($uniques); // unique values
Kevin
  • 41,694
  • 12
  • 53
  • 70
  • For the scope of this question, a loose comparison is just fine. However, visit this link to see the potential unexpected outcomes (thanks to PHP's type juggling) when using loose comparisons on keys that _may_ be `0`. [Type juggling while making loose comparison yields unwanted result](https://stackoverflow.com/questions/44426990/type-juggling-while-making-loose-comparison-yields-unwanted-result)] – mickmackusa Sep 08 '17 at 02:23
0

Your fiddle was nearly spot on - the problem was, that the user parameter is given by-reference only within same levels of recursion. You need to use indirection with a reference:

$bigArray = array(
    'name'=>'one', 
    'something'=>array(
        'name'=>'two', 
        'subthing'=>array('name'=>'three')
    ), 
    'anotherthing'=>array('name'=>'one')
);

function singleOut($item, $key, $indirect) {
    $uniques=&$indirect[0];
    if ($key == 'name' && !in_array($item,$uniques,true) ) $uniques[] = $item;

}

$uniques = array();
$indirect = array(&$uniques);
array_walk_recursive($bigArray, 'singleOut', $indirect);
print_r($uniques);

Edit:

Fiddle is here

Eugen Rieck
  • 64,175
  • 10
  • 70
  • 92
0

To avoid doing an in_array() check inside of array_walk_recursive(), you can store name values as keys in the output array. This will effectively eliminate duplicates by overwriting previous identical keys. When array_walk_recursive() is finished, you can use array_keys() to move the data from keys to values.

Code: (Demo)

$bigArray=[
    'name'=>'one', 
    'something'=>[
        'name'=>'two', 
        'subthing'=>['name'=>'three']
    ], 
    'anotherthing'=>['name'=>'one']
];
array_walk_recursive($bigArray,function($v,$k)use(&$uniques){
    if($k==='name')
        $uniques[$v]='';
});
var_export(array_keys($uniques));

Output:

array (
  0 => 'one',
  1 => 'two',
  2 => 'three',
)

Because array_unique() can be slow in some cases, using array_keys() should generally perform faster. That said, if micro-optimization is a concern then you should do benchmark testing using your actual data and your specific environment and select the best method for your project.

As I mentioned in a comment under Ghost's answer, it is a good habit to make === strict comparisons on keys in your multi-dimensional array because if you are looking for a string, but encounter a 0 key, then PHP's type juggling "feature" will provide unexpected results.

Here is a page where I discuss and demonstrate this behavior: Type juggling while making loose comparison yields unwanted result

mickmackusa
  • 43,625
  • 12
  • 83
  • 136