Reputation: 3050
I'm having some issues with the Laravel Collection class.
What I'm trying to do:
So what I currently do is:
unique('name')
.This gives me unique facilitators, but only picks the first one it detects and then deletes the other ones.
So lets say I have this collection:
Collection {
#items: array:3 [
0 => array:2 [
"name" => "John"
"site" => "Example"
]
1 => array:2 [
"name" => "Martin"
"site" => "Another"
]
2 => array:2 [
"name" => "John"
"site" => "Another"
]
]
}
With unique()
I would get:
Collection {
#items: array:3 [
0 => array:2 [
"name" => "John"
"site" => "Example"
]
1 => array:2 [
"name" => "Martin"
"site" => "Another"
]
]
}
And this is what I want to get:
Collection {
#items: array:3 [
0 => array:2 [
"name" => "John"
"site" => ["Example", "Another"]
]
1 => array:2 [
"name" => "Martin"
"site" => "Another"
]
]
}
Does anyone have an idea how I could accomplish this with Laravel's collection class?
Upvotes: 3
Views: 1121
Reputation: 4321
you can do it by chaining to get exactly what you want, assuming $collection is the main collection
$collection->groupBy('name')->map(function($facilitators) {
return ['name' => $facilitators->first()['name'], 'site' => $facilitators->pluck('site')->toArray()];
})->values()->toArray();
first we group by name so it will give 2 dimensional array inside collection, then iterate to that and name will be common so get it from first element, then from all the element pluck site and convert it to array, using flatMap will make it single level nested.
Upvotes: 0
Reputation: 12358
When stuck with collections always remember reduce is a powerful tool in your arsenal.
Building on Sam's answer which I couldn't get to work, I think using reduce alongside groupBy should work...
$sites = collect([
["name" => "John", "site" => "Example"],
["name" => "Martin", "site" => "Another"],
["name" => "John", "site" => "Another"],
]);
$sites->groupBy('name')->reduce(function ($result, $item) {
$result[] = [
'name' => $item->first()['name'],
'sites' => $item->pluck('site')->toArray()
];
return $result;
}, collect([]))->toArray();
And from the console...
λ php artisan tinker
Psy Shell v0.8.2 (PHP 7.0.10 ÔÇö cli) by Justin Hileman
>>> $sites = collect([
... ["name" => "John", "site" => "Example"],
... ["name" => "Martin", "site" => "Another"],
... ["name" => "John", "site" => "Another"],
... ]);
=> Illuminate\Support\Collection {#698
all: [
[
"name" => "John",
"site" => "Example",
],
[
"name" => "Martin",
"site" => "Another",
],
[
"name" => "John",
"site" => "Another",
],
],
}
>>> $sites->groupBy('name')->reduce(function ($result, $item) {
... $result[] = ['name' => $item->first()['name'], 'sites' => $item->pluck('site')->toArray()];
...
... return $result;
... }, collect([]))->toArray();
=> [
[
"name" => "John",
"sites" => [
"Example",
"Another",
],
],
[
"name" => "Martin",
"sites" => [
"Another",
],
],
]
One thing to note is that you specified in your question that the sites should return a single string if there's only one site and an array if there's many.The above solution does not provide this! I think this is inconsistent and you should always return an array for the sites key, even if it only has one value as it will make it more difficult to read and manipulate later on.
However, if this is something important, you could instead check if there are many sites when using pluck to set an array and if not you could set it as a single string, like this:
$sites->groupBy('name')->reduce(function ($result, $item) {
$result[] = [
'name' => $item->first()['name'],
'sites' => $item->pluck('site')->count() > 1 ? $item->pluck('site') : $item->first()['site']
];
return $result;
}, collect([]))->toArray();
which would produce...
[
[
"name" => "John",
"sites" => [
"Example",
"Another",
],
],
[
"name" => "Martin",
"sites" => "Another",
],
]
Upvotes: 3