Question

I have an array

Array(
[0] => Array
    (
        [0] => 33
        [user_id] => 33
        [1] => 3
        [frame_id] => 3
    )

[1] => Array
    (
        [0] => 33
        [user_id] => 33
        [1] => 3
        [frame_id] => 3
    )

[2] => Array
    (
        [0] => 33
        [user_id] => 33
        [1] => 8
        [frame_id] => 8
    )

[3] => Array
    (
        [0] => 33
        [user_id] => 33
        [1] => 3
        [frame_id] => 3
    )

[4] => Array
    (
        [0] => 33
        [user_id] => 33
        [1] => 3
        [frame_id] => 3
    )

)

As you can see key 0 is the same as 1, 3 and 4. And key 2 is different from them all.

When running the array_unique function on them, the only left is

Array (
[0] => Array
    (
        [0] => 33
        [user_id] => 33
        [1] => 3
        [frame_id] => 3
    )
)

Any ideas why array_unique isn't working as expected?

Was it helpful?

Solution

It's because array_unique compares items using a string comparison. From the docs:

Note: Two elements are considered equal if and only if (string) $elem1 === (string) $elem2. In words: when the string representation is the same. The first element will be used.

The string representation of an array is simply the word Array, no matter what its contents are.

You can do what you want to do by using the following:

$arr = array(
    array('user_id' => 33, 'frame_id' => 3),
    array('user_id' => 33, 'frame_id' => 3),
    array('user_id' => 33, 'frame_id' => 8)
);

$arr = array_intersect_key($arr, array_unique(array_map('serialize', $arr)));

//result:
array
  0 => 
    array
      'user_id' => int 33
      'user' => int 3
  2 => 
    array
      'user_id' => int 33
      'user' => int 8

Here's how it works:

  1. Each array item is serialized. This will be unique based on the array's contents.

  2. The results of this are run through array_unique, so only arrays with unique signatures are left.

  3. array_intersect_key will take the keys of the unique items from the map/unique function (since the source array's keys are preserved) and pull them out of your original source array.

OTHER TIPS

array_unique() only supports multi-dimensional arrays in PHP 5.2.9 and higher.

Instead, you can create a hash of the array and check it for unique-ness.

$hashes = array(); 

foreach($array as $val) { 
    $hashes[md5(serialize($val))] = $val; 
} 

array_unique($hashes);

Here's an improved version of @ryeguy's answer:

<?php

$arr = array(
    array('user_id' => 33, 'tmp_id' => 3),
    array('user_id' => 33, 'tmp_id' => 4),
    array('user_id' => 33, 'tmp_id' => 5)
);


# $arr = array_intersect_key($arr, array_unique(array_map('serialize', $arr)));
$arr = array_intersect_key($arr, array_unique(array_map(function ($el) {
    return $el['user_id'];
}, $arr)));

//result:
array
  0 => 
    array
      'user_id' => int 33
      'tmp_id' => int 3

First, it doesn't do unneeded serialization. Second, sometimes attributes may be different even so id is the same.

I've run into it with Google Places API. I was combining results of several requests with different type of objects (think tags). But I got duplicates, since an object may be put into several categories (types). And the method with serialize didn't work, since the attrs were different, namely, photo_reference and reference. Probably these are like temporary ids.

array_unique deosn't work recursive, so it just thinks "this are all Arrays, let's kill all but one... here we go!"

Quick Answer (TL;DR)

  • Distinct values may be extracted from PHP Array of AssociativeArrays using foreach
  • This is a simplistic approach

Detailed Answer

Context

  • PHP 5.3
  • PHP Array of AssociativeArrays (tabluar composite data variable)
  • Alternate name for this composite variable is ArrayOfDictionary (AOD)

Problem

  • Scenario: DeveloperMarsher has a PHP tabular composite variable
    • DeveloperMarsher wishes to extract distinct values on a specific name-value pair
    • In the example below, DeveloperMarsher wishes to get rows for each distinct fname name-value pair

Solution

  • example01 ;; DeveloperMarsher starts with a tabluar data variable that looks like this

    $aodtable = json_decode('[
    {
      "fname": "homer"
      ,"lname": "simpson"
    },
    {
      "fname": "homer"
      ,"lname": "jackson"
    },
    {
      "fname": "homer"
      ,"lname": "johnson"
    },
    {
      "fname": "bart"
      ,"lname": "johnson"
    },
    {
      "fname": "bart"
      ,"lname": "jackson"
    },
    {
      "fname": "bart"
      ,"lname": "simpson"
    },
    {
      "fname": "fred"
      ,"lname": "flintstone"
    }
    ]',true);
    
  • example01 ;; DeveloperMarsher can extract distinct values with a foreach loop that tracks seen values

    $sgfield  =   'fname';
    $bgnocase =   true;
    
    //
    $targfield  =   $sgfield;
    $ddseen     =   Array();
    $vout       =   Array();
    foreach ($aodtable as $datarow) {
    if( (boolean) $bgnocase == true ){ @$datarow[$targfield] = @strtolower($datarow[$targfield]); }
    if( (string) @$ddseen[ $datarow[$targfield] ] == '' ){
      $rowout   = array_intersect_key($datarow, array_flip(array_keys($datarow)));
      $ddseen[ $datarow[$targfield] ] = $datarow[$targfield];
      $vout[] = Array( $rowout );
    }
    }
    //;;
    
    print var_export( $vout, true );
    

Output result

array (
  0 =>
  array (
    0 =>
    array (
      'fname' => 'homer',
      'lname' => 'simpson',
    ),
  ),
  1 =>
  array (
    0 =>
    array (
      'fname' => 'bart',
      'lname' => 'johnson',
    ),
  ),
  2 =>
  array (
    0 =>
    array (
      'fname' => 'fred',
      'lname' => 'flintstone',
    ),
  ),
)

Pitfalls

  • This solution does not aggregate on fields that are not part of the DISTINCT operation
  • Arbitrary name-value pairs are returned from arbitrarily chosen distinct rows
  • Arbitrary sort order of output
  • Arbitrary handling of letter-case (is capital A distinct from lower-case a ?)

See also

  • php array_intersect_key
  • php array_flip
function array_unique_recursive($array)
{
    $array = array_unique($array, SORT_REGULAR);

    foreach ($array as $key => $elem) {
        if (is_array($elem)) {
            $array[$key] = array_unique_recursive($elem);
        }
    }

    return $array;
}

Doesn't that do the trick ?

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top