4

How to filter an array to give unique elements according to one or more columns. Example:

array(
    array('name'=>'toto', 'type'=>'1', 'type2'=> '2')
    array('name'=>'tata', 'type'=>'1', 'type2'=> '3')
    array('name'=>'titi', 'type'=>'1', 'type2'=> '2')
    array('name'=>'tutu', 'type'=>'2', 'type2'=> '4')
    array('name'=>'tete', 'type'=>'3', 'type2'=> '2')
)

If we choose type and type2 as the unique column. The result of the algorithm should gives

array(
    array('name'=>'toto', 'type'=>'1', 'type2'=> '2')
    array('name'=>'tata', 'type'=>'1', 'type2'=> '3')
    array('name'=>'tutu', 'type'=>'2', 'type2'=> '4')
    array('name'=>'tete', 'type'=>'3', 'type2'=> '2')
)

I can think of an algorithm by hashing the type concatenate with type2, store in a table and using isset to find the existence. But I'm not sure that's the best algorithm.

7
  • 4
    How does your algorithm decide which of titi or toto to discard? Commented May 7, 2013 at 12:46
  • You need to get unique values according to 'type2'?How to decide titi or toto is needed? Commented May 7, 2013 at 12:47
  • why not just do this in a database with unique column constraints? Commented May 7, 2013 at 12:50
  • @KevinBrydon Let's just say by default we take the first found array. In this case is toto array. Or if possible I want also a version that take the last found array. @Jakub: in my case, the data doesn't necessary come from a dump of SQL Commented May 7, 2013 at 13:04
  • 1
    Can anyone provide an answer here that does not use foreach and assignment, but is instead based on a map/filter/reduce implementation using PHP's array functions? Commented Mar 8, 2017 at 17:15

5 Answers 5

2

All you need is

$data = array(
    array('name'=>'toto', 'type'=>'1', 'type2'=> '2'),
    array('name'=>'tata', 'type'=>'1', 'type2'=> '3'),
    array('name'=>'titi', 'type'=>'1', 'type2'=> '2'),
    array('name'=>'tutu', 'type'=>'2', 'type2'=> '4'),
    array('name'=>'tete', 'type'=>'3', 'type2'=> '2')
);


$tmp = array();
foreach($data as $v) {
    $id = $v['type'] . "|" . $v['type2'];
    isset($tmp[$id]) or $tmp[$id] = $v;
}
print_r(array_values($tmp));
Sign up to request clarification or add additional context in comments.

8 Comments

Same as above, this only check for uniqueness of type2, I also want a comparison with type too
Your version only works in this special case... If I add array('name'=>'tete', 'type'=>'3', 'type2'=> '2') it's not gonna work...
Your solution is what I said in my post : I can think of an algorithm by hashing the type concatenate with type2, store in a table and using isset to find the existence. But I'm not sure that's the best algorithm. Actually, it would better if you hash your $id with md5()
Keep it simple .... alternatives would be to use array_reduce might not be as readable or faster ....
'type'=>'11', 'type2'=> '3' can't be equivalent to 'type'=>'1', 'type2'=> '13'?
|
2
$arr = array(
    array('name'=>'toto', 'type'=>'1', 'type2'=> '2'),
    array('name'=>'tata', 'type'=>'1', 'type2'=> '3'),
    array('name'=>'titi', 'type'=>'1', 'type2'=> '2'),
    array('name'=>'tutu', 'type'=>'2', 'type2'=> '4')
);

$filtered = array(); // resulting array
$combinations = array(); // combinations of 'type' and 'type2' values

foreach ( $arr as $elem ) {

    $comb = array($elem['type'], $elem['type2']);

    if ( !in_array($comb, $combinations) ) { // new combination
        $combinations[] = $comb;
        $filtered[] = $elem;
    }
}

1 Comment

This one works too. But same as above, in_array is overkill
2
$keys = array('type', 'type2');
$filtered = array();

foreach ($array as $elem) {
    $compKey = join('|', array_intersect_key($elem, array_flip($keys)));
    $filtered[$compKey] = $elem;
}

// optionally: $filtered = array_values($filtered);

5 Comments

Although generates the example result array, it only uses 'type2' as criteria, and OP required uniqueness based in 'type' column too.
Maybe true. The question is a bit too vague how exactly the algorithm should behave. I just read it as "choose one or the other".
Both type and type2 are required, sorry. Unique column is easy, I wouldn't ask...
@Thanh Then joining all values of all keys by some separator and use it as composite key should do the trick. Updated answer.
@deceze Result found will be : titi, tata, tutu, tete. Which is good enough for me. One small problem is that titi position is changed. Having titi after tata is perfect because we keep the order. Or is possible to replace titi with toto ?
1

Updated to support key combinations

<?php

$array=array(
    array('name'=>'toto', 'type'=>'1', 'type2'=> '2'),
    array('name'=>'tata', 'type'=>'1', 'type2'=> '3'),
    array('name'=>'titi', 'type'=>'1', 'type2'=> '2'),
    array('name'=>'tutu', 'type'=>'2', 'type2'=> '4'),
);

function filter($key,$array){
    $filtered=array();
    $used=array();
    foreach ($array as $e){
        $v=array();
        foreach ($key as $k) $v[]=$e[$k];
        if (!in_array($v,$used)){
            $used[]=$v;
            $filtered[]=$e;
        }
    }
    return $filtered;
}

$filtered=filter(array('type2','type'),$array);

4 Comments

Which it is supposed to do when filtering on 'type'. Filtering on 'type2' will remove 'titi'. Second run, filtering on 'type' will remove tata.
Yes, but it doesn't give the same result I want. The unique constraint is a couple (type, type2) not just one or another
Thanks, it worked. One problem is performance. Using in_array is overkill when you have 10k line of record for example. Try to use less function call as possible. The number of loop is nb_field_unique * array_size²
Now you want optimized as well? :-) The solutions using keys will be faster then, since the keys are hashtables and thus O(1).
0

What about this way?

Result array is not the same as that of the author. But still valid for the condition:

...filter an array to give unique elements according to one or more columns.

<?php

declare(strict_types=1);

$input = [
    ['name' => 'toto', 'type' => '1', 'type2' => '2'],
    ['name' => 'tata', 'type' => '1', 'type2' => '3'],
    ['name' => 'titi', 'type' => '1', 'type2' => '2'],
    ['name' => 'tutu', 'type' => '2', 'type2' => '4'],
    ['name' => 'tete', 'type' => '3', 'type2' => '2'],
];

$keys = array_map(
    static fn(string $key, string $value) => "$key:$value",
    array_column($input, 'type'), array_column($input, 'type2'));

$result = array_values(array_combine($keys, array_values($input)));

$expect = [
    ['name' => 'titi', 'type' => '1', 'type2' => '2'],
    ['name' => 'tata', 'type' => '1', 'type2' => '3'],
    ['name' => 'tutu', 'type' => '2', 'type2' => '4'],
    ['name' => 'tete', 'type' => '3', 'type2' => '2'],
];

assert($result === $expect); // true

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.