max
max

Reputation: 393

How to compare two collection arrays?

I'm working with laravel, and I have two collection arrays that look something like this:

$collection1 = [
    ['id' => 1, 'name'=> 'phone', 'quantity' => 1, 'price' => 1200],
    ['id' => 2, 'name'=> 'tv', 'quantity' => 3, 'price' => 800],
];

$collection2 = [
    ['id' => 1, 'name'=> 'phone', 'quantity' => 1, 'price' => 1200],
    ['id' => 2, 'name'=> 'tv', 'quantity' => 3, 'price' => 400],
];

so I need to know if they are the same or not, comparing both collections. and if in some of the arrays one of the keys (or several) has a different value, then they will no longer be the same collections, for example in 1 collection the price of the second item has a value of 800 and the other collection of 400. there is Any native method of laravel collections to do this ?, or how can i do it with simple php arrays?

Upvotes: 8

Views: 37556

Answers (6)

volvpavl
volvpavl

Reputation: 644

I ended up comparing simply the output of toJson() of collections. I can't say how bad this is in terms of performance, but it's fine for my case (unit tests). But please note, in this case array elements need to be sorted in the same order on all levels.

Upvotes: 0

Sachin Kumar
Sachin Kumar

Reputation: 3240

This method only works if both collections have the same key order.

By default, Laravel has a diffAssoc method which actually compares the single item of a collection. If you want to compare two collection arrays then you have to create your own solution.

Here is my solution where I create(or you can say extend) a collection method.

First I map each item and serialize the item and then do the same with another collection. Get the difference by using the diffAssoc method and unserialize the final output.

AppServiceProvider.php

<?php

namespace App\Providers;

use Illuminate\Support\Collection;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        //
    }

    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        Collection::macro('diffAssocMultiple', function ($anotherCollection) {
            /* @var $this Collection */
            return $this->map(function($arr) {
                return serialize($arr);
            })->diffAssoc($anotherCollection->map(function($arr) {
                return serialize($arr);
            }))->map(function($arr) {
                return unserialize($arr);
            });
        });
    }
}

Usage

$diff = $collectionOne->diffAssocMultiple($collectionTwo);

Note that, this new method returns another collection(non-zero based). and this doesn't have the 'all()' method as diffAssoc has. If you want a zero-based index array then use the values function along with.

$diff = $collectionOne->diffAssocMultiple($collectionTwo)->values();

Upvotes: 0

TEFO
TEFO

Reputation: 1643

the laravel collections have a method called diff, with this method you can Get the items in the collection that are not present in the given items. and the items is $collection2 which can be an array or a collection so you can take the different items between this two collection like this

$collection1->diff($collection2);

it returns a Illuminate\Support\Enumerable class. you can get the items by call all() on this:

$collection = collect([1, 2, 3, 4, 5]);

$differentItems = $collection->diff([2, 4, 6, 8]);

$differentItems->all();

// [1, 3, 5]

this code belongs to laravel docs. https://laravel.com/docs/7.x/collections#method-diff . and at the end you can turn $differentItems to boolean. like this:

$collection = collect([1, 2, 3, 4, 5]);
$differentItems = $collection->diff([2, 4, 6, 8]);
$differentItems->isEmpty();
// return false

$collection = collect([1, 2, 3, 4, 5]);
$differentItems = $collection->diff($collection);
$differentItems->isEmpty();
// return true

more links https://laravel.com/api/7.x/Illuminate/Support/Collection.html#method_diff https://laravel.com/api/7.x/Illuminate/Support/Enumerable.html

Upvotes: 9

Ali Khalili
Ali Khalili

Reputation: 1674

As far as I know, as your collection items are array, there is no native simple Laravel way to do so and you should write a function to compare them:

So, assuming that, you have:

$collection1 = collectt([
    ['id' => 1, 'name'=> 'phone', 'quantity' => 1, 'price' => 1200],
    ['id' => 2, 'name'=> 'tv', 'quantity' => 3, 'price' => 800],
]);

$collection2 = collect([
    ['id' => 1, 'name'=> 'phone', 'quantity' => 1, 'price' => 1200],
    ['id' => 2, 'name'=> 'tv', 'quantity' => 3, 'price' => 400],
]);

You can for example compare them by:

    private function collectionsAreEqual($collection1, $collection2)
    {
        if ($collection1->count() != $collection2->count()) {
            return false;
        }
        //assuming that, from each id, you don't have more that one item:
        $collection2 = $collection2->keyBy('id');
        foreach ($collection1->keyBy('id') as $id => $item) {
            if (!isset($collection2[$id])) {
                return false;
            }
            //your items in the collection are key value arrays 
            // and can compare them with == operator
            if ($collection2[$id] != $item) {
                return false;
            }
        }
        return true;
    }
    dd(collectionsAreEqual($collection1, $collection2));

Upvotes: 0

Andy Song
Andy Song

Reputation: 4684

So serialize each element first, and then compare.

    $serialize1 = $collection1->map(function ($item) {
        return serialize($item);
    });

    $serialize2 = $collection2->map(function ($item) {
        return serialize($item);
    });

dd($serialize1->diff($serialize2)->isEmpty());

Upvotes: 1

saulotoledo
saulotoledo

Reputation: 1972

Follows a comparison function:

function compareCollections($c1, $c2) {
    // If the colletions have different sizes we return false:
    if (count($c1) != count($c2)) {
        return false;
    }

    // The collections have the same size, we check element by element:
    foreach($c1 as $item) {

        // We find the current element in $c1 in $c2:
        $itemToCompare = array_filter($c2, function ($compareItem) use ($item) {
            return ($compareItem['id'] == $item['id']);
        });

        // If we did not find the element in $c2, the collections are different:
        if (empty($itemToCompare)) {
            return false;
        }

        $itemToCompare = current($itemToCompare);

        // We now use PHP to check the element keys:
        $diff = array_diff_key($item, $itemToCompare);

        // If there is a different, return false:
        if (!empty($diff)) {
            return false;
        }       
    }

    // If everything is ok until here, the collections are the same:
    return true;
}

And a test:

$collection1 = [
    ['id' => 1, 'name'=> 'phone', 'quantity' => 1, 'price' => 1200],
    ['id' => 2, 'name'=> 'tv', 'quantity' => 3, 'price' => 800],
];

$collection2 = [
    ['id' => 1, 'name'=> 'phone', 'quantity' => 1, 'price' => 1200],
    ['id' => 2, 'name'=> 'tv', 'quantity' => 3, 'price' => 400],
];

$collection3 = [
    ['id' => 1, 'name'=> 'tv', 'quantity' => 1, 'price' => 1200],
    ['id' => 2, 'name'=> 'tv', 'quantity' => 3],
];

var_dump(compareCollections($collection1, $collection2)); // true
var_dump(compareCollections($collection1, $collection3)); // false

Upvotes: 0

Related Questions