Thomazzi
Thomazzi

Reputation: 59

Combine 2 collections (keep the similar ones)

I've several collections, I want to keep only the elements that are present in each collection.

I went through the available methods, but I didn't find anything that would match.

$candidatesByConsultant = Consultant::find(request('consultant_id'))->candidates;
$candidatesByCreation = Candidate::whereBetween('created_at',[Carbon::parse(request('meeting_since')), Carbon::parse(request('meeting_to'))])->get();

Do you have any idea? :)

Upvotes: 0

Views: 62

Answers (3)

Namoshek
Namoshek

Reputation: 6544

The built-in for this is $collection->intersect($other), but you can also achieve the desired result with a simple custom filter:

$left  = collect([Model::find(1), Model::find(2), Model::find(3)]);
$right = collect([Model::find(1), Model::find(3), Model::find(5)]);

$result = $left->filter(function ($value, $key) use ($right) {
    return $right->contains(function ($v, $k) use ($value) {
        return $v->id === $value->id;
    });
});

This will perform model comparison by id. It is not very performant though. Another approach would be to retrieve two arrays of ids, intersect them and filter the merged sets based on this list:

$left   = collect([Model::find(1), Model::find(2), Model::find(3)]);
$right  = collect([Model::find(1), Model::find(3), Model::find(5)]);
$merged = $left->merge($right);

$ids = array_intersect($left->pluck('id')->toArray(), $right->pluck('id')->toArray());

$result = $merged->filter(function ($value, $key) use ($ids) {
    return in_array($value->id, $ids);
});

Upvotes: 0

Mathieu Bour
Mathieu Bour

Reputation: 696

The intersect method may be suitable : https://laravel.com/docs/5.8/collections#method-intersect

Example taken from the documentation:

$collection = collect(['Desk', 'Sofa', 'Chair']);

$intersect = $collection->intersect(['Desk', 'Chair', 'Bookcase']);

$intersect->all();

// [0 => 'Desk', 2 => 'Chair']

However, especially if you are trying to intersect multiple collections of Eloquent models, it may not work since the equality between two models is defined by the Model::is() method. Check https://laravel.com/docs/5.8/eloquent#comparing-models for more information about comparing two Eloquent models.

To handle this, I would do the following, assuming the primary key of your models is id:

$candidatesByConsultant = Consultant::find(request('consultant_id'))->candidates;
$candidatesByCreation = Candidate::whereBetween('created_at',[Carbon::parse(request('meeting_since')), Carbon::parse(request('meeting_to'))])->get();

$candidates = $candidatesByConsultant->merge($candidatesByCreation)->unique("id");

You may check the merge() and unique() documentations.

Upvotes: 0

Salar Bahador
Salar Bahador

Reputation: 1494

In order to have values that only present in both collection you must use intersect method:

$result = $candidatesByConsultant->intersect($candidatesByCreation);

The intersect method intersects the values of both collections. You can read it in Laravel's official documentation.

And in order to get have results that are not present in both collection you must use diff method:

$result = $candidatesByConsultant->diff($candidatesByCreation);

The diff method finds differences between collections. You can read it in Laravel's official documentation.

Upvotes: 1

Related Questions