2

Is there a simpler way to get all array keys that has same value, when the value is unknown.

The problem with array_unique is that it returns the unique array and thus it doesn't find unique values.

That is, for example, from this array:

Array (
  [a]=>1000
  [b]=>1
  [c]=>1000
)

I want to get this

Array (
  [a]=>1000
  [c]=>1000
)

Another way around this is, if I could find the lonely values, and then their keys, and then use array_diff

This is what I've got so far, looks awful:

$a = array( 'a' => 1000, 'b' => 1, 'c' => 1000 );
$b = array_flip( array_count_values( $a ) );
krsort( $b );
$final = array_keys( $a, array_shift( $b ) );

Update
Using Paulo Freites' answer as a code base, I could get it working pretty easily, maintainable and easy on eyes kind of way… by using the filtering as a static class method I can get the duplicate values from an array by just calling ClassName::get_duplicates($array_to_filter)

private static $counts = null;

private static function filter_duplicates ($value) {
    return self::$counts[ $value ] > 1;
}

public static function get_duplicates ($array) {
    self::$counts = array_count_values( $array );
    return array_filter( $array, 'ClassName::filter_duplicates' );
}
6
  • what do you mean by extract? Remove duplicates? keep only duplicates? Commented Nov 13, 2013 at 23:23
  • guess he means to create new array with same elements keeping their original keys like in an example. Commented Nov 13, 2013 at 23:25
  • I don't need the initial array, so, either i want to have the keys that are unique, or keys that have duplicates.. and by unique I mean the value of that key doesn't exist anywhere else in the array Commented Nov 13, 2013 at 23:25
  • @rakimo …and what should I pass as a second parameter? Commented Nov 14, 2013 at 13:08
  • pass array1 and array2 to array_intersect Commented Nov 14, 2013 at 13:47

6 Answers 6

7

Taking advantage of closures for a more straightforward solution:

$array = array('a' => 1000, 'b' => 1, 'c' => 1000);
$counts = array_count_values($array);
$filtered = array_filter($array, function ($value) use ($counts) {
    return $counts[$value] > 1;
});
var_dump($filtered);

This gave me the following:

array(2) {
  ["a"]=>
  int(1000)
  ["c"]=>
  int(1000)
}

Demo: https://eval.in/67526

That's all! :)

Update: backward-compatible solution

$array = array('a' => 1000, 'b' => 1, 'c' => 1000);
$counts = array_count_values($array);
$filtered = array_filter($array, create_function('$value',
    'global $counts; return $counts[$value] > 1;'));
var_dump($filtered);

Demo: https://eval.in/68255

Sign up to request clarification or add additional context in comments.

4 Comments

nice one. Isn't this working only in PHP 5.3 and above? The server has 5.2.4
@micadelli Unfortunately yes! I've updated my answer with a backward-compatible solution which uses create_function() in place of closures. :)
interesting stuff, that create_function. Unfortunately thought, it has bad performance and memory usage characteristics… Somehow coming from JavaScript side, I like to stay away from any evil eval kind of code :) I'll try to fork your code using a static class method instead and see how it works.
@micadelli Yea, create_function() it's not so bad, but have disadvantages in place of closures... You can get more info about that difference here: stackoverflow.com/q/6485336/222758 Unfortunately, the only other way to achieve that with array_filter() is the way you've switched for, using classes. It's not so bad either... Glad to know that I was able to help you! :)
4

Your implementation has a few issues.

1) If there are 2 of value 1000 and 2 of another value, the array_flip will lose one of the sets of values.

2) If there are more than two different values, the array_keys will only find the one value that occurs most.

3) If there are no duplicates, you will still bring back one of the values.

Something like this works always and will return all duplicate values:

<?php
//the array
$a = array( 'a' => 1000, 'b' => 1, 'c' => 1000 );
//count of values
$cnt = array_count_values($a);

//a new array
$newArray = array();
//loop over existing array
foreach($a as $k=>$v){
    //if the count for this value is more than 1 (meaning value has a duplicate)
    if($cnt[$v] > 1){
        //add to the new array
        $newArray[$k] = $v;
    }
}

print_r($newArray);

http://codepad.viper-7.com/fal5Yz

Comments

1

If you want to get the duplicates in an array try this:

array_unique(array_diff_assoc($array1, array_unique($array1)))

I found this from:

http://www.php.net/manual/en/function.array-unique.php#95203

3 Comments

that method returns the key/value that has the duplicates itself
Ah, okay misunderstanding.
@micadelli I found a nice little snippet from the comments section below in the array_unique php docs, hope that helps.
1

at the moment I cant figure out another solution...

  // target array
  $your_array = array('a'=>1000, 'b'=>1, 'c'=>1000);

  // function to do all the job
  function get_duplicate_elements($array) {
    $res = array();
    $counts = array_count_values($array);
    foreach ($counts as $id=>$count) {
     if ($count > 1) {
       $r = array();
       $keys = array_keys($array, $id);
       foreach ($keys as $k) $r[$k] = $id;
       $res[] = $r;
       }
     }
    return sizeof($res) > 0 ? $res : false;
    }

  // test it
  print_r(get_duplicate_elements($your_array));

output:

Array
(
    [0] => Array
        (
            [a] => 1000
            [c] => 1000
        )

)

example #2: - when you have different values multiplied

// target array
$your_array = array('a'=>1000, 'b'=>1, 'c'=>1000, 'd'=>500, 'e'=>1);

// output
print_r(get_duplicate_elements($your_array));

output:

Array
(
    [0] => Array
        (
            [a] => 1000
            [c] => 1000
        )

    [1] => Array
        (
            [b] => 1
            [e] => 1
        )

)

if function result has been assigned to $res variable $res[0] gets an array of all elements from original array with first value found more than once, $res[1] gets array of elements with another duplicated-value, etc... function returns false if nothing duplicate has been found in argument-array.

4 Comments

updated… i don't know the value. the value is "duplicate value"
ah, you want to match duplicate values first then to pull elements out.
correct… that's what my script does, but i thought it could be achieved way more simpler
it now works, even if you have more different values duplicated in an array.
1

Try this

$a = array( 'a' => 1, 'b' => 1000, 'c' => 1000,'d'=>'duplicate','e'=>'duplicate','f'=>'ok','g'=>'ok' );
$b = array_map("unserialize", array_unique(array_map("serialize", $a)));
$c = array_diff_key($a, $b);

2 Comments

sorry, dude, didn't mention that those values are random, only put 1 vs 1000 for readability
I edited the code, sorry. See if it works for you. @micadelli
0
$array = array("1"=>"A","2"=>"A","3"=>"A","4"=>"B","5"=>"B","6"=>"B");
$val   = array_unique(array_values($array));
foreach ($val As $v){
  $dat[$v] = array_keys($array,$v);
}
print_r($dat);

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.