Mostafa Bayomi
Mostafa Bayomi

Reputation: 13

Merge two Collections in laravel

Need to merge two collections

I have two collections and need to merge them in one collection

these collections was output of two queries using Eloquent

The First Result is like below:

Collection {#350
  #items: array:2 [
    0 => {#342
      +"id": 1
      +"code": "C000215"
      +"name": "Mostafa Mohamed Bayomi"
      +"mobile1": "01228902157"
      +"mobile2": ""
      +"CoachServiceCount": 0
      +"CourseServiceCount": 1
      +"CommunicationCount": 0
    }
    1 => {#337
      +"id": 2
      +"code": "C000216"
      +"name": "Sayed Mohamed Bayomi"
      +"mobile1": "01228902158"
      +"mobile2": ""
      +"CoachServiceCount": 0
      +"CourseServiceCount": 2
      +"CommunicationCount": 0
    }
]
}

The second query result is like below:

Collection {#350
  #items: array:2 [
    0 => {#346
      +"id": 1
      +"code": "C000215"
      +"name": "Mostafa Mohamed Bayomi"
      +"mobile1": "01228902157"
      +"mobile2": ""
      +"CoachServiceAmount": 0.0
    }
    1 => {#340
      +"id": 2
      +"code": "C000216"
      +"name": "Sayed Mohamed Bayomi"
      +"mobile1": "01228902158"
      +"mobile2": ""
      +"CoachServiceAmount": 0.0
    }
]
}

I used this code to merge both collections

$combined = $firstCollection->merge($secondCollection)

I expected below output

Collection {#350
  #items: array:2 [
    0 => {#346
      +"id": 1
      +"code": "C000215"
      +"name": "Mostafa Mohamed Bayomi"
      +"mobile1": "01228902157"
      +"mobile2": ""
      +"CoachServiceCount": 0
      +"CourseServiceCount": 1
      +"CommunicationCount": 0
      +"CoachServiceAmount": 0.0
    }
    1 => {#340
      +"id": 2
      +"code": "C000216"
      +"name": "Sayed Mohamed Bayomi"
      +"mobile1": "01228902158"
      +"mobile2": ""
      +"CoachServiceCount": 0
      +"CourseServiceCount": 2
      +"CommunicationCount": 0
      +"CoachServiceAmount": 0.0
    }
]
}

But I got this output

Collection {#350
  #items: array:4 [
    0 => {#342
      +"id": 1
      +"code": "C000215"
      +"name": "Mostafa Mohamed Bayomi"
      +"mobile1": "01228902157"
      +"mobile2": ""
      +"CoachServiceCount": 0
      +"CourseServiceCount": 1
      +"CommunicationCount": 0
    }
    1 => {#337
      +"id": 2
      +"code": "C000216"
      +"name": "Sayed Mohamed Bayomi"
      +"mobile1": "01228902158"
      +"mobile2": ""
      +"CoachServiceCount": 0
      +"CourseServiceCount": 2
      +"CommunicationCount": 0
    }
    2 => {#346
      +"id": 1
      +"code": "C000215"
      +"name": "Mostafa Mohamed Bayomi"
      +"mobile1": "01228902157"
      +"mobile2": ""
      +"CoachServiceAmount": 0.0
    }
    3 => {#340
      +"id": 2
      +"code": "C000216"
      +"name": "Sayed Mohamed Bayomi"
      +"mobile1": "01228902158"
      +"mobile2": ""
      +"CoachServiceAmount": 0.0
    }
  ]
}

Upvotes: 0

Views: 1230

Answers (2)

Lito
Lito

Reputation: 1332

Use the union method: https://laravel.com/docs/10.x/collections#method-union

The union method adds the given array to the collection. If the given array contains keys that are already in the original collection, the original collection's values will be preferred:

$collection = collect([1 => ['a'], 2 => ['b']]);
 
$union = $collection->union([3 => ['c'], 1 => ['d']]);
 
$union->all();
 
// [1 => ['a'], 2 => ['b'], 3 => ['c']]

Upvotes: 0

PtrTon
PtrTon

Reputation: 3835

The issue

The documentation for merge() contains the following text:

The merge method merges the given array or collection with the original collection. If a string key in the given items matches a string key in the original collection, the given items's value will overwrite the value in the original collection (...) If the given items's keys are numeric, the values will be appended to the end of the collection

Meaning merge() will not work as you'd expected, since you are using numeric keys.

Solution 1: non-numeric keys

One solution for your issue would be to use non-numeric values as array keys, this solution will only work if the chosen field (code in the example, is the primary key):

$firstCollection = $firstCollection->keyBy('code');
$secondCollection = $secondCollection->keyBy('code');
$combined = $firstCollection->merge($secondCollection);

// Optional: reset the array keys to be numeric
$combined = $combined->values();

The keyBy method keys the collection by the given key. If multiple items have the same key, only the last one will appear in the new collection (source)

The values method returns a new collection with the keys reset to consecutive integers (source)

Solution 2: zip

If you have the same amount of elements and in the same order in both collections, you could do this:

$combined = $firstCollection->zip($secondCollection);

The zip method merges together the values of the given array with the values of the original collection at the corresponding index (source)

Upvotes: 2

Related Questions