Remove duplicates from an array based on object property?
One-liner:
$filtered = array_intersect_key($array, array_unique(array_column($array, 'someProperty')));
Please note that array_column() works with array of objects in PHP 7 and newer.
I was searching for a good answer to this and couldn't find one, so I've written my own that will handle this case, and the case when you want to de-dupe based on multiple properties.
$array = DeDupeArrayOfObjectsByProps($array, ['name']);
Here's the generic function:
/**
* Iterates over the array of objects and looks for matching property values.
* If a match is found the later object is removed from the array, which is returned
* @param array $objects The objects to iterate over
* @param array $props Array of the properties to dedupe on.
* If more than one property is specified then all properties must match for it to be deduped.
* @return array
*/
public function DeDupeArrayOfObjectsByProps($objects, $props) {
if (empty($objects) || empty($props))
return $objects;
$results = array();
foreach ($objects as $object) {
$matched = false;
foreach ($results as $result) {
$matchs = 0;
foreach ($props as $prop) {
if ($object->$prop == $result->$prop)
$matchs++;
}
if ($matchs == count($props)) {
$matched = true;
break;
}
}
if (!$matched)
$results[] = $object;
}
return $results;
}
Here's the full usage for your case:
class my_obj {
public $term_id;
public $name;
public $slug;
public function __construct($i, $n, $s) {
$this->term_id = $i;
$this->name = $n;
$this->slug = $s;
}
}
$objA = new my_obj(23, "Assasination", "assasination");
$objB = new my_obj(14, "Campaign Finance", "campaign-finance");
$objC = new my_obj(15, "Campaign Finance", "campaign-finance-good-government-political-reform");
$array = array($objA, $objB, $objC);
$array = DeDupeArrayOfObjectsByProps($array, ['name']);
var_dump($array);
I needed the same thing... Here's what I did (work for both array and objects, based on this post)
function my_array_unique_by_subkey($array,$subkey){
$temp = array();
$unique = array_filter($array, function ($v) use (&$temp,$subkey) {
if ( is_object($v) ) $v = (array)$v;
if ( !array_key_exists($subkey,$v) ) return false;
if ( in_array($v[$subkey], $temp) ) {
return false;
} else {
array_push($temp, $v[$subkey]);
return true;
}
});
return $unique;
}
For php >=7.0
:
Since array_column
works on object-arrays since PHP 7.0 you can use the following combination as suggested by @plashenkov:
$filtered = array_intersect_key($array, array_unique(array_column($array, 'someProperty')));
Full example: https://3v4l.org/IboLu#v8.0.8
class my_obj
{
public $term_id;
public $name;
public $slug;
public function __construct($i, $n, $s)
{
$this->term_id = $i;
$this->name = $n;
$this->slug = $s;
}
}
$objA = new my_obj(23, 'Assasination', 'assasination');
$objB = new my_obj(14, 'Campaign Finance', 'campaign-finance');
$objC = new my_obj(15, 'Campaign Finance', 'campaign-finance-good-government-political-reform');
$array = array($objA, $objB, $objC);
echo 'Original array:\n';
print_r($array);
/** Answer Code begins here */
$filtered = array_intersect_key($array, array_unique(array_column($array, 'name')));
/** Answer Code ends here */
echo 'After removing duplicates\n';
print_r($filtered);
Output:
Original array:
Array
(
[0] => my_obj Object
(
[term_id] => 23
[name] => Assasination
[slug] => assasination
)
[1] => my_obj Object
(
[term_id] => 14
[name] => Campaign Finance
[slug] => campaign-finance
)
[2] => my_obj Object
(
[term_id] => 15
[name] => Campaign Finance
[slug] => campaign-finance-good-government-political-reform
)
)
After removing duplicates
Array
(
[0] => my_obj Object
(
[term_id] => 23
[name] => Assasination
[slug] => assasination
)
[1] => my_obj Object
(
[term_id] => 14
[name] => Campaign Finance
[slug] => campaign-finance
)
)
The object with term_id 15 was removed as it had the same name as term_id 14.
For php <7.0
:
Build a new array with the existing keys and just the name as value, use array_unique
(note that it preserves keys).
Then copy every key from the original array to a new array ($filtered
) (or remove everything thats not in the unique'ed array from the original key-wise).
Edit: Complete example: https://3v4l.org/SCrko#v5.6.40
class my_obj
{
public $term_id;
public $name;
public $slug;
public function __construct($i, $n, $s)
{
$this->term_id = $i;
$this->name = $n;
$this->slug = $s;
}
}
$objA = new my_obj(23, 'Assasination', 'assasination');
$objB = new my_obj(14, 'Campaign Finance', 'campaign-finance');
$objC = new my_obj(15, 'Campaign Finance', 'campaign-finance-good-government-political-reform');
$array = array($objA, $objB, $objC);
echo 'Original array:\n';
print_r($array);
/** Answer Code begins here **/
// Build temporary array for array_unique
$tmp = array();
foreach($array as $k => $v)
$tmp[$k] = $v->name;
// Find duplicates in temporary array
$tmp = array_unique($tmp);
// Build new array with only non-duplicate items
$filtered = [];
foreach($array as $k => $v)
{
if (array_key_exists($k, $tmp))
$filtered[$k] = $v;
}
/** Answer Code ends here **/
echo 'After removing duplicates\n';
print_r($filtered);
Output:
Original array:
Array
(
[0] => my_obj Object
(
[term_id] => 23
[name] => Assasination
[slug] => assasination
)
[1] => my_obj Object
(
[term_id] => 14
[name] => Campaign Finance
[slug] => campaign-finance
)
[2] => my_obj Object
(
[term_id] => 15
[name] => Campaign Finance
[slug] => campaign-finance-good-government-political-reform
)
)
After removing duplicates
Array
(
[0] => my_obj Object
(
[term_id] => 23
[name] => Assasination
[slug] => assasination
)
[1] => my_obj Object
(
[term_id] => 14
[name] => Campaign Finance
[slug] => campaign-finance
)
)
The object with term_id 15 was removed as it had the same name as term_id 14.