Zakaria Acharki
Zakaria Acharki

Reputation: 67525

How to iterate through two collections of the same length using foreach

If we want to concat some properties of two collections assuming they have the same length, e.g :

Collection 1 :

$collection1 = Collection { ▼
  #items: array:2 [ ▼
    0 => Item { ▼
      +id: 1
      +first_name: 'first_name 1'
      +last_name: 'first_name 1'
      +nbr_hours: 9
    }
    1 => Item { ▼
      +id: 2
      +first_name: 'first_name 2'
      +last_name: 'first_name 2'
      +nbr_hours: 10
    }
  ]
}

Collection 2 :

$collection2 = Collection { ▼
  #items: array:2 [ ▼
    0 => Item { ▼
      +id: 1
      +first_name: 'first_name 1'
      +last_name: 'first_name 1'
      +nbr_hours: 10
    }
    1 => Item { ▼
      +id: 2
      +first_name: 'first_name 2'
      +last_name: 'first_name 2'
      +nbr_hours: 12
    }
  ]
}

How we could loop through them in same time and concat the nbr_hours attributes, for example so the output will be like :

$nested_collection = Collection { ▼
  #items: array:2 [ ▼
    0 => Item { ▼
      +id: 1
      +first_name: 'first_name 1'
      +last_name: 'first_name 1'
      +nbr_hours: 19
    }
    1 => Item { ▼
      +id: 2
      +first_name: 'first_name 2'
      +last_name: 'first_name 2'
      +nbr_hours: 22
    }
  ]
}

Upvotes: 3

Views: 2371

Answers (5)

TheAlexLichter
TheAlexLichter

Reputation: 7289

First of all, requirements

A solution with using the foreach-loop to traverse through the collection(s) is wanted

Approach with two collections with same items in same order

You can use the index of each element that you traverse to get the "partner element" from the other collection, in case the item count and sorting is the same for each collection.

foreach($collection1 as $index => $item){
    $hours = $item->nbr_hours . $collection2[$index]->nbr_hours;
}

Approach regardless of sorting and item count

When your collection elements are not in the same order, or the collection item count is different, the query looks a bit more complex. In this case, you need to query elements where the id's are the same. To achieve that, we can change the collection indexes to the id's of the containing model. Now we can again use the index of one element to find the corresponding one in the other collection.

$collection2 = $collection2->keyBy('id'); //Change indexes to model ids 
$collection1 = $collection1->keyBy('id'); //For both collections
foreach($collection1 as $index => $item){
    $hours = $item->nbr_hours . $collection2->get($index)->nbr_hours;
}

Upvotes: 1

prateekkathal
prateekkathal

Reputation: 3572

A small modification to @AmitGupta's answer. If the collection 2 is required to be matched by $item->id. Try the below answer. :)

$nested_collection = $collection1->map(function($item) use ($collection2) {
    $item->nbr_hours += $collection2->where('id', $item->id)->first()->nbr_hours;

    return $item;
});

Let me know :)

Upvotes: 1

Amit Gupta
Amit Gupta

Reputation: 17678

You can use map method as:

$nested_collection  = $collection1->map(function ($item, $key) use($collection2) {
    $item->nbr_hours = $item->nbr_hours + $collection2->get($key)->nbr_hours;
    return $item;
});

Note - If the values are not on same order then you can use values method.

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

Upvotes: 1

Laerte
Laerte

Reputation: 7083

If you want to use Collection methods, you could use this approach:

    $zip = $c1->zip($c2->toArray());
    $m = $zip->map(function($items, $key) {
        $sum = 0;
        foreach ($items as $k => $item) {
            $sum += $item['nbr_hours'];
        }
        $r = $items[$k];
        $r['nbr_hours'] = $sum;
        return $r;
    });

Basically, zip method will "merge" the arrays in the same key. Then, if you use the map method, you can iterate over the main keys. After that, you can deal with both arrays as items. So, you can make any kind of operations you need, and then return a new array as the result.

Upvotes: 1

Sougata Bose
Sougata Bose

Reputation: 31749

This is a example with array what you can do to achieve this -

$c1 = [
['id' => 1, 'hrs' => 9],
['id' => 2, 'hrs' => 12],
];

$c2 = [
['id' => 1, 'hrs' => 10],
['id' => 2, 'hrs' => 11],
];

foreach($c1 as &$c) {
    // get the index for the id from the 2nd array
    $c2_index = array_search($c['id'], array_column($c2, 'id'));
    // Increment the value
    $c['hrs'] += $c2[$c2_index]['hrs'];
}

Demo

Upvotes: 0

Related Questions