Reputation: 393
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
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
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
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
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
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
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