36

I want to retrieve all case-insensitive duplicate entries from an array. Is this possible in PHP?

array(
    1 => '1233',
    2 => '12334',
    3 => 'Hello',
    4 => 'hello',
    5 => 'U'
);

Desired output array:

array(
    1 => 'Hello',
    2 => 'hello'
);
mickmackusa
  • 43,625
  • 12
  • 83
  • 136
coderex
  • 27,225
  • 45
  • 116
  • 170
  • There is a similar question here: http://stackoverflow.com/questions/1212605/php-array-search-for-multiple-values/1212863 – Till Theis Aug 11 '09 at 10:26

7 Answers7

41
function get_duplicates ($array) {
    return array_unique( array_diff_assoc( $array, array_unique( $array ) ) );
}
Alucard
  • 1,814
  • 1
  • 21
  • 33
  • Is the first `array_unique` really needed? [Without it](https://3v4l.org/VhjAs) seems to give the same result. – luukvhoudt Jul 08 '22 at 15:06
  • I believe without the first `array_unique`, if the original array had a value three times, this would return an array that had it twice; or if the original had it four times, the return would have it three times, etc. – 75th Trombone Oct 01 '22 at 22:08
  • 1
    The above comments are why it is always important to explain the code in answers. – mickmackusa Dec 12 '22 at 21:28
27
<?php
function array_not_unique($raw_array) {
    $dupes = array();
    natcasesort($raw_array);
    reset($raw_array);

    $old_key   = NULL;
    $old_value = NULL;
    foreach ($raw_array as $key => $value) {
        if ($value === NULL) { continue; }
        if (strcasecmp($old_value, $value) === 0) {
            $dupes[$old_key] = $old_value;
            $dupes[$key]     = $value;
        }
        $old_value = $value;
        $old_key   = $key;
    }
    return $dupes;
}

$raw_array    = array();
$raw_array[1] = 'abc@xyz.com';
$raw_array[2] = 'def@xyz.com';
$raw_array[3] = 'ghi@xyz.com';
$raw_array[4] = 'abc@xyz.com'; // Duplicate

$common_stuff = array_not_unique($raw_array);
var_dump($common_stuff);
Alexander Yancharuk
  • 13,817
  • 5
  • 55
  • 55
Shiva Srikanth Thummidi
  • 2,898
  • 4
  • 27
  • 36
21

You will need to make your function case insensitive to get the "Hello" => "hello" result you are looking for, try this method:

$arr = array(1=>'1233',2=>'12334',3 =>'Hello' ,4=>'hello', 5=>'U');

// Convert every value to uppercase, and remove duplicate values
$withoutDuplicates = array_unique(array_map("strtoupper", $arr));

// The difference in the original array, and the $withoutDuplicates array
// will be the duplicate values
$duplicates = array_diff($arr, $withoutDuplicates);
print_r($duplicates);

Output is:

Array
(
[3] => Hello
[4] => hello
)

Edit by @AlixAxel:

This answer is very misleading. It only works in this specific condition. This counter-example:

$arr = array(1=>'1233',2=>'12334',3 =>'Hello' ,4=>'HELLO', 5=>'U');

Fails miserably. Also, this is not the way to keep duplicates:

array_diff($arr, array_unique($arr));

Since one of the duplicated values will be in array_unique, and then chopped off by array_diff.

Edit by @RyanDay:

So look at @Srikanth's or @Bucabay's answer, which work for all cases (look for case insensitive in Bucabay's), not just the test data specified in the question.

ryanday
  • 2,506
  • 18
  • 25
  • Very clean and nice way to do it. Add some comments to it so noob PHP coders can understand : it's possible to write some neat code with that language, and it should be known :-) – Bite code Aug 11 '09 at 10:44
  • BTW, if the array is very long, I recommand to had an example using the iterator pattern of the SPL to save memory. – Bite code Aug 11 '09 at 10:45
  • You need array_diff_key() otherwise you'll return every value that isn't uppercase in original array. – bucabay Aug 11 '09 at 10:52
  • Good idea, I've changed the answer to use better variables and comments. I agree with the iterator also, Mittu how large would the real world array get? – ryanday Aug 11 '09 at 10:56
  • Very clean and logical. First get only unique ones and diff with the original array. – Jamol Aug 11 '09 at 10:58
  • @bucabay If he only wants one match of the duplicate values, yes you are right. In his example he gave the array with both "Hello" and "hello" so I only used array_diff(). But Mittu, as bucabay says, if you only want one copy of each duplicate, change array_diff() to array_diff_key() – ryanday Aug 11 '09 at 11:01
  • 1
    @rday - notice your test subject contains numbers and an uppercase "U" which are all uppercase as far as strtoupper() goes. The returned results are the mixed case values that strtoupper() modified, not the duplicates. If the poster needs both dupes, then the solution john proposed will do the job quite efficiently with a time complexity of O(n) which I believe is the best case here. – bucabay Aug 11 '09 at 21:48
  • 7
    -1, This is a total fail: http://codepad.org/7NQ9lQLU. Works only by pure chance. – Alix Axel Oct 20 '12 at 16:55
  • 3
    wasted my 15 min shud have seen @AkshatSinghal comment – mokNathal Oct 06 '15 at 11:18
8

This is the correct way to do it (case-sensitive):

array_intersect($arr, array_unique(array_diff_key($arr, array_unique($arr))));

And a case-insensitive solution:

$iArr = array_map('strtolower', $arr);
$iArr = array_intersect($iArr, array_unique(array_diff_key($iArr, array_unique($iArr))));

array_intersect_key($arr, $iArr);

But @Srikanth answer is more efficient (actually, it's the only one that works correctly besides this one).

Community
  • 1
  • 1
Alix Axel
  • 151,645
  • 95
  • 393
  • 500
  • ok, I'll bite. My solution has the error, but yours doesn't give the return value the OP was looking for. I don't understand how my answer is a miserable failure, and your's is better? – ryanday Oct 22 '12 at 15:13
  • @ryanday: Mine does it's job in a case-sensitive way, and can be adapted to be case-insensitive. Your answer on the other hand is almost as useful as `return array(1 =>'Hello' ,2=>'hello');`. I hope you get my point... – Alix Axel Oct 22 '12 at 15:53
  • @ryanday: Added a case-insensitive version. – Alix Axel Oct 22 '12 at 16:01
7
function array_not_unique($raw_array) {
    $dupes = array();
    natcasesort($raw_array);
    reset($raw_array);

    $old_key   = NULL;
    $old_value = NULL;
    foreach ($raw_array as $key => $value) {
        if ($value === NULL) { continue; }
        if (strcasecmp($old_value, $value) === 0) {
            $dupes[$old_key] = $old_value;
            $dupes[$key]     = $value;
        }
        $old_value = $value;
        $old_key   = $key;
    } return $dupes;
}

What Srikanth (john) added but with the case insensitive comparison.

Community
  • 1
  • 1
bucabay
  • 5,235
  • 2
  • 26
  • 37
6

Try:

$arr2 = array_diff_key($arr, array_unique($arr));

case insensitive:

array_diff_key($arr, array_unique(array_map('strtolower', $arr)));
bucabay
  • 5,235
  • 2
  • 26
  • 37
0

12 year old post and the accepted answer returns a blank array and others are long.

Here is my take for future Googlers that is short and returns ALL duplicate indexes (Indices?).

$myArray = array('fantastic', 'brilliant', 'happy', 'fantastic', 'Happy', 'wow', 'battlefield2042 :(');

function findAllDuplicates(array $array)
{
    // Remove this line if you do not need case sensitive.
    $array = array_map('strtolower', $array);

    // Remove ALL duplicates
    $removedDuplicates = array_diff($array, array_diff_assoc($array, array_unique($array)));

    return array_keys(array_diff($array, $removedDuplicates));
    // Output all keys with duplicates
    // array(4) {
    //   [0]=>int(0)
    //   [1]=>int(2)
    //   [2]=>int(3)
    //   [3]=>int(4)
    // }


    return array_diff($array, $removedDuplicates);
    // Output all duplicates
    // array(4) {
    //   [0]=>string(9) "fantastic"
    //   [2]=>string(5) "happy"
    //   [3]=>string(9) "fantastic"
    //   [4]=>string(5) "happy"
    // }
}
Bossman
  • 1,416
  • 1
  • 11
  • 17