Collections
Syntax#
- $collection = collect([‘Value1’, ‘Value2’, ‘Value3’]); // Keys default to 0, 1, 2, …,
Remarks#
Illuminate\Support\Collection
provides a fluent and convenient interface to deal with arrays of data. You may well have used these without knowing, for instance Model queries that fetch multiple records return an instance of Illuminate\Support\Collection
.
For up to date documentation on Collections you can find the official documentation here
Creating Collections
Using the collect()
helper, you can easily create new collection instances by passing in an array such as:
$fruits = collect(['oranges', 'peaches', 'pears']);
If you don’t want to use helper functions, you can create a new Collection using the class directly:
$fruits = new Illuminate\Support\Collection(['oranges', 'peaches', 'pears']);
As mentioned in the remarks, Models by default return a Collection
instance, however you are free to create your own collections as needed. If no array is specified on creation, an empty Collection will be created.
where()
You can select certain items out of a collection by using the where()
method.
$data = [
['name' => 'Taylor', 'coffee_drinker' => true],
['name' => 'Matt', 'coffee_drinker' => true]
];
$matt = collect($data)->where('name', 'Matt');
This bit of code will select all items from the collection where the name is ‘Matt’. In this case, only the second item is returned.
Nesting
Just like most array methods in Laravel, where()
supports searching for nested elements as well. Let’s extend the example above by adding a second array:
$data = [
['name' => 'Taylor', 'coffee_drinker' => ['at_work' => true, 'at_home' => true]],
['name' => 'Matt', 'coffee_drinker' => ['at_work' => true, 'at_home' => false]]
];
$coffeeDrinkerAtHome = collect($data)->where('coffee_drinker.at_home', true);
This will only return Taylor, as he drinks coffee at home. As you can see, nesting is supported using the dot-notation.
Additions
When creating a Collection of objects instead of arrays, those can be filtered using where()
as well. The Collection will then try to receive all desired properties.
Please note, that since Laravel 5.3 the where()
method will try to loosely compare the values by default. That means when searching for (int)1
, all entries containing '1'
will be returned as well. If you don’t like that behaviour, you may use the whereStrict()
method.
Using Get to lookup value or return default
You often find yourself in a situation where you need to find a variables corresponding value, and collections got you covered.
In the example below we got three different locales in an array with a corresponding calling code assigned. We want to be able to provide a locale and in return get the associated calling code. The second parameter in get
is a default parameter if the first parameter is not found.
function lookupCallingCode($locale)
{
return collect([
'de_DE' => 49,
'en_GB' => 44,
'en_US' => 1,
])->get($locale, 44);
}
In the above example we can do the following
lookupCallingCode('de_DE'); // Will return 49
lookupCallingCode('sv_SE'); // Will return 44
You may even pass a callback as the default value. The result of the callback will be returned if the specified key does not exist:
return collect([
'de_DE' => 49,
'en_GB' => 44,
'en_US' => 1,
])->get($locale, function() {
return 44;
});
Using Contains to check if a collection satisfies certain condition
A common problem is having a collection of items that all need to meet a certain criteria. In the example below we have collected two items for a diet plan and we want to check that the diet doesn’t contain any unhealthy food.
// First we create a collection
$diet = collect([
['name' => 'Banana', 'calories' => '89'],
['name' => 'Chocolate', 'calories' => '546']
]);
// Then we check the collection for items with more than 100 calories
$isUnhealthy = $diet->contains(function ($i, $snack) {
return $snack["calories"] >= 100;
});
In the above case the $isUnhealthy
variable will be set to true
as Chocolate meets the condition, and the diet is thus unhealthy.
Using Pluck to extract certain values from a collection
You will often find yourself with a collection of data where you are only interested in parts of the data.
In the example below we got a list of participants at an event and we want to provide a the tour guide with a simple list of names.
// First we collect the participants
$participants = collect([
['name' => 'John', 'age' => 55],
['name' => 'Melissa', 'age' => 18],
['name' => 'Bob', 'age' => 43],
['name' => 'Sara', 'age' => 18],
]);
// Then we ask the collection to fetch all the names
$namesList = $partcipants->pluck('name')
// ['John', 'Melissa', 'Bob', 'Sara'];
You can also use pluck
for collections of objects or nested arrays/objects with dot notation.
$users = User::all(); // Returns Eloquent Collection of all users
$usernames = $users->pluck('username'); // Collection contains only user names
$users->load('profile'); // Load a relationship for all models in collection
// Using dot notation, we can traverse nested properties
$names = $users->pluck('profile.first_name'); // Get all first names from all user profiles
Using Map to manipulate each element in a collection
Often you need to change the way a set of data is structured and manipulate certain values.
In the example below we got a collection of books with an attached discount amount. But we much rather have a list of books with a price that’s already discounted.
$books = [
['title' => 'The Pragmatic Programmer', 'price' => 20, 'discount' => 0.5],
['title' => 'Continuous Delivery', 'price' => 25, 'discount' => 0.1],
['title' => 'The Clean Coder', 'price' => 10, 'discount' => 0.75],
];
$discountedItems = collect($books)->map(function ($book) {
return ['title' => $book["title"], 'price' => $book["price"] * $book["discount"]];
});
//[
// ['title' => 'The Pragmatic Programmer', 'price' => 10],
// ['title' => 'Continuous Delivery', 'price' => 12.5],
// ['title' => 'The Clean Coder', 'price' => 5],
//]
This could also be used to change the keys, let’s say we wanted to change the key title
to name
this would be a suitable solution.
Using sum, avg, min or max on a collection for statistical calculations
Collections also provide you with an easy way to do simple statistical calculations.
$books = [
['title' => 'The Pragmatic Programmer', 'price' => 20],
['title' => 'Continuous Delivery', 'price' => 30],
['title' => 'The Clean Coder', 'price' => 10],
]
$min = collect($books)->min('price'); // 10
$max = collect($books)->max('price'); // 30
$avg = collect($books)->avg('price'); // 20
$sum = collect($books)->sum('price'); // 60
Sorting a collection
There are a several different ways of sorting a collection.
Sort()
The sort
method sorts the collection:
$collection = collect([5, 3, 1, 2, 4]);
$sorted = $collection->sort();
echo $sorted->values()->all();
returns : [1, 2, 3, 4, 5]
The sort
method also allows for passing in a custom callback with your own algorithm. Under the hood sort uses php’s usort
.
$collection = $collection->sort(function ($a, $b) {
if ($a == $b) {
return 0;
}
return ($a < $b) ? -1 : 1;
});
SortBy()
The sortBy
method sorts the collection by the given key:
$collection = collect([
['name' => 'Desk', 'price' => 200],
['name' => 'Chair', 'price' => 100],
['name' => 'Bookcase', 'price' => 150],
]);
$sorted = $collection->sortBy('price');
echo $sorted->values()->all();
returns: [
['name' => 'Chair', 'price' => 100],
['name' => 'Bookcase', 'price' => 150],
['name' => 'Desk', 'price' => 200],
]
The sortBy
method allows using dot notation format to access deeper key in order to sort a multi-dimensional array.
$collection = collect([
["id"=>1,"product"=>['name' => 'Desk', 'price' => 200]],
["id"=>2, "product"=>['name' => 'Chair', 'price' => 100]],
["id"=>3, "product"=>['name' => 'Bookcase', 'price' => 150]],
]);
$sorted = $collection->sortBy("product.price")->toArray();
return: [
["id"=>2, "product"=>['name' => 'Chair', 'price' => 100]],
["id"=>3, "product"=>['name' => 'Bookcase', 'price' => 150]],
["id"=>1,"product"=>['name' => 'Desk', 'price' => 200]],
]
SortByDesc()
This method has the same signature as the sortBy
method, but will sort the collection in the opposite order.
Using reduce()
The reduce
method reduces the collection to a single value, passing the result of each iteration into the subsequent iteration. Please see reduce method.
The reduce
method loops through each item with a collection and produces new result to the next iteration. Each result from the last iteration is passed through the first parameter (in the following examples, as $carry
).
This method can do a lot of processing on large data sets. For example the following examples, we will use the following example student data:
$student = [
['class' => 'Math', 'score' => 60],
['class' => 'English', 'score' => 61],
['class' => 'Chemistry', 'score' => 50],
['class' => 'Physics', 'score' => 49],
];
Sum student’s total score
$sum = collect($student)
->reduce(function($carry, $item){
return $carry + $item["score"];
}, 0);
Result: 220
Explanation:
$carry
is the result from the last iteration.- The second parameter is the default value for the $carry in the first round of iteration. This case, the default value is 0
Pass a student if all their scores are >= 50
$isPass = collect($student)
->reduce(function($carry, $item){
return $carry && $item["score"] >= 50;
}, true);
Result: false
Explanation:
- Default value of $carry is true
- If all score is greater than 50, the result will return true; if any less than 50, return false.
Fail a student if any score is < 50
$isFail = collect($student)
->reduce(function($carry, $item){
return $carry || $item["score"] < 50;
}, false);
Result: true
Explain:
- the default value of $carry is false
- if any score is less than 50, return true; if all scores are greater than 50, return false.
Return subject with the highest score
$highestSubject = collect($student)
->reduce(function($carry, $item){
return $carry === null || $item["score"] > $carry["score"] ? $item : $carry;
});
result: [ "subject" => "English", "score" => 61 ]
Explain:
-
The second parameter is not provided in this case.
-
The default value of $carry is null, thus we check for that in our conditional.
Using macro() to extend collections
The macro()
function allows you to add new functionality to Illuminate\Support\Collection
objects
Usage:
Collection::macro("macro_name", function ($parameters) {
// Your macro
});
For example:
Collection::macro('uppercase', function () {
return $this->map(function ($item) {
return strtoupper($item);
});
});
collect(["hello", "world"])->uppercase();
Result: ["HELLO", "WORLD"]
Using Array Syntax
The Collection
object implements the ArrayAccess
and IteratorAggregate
interface, allowing it to be used like an array.
Access collection element:
$collection = collect([1, 2, 3]);
$result = $collection[1];
Result: 2
Assign new element:
$collection = collect([1, 2, 3]);
$collection[] = 4;
Result: $collection
is [1, 2, 3, 4]
Loop collection:
$collection = collect(["a" => "one", "b" => "two"]);
$result = "";
foreach($collection as $key => $value){
$result .= "(".$key.": ".$value.") ";
}
Result: $result
is (a: one) (b: two)
Array to Collection conversion:
To convert a collection to a native PHP array, use:
$array = $collection->all();
//or
$array = $collection->toArray()
To convert an array into a collection, use:
$collection = collect($array);
Using Collections with Array Functions
Please be aware that collections are normal objects which won’t be converted properly when used by functions explicitly requiring arrays, like array_map($callback)
.
Be sure to convert the collection first, or, if available, use the method provided by the Collection
class instead: $collection->map($callback)