0

related to : Remove duplicates from an array based on object property?

is there a way to do this exact same thing but consider the order? for example if instead of keeping [2] I wanted to keep [1]

  [0]=>
  object(stdClass)#337 (9) {
    ["term_id"]=>
    string(2) "23"
    ["name"]=>
    string(12) "Assasination"
    ["slug"]=>
    string(12) "assasination"
  }
  [1]=>
  object(stdClass)#44 (9) {
    ["term_id"]=>
    string(2) "14"
    ["name"]=>
    string(16) "Campaign Finance"
    ["slug"]=>
    string(16) "campaign-finance"
  }
  [2]=>
  object(stdClass)#298 (9) {
    ["term_id"]=>
    string(2) "15"
    ["name"]=>
    string(16) "Campaign Finance"
    ["slug"]=>
    string(49) "campaign-finance-good-government-political-reform"
  }

I tried posting a comment but because of my reputation it won't let me so I decided to start a new thread

Community
  • 1
  • 1
cocopan
  • 109
  • 3
  • 19

3 Answers3

1

Here is one way to do it. This should keep the first object for each name attribute and remove the rest.

$unique = [];

foreach ($array as $key => $object) {

    if (!isset($unique[$object->name])) {
        // this will add each name to the $unique array the first time it is encountered
        $unique[$object->name] = true;
    } else {
        // this will remove all subsequent objects with that name attribute
        unset($array[$key]);
    }
}

I used the object name as a key rather than a value in the $unique array so isset could be used to check for existing names, which should be faster than in_array which would have to be used if the names were added as values.

Don't Panic
  • 41,125
  • 10
  • 61
  • 80
  • @sevavietl I mutated the original array because that appeared to be the goal in the linked question this one was based on. `$unique` will only hold each `name` attribute from `$array` as keys, and after the loop, any objects with duplicate names will be removed. – Don't Panic Dec 02 '16 at 22:43
  • Ah, no worries. – Don't Panic Dec 02 '16 at 22:47
1

It is trivial to reverse and array. So before processing the array, call array_reverse() on it:

/** flip it to keep the last one instead of the first one **/
$array = array_reverse($array);

Then at the end you can reverse it again if ordering is an issue:

/** Answer Code ends here **/
/** flip it back now to get the original order **/
$array = array_reverse($array);

So put all together looks like this:

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);

/** flip it to keep the last one instead of the first one **/
$array = array_reverse($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);

// Remove the duplicates from original array
foreach($array as $k => $v) {
  if (!array_key_exists($k, $tmp))
    unset($array[$k]);
}

/** Answer Code ends here **/
/** flip it back now to get the original order **/
$array = array_reverse($array);

echo "After removing duplicates\n";
echo "<pre>".print_r($array, 1);

produces the following output

Array
(
  [0] => my_obj Object
    (
        [term_id] => 23
        [name] => Assasination
        [slug] => assasination
    )

  [1] => my_obj Object
    (
        [term_id] => 15
        [name] => Campaign Finance
        [slug] => campaign-finance-good-government-political-reform
    )

)
WEBjuju
  • 5,797
  • 4
  • 27
  • 36
0

You can do it with array_reduce:

$names = [];

$withUniqueNames = array_reduce(
    [$objA, $objB, $objC],
    function ($withUniqueNames, $object) use (&$names) {
        if (!in_array($object->name, $names)) {
            $names[] = $object->name;
            $withUniqueNames[] = $object;
        } 

        return $withUniqueNames;
    },
    []
);

Basically, we traverse an array of objects and keep track of used names. If the name was not used yet, we add it to used and add current object to the result.

Here is working demo.

sevavietl
  • 3,762
  • 1
  • 14
  • 21