2
\$\begingroup\$

What I've been working on is some kind of function that can take basically any well constructed multi-dimensional array and remove any and all duplicates within matching key sets.

The particular function I've designed provides convenience and functionality but it is far from pretty and seemingly slow compared to a possibly more optimized function.

Sample Data

$echo['level-one'][] = '1';
$echo['level-one'][] = '1';
$echo['level-one'][] = '1';
$echo['level-one'][] = '1';
$echo['level-one']['level-two'][] = '1';
$echo['level-one']['level-two'][] = '1';
$echo['level-one']['level-two'][] = '1';
$echo['level-one']['level-two'][] = '1';
$echo['level-one']['level-two']['level-three'][] = '1';
$echo['level-one']['level-two']['level-three'][] = '2';
$echo['level-one']['level-two']['level-three'][] = '3';
$echo['level-one']['level-two']['level-three'][] = '4';

Function Declaration and Execution

function array_unique_multidimensional($name=null,$tv=null,$tk=null,$ta=null){

  if(!isset($ta)){$ta = ''.$name.'';}else{$ta .= '['.$tk.']';}

  if(!isset($tv)){$tv = '$v';}else{$tv .= 'v';}

  if(!isset($tk)){$tk = '$k';}else{$tk .= 'k';}

  return('
    foreach('.$ta.' as '.$tk.' => '.$tv.'){

      if(is_array('.$tv.')){
        eval(array_unique_multidimensional(\'\',\''.$tv.'\',\''.$tk.'\',\''.$ta.'\'));
      }else{
        '.$ta.' = array_unique('.$ta.');
      }

     }
  '); //end return

}

eval(array_unique_multidimensional('$echo'));
//Yes this function returns a foreach loop so it must be eval'd
//then it recursively calls itself until the entire array is traversed
//This will ensure every segment of the array is unique individually

Alright so I spaced it out a lot more then it probably needs to be and it is probably best viewed in some kind of program editor. Anyway after running the script as it is shown above you should get level's one and two reduced to a single entry and level three should have all four of it's entries (since their values are unique and one and two's are not).

I've seen a lot of answers using array_map to serialize and unique but I've found that they do not work for multidimensional arrays of varying calibers. Ultimately this function will accept any size and depth of array, compare every single key set to itself, remove any duplicates, and preserve every array name/key contained within.

Does anyone know of a better way to go about this? If not enjoy the working code above, if you do please share :)

\$\endgroup\$
  • \$\begingroup\$ I'd only add that your variables are not very descriptive (ta, tv, tk), which makes reading the function difficult at places. And also that eval() is evil. I'd use either of the other two functions before this one just for that alone. \$\endgroup\$ – mseancole Jul 5 '12 at 14:12
2
\$\begingroup\$

Shortest solution I can come up with:

function array_unique_recursive ($array) {

    // First loop the array and call self on any sub arrays
    // Note that $value is referenced
    foreach ($array as &$value) {
        if (is_array($value)) {
            $value = array_unique_recursive($value);
        }
    }

    // Now unique this level
    return array_unique($array, SORT_REGULAR);

}

print_r(array_unique_recursive($echo));

See it working

\$\endgroup\$
1
\$\begingroup\$

This is probably better than using eval. This solution touches only keys which are numeric, leaving string keys unchanged:

<?php

$echo['level-one'][] = '1';
$echo['level-one'][] = '1';
$echo['level-one'][] = '1';
$echo['level-one'][] = '1';
$echo['level-one']['level-two'][] = '1';
$echo['level-one']['level-two'][] = '1';
$echo['level-one']['level-two'][] = '1';
$echo['level-one']['level-two'][] = '1';
$echo['level-one']['level-two']['level-three'][] = '1';
$echo['level-one']['level-two']['level-three'][] = '2';
$echo['level-one']['level-two']['level-three'][] = '3';
$echo['level-one']['level-two']['level-three'][] = '4';

function makeUnique(array $arr) {
  $seen = array();
  foreach (array_keys($arr) as $key) {
    if (is_array($arr[$key])) {
      $arr[$key] = makeUnique($arr[$key]);
    } else {
      if (is_numeric($key)) {
        $v = (string)$arr[$key];
        if (isset($seen[$v])) {
          unset($arr[$key]);
        } else {
          $seen[$v] = true;
        }
      }
    }
  }
  return $arr;
}

print_r(makeUnique($echo));
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy