Grouping only some elements in a Laravel collection

Given the following data:
manufacturers makes
id | name id | manufacturer_id | name
---|----- -- | --------------- | ----
1 | Honda 1 | 1 | Honda
2 | Toyota 2 | 1 | Acura
3 | Mazda 3 | 2 | Toyota
4 | Other 4 | 2 | Lexus
5 | 3 | Mazda
6 | 4 | Other
I'm trying to get an array that looks like this:
[
"Honda" => [1 => "Honda", 2 => "Acura"],
"Toyota" => [3 => "Toyota", 4 => "Lexus"],
5 => "Mazda",
6 => "Other",
]
In other words, group by manufacturer, but only if the manufacturer has more than one make. The method for specifying this condition is eluding me. So far I've got this:
Make::with("manufacturer")
->get()
->groupBy(
fn (Make $m) => $m->manufacturer->models->count() < 2 ? null : $m->manufacturer->name
)
->map(
fn (Collection $c) => $c->mapWithKeys(
fn (Make $m) => [$m->id => $m->name]
)
)
->toArray();
I had hoped returning null
from the groupBy
callback would prevent the grouping, but no such luck. It gives me this:
[
"" => [5 => "Mazda", 6 => "Other"]
"Honda" => [1 => "Honda", 2 => "Acura"],
"Toyota" => [3 => "Toyota", 4 => "Lexus"],
]
So I want a way to either conditionally group in the first place, or conditionally flatten after the grouping. I know I can do the latter manually, but it seems like something there should be a built-in Collection method for doing.
Answer
Returning null
from groupBy
groups single-make manufacturers under a "" (empty string) key, which isn’t what you want. Laravel’s groupBy doesn’t natively skip grouping for certain items, and there’s no direct collection method to conditionally group only some elements.
You could group all makes by manufacturer, then post-process to flatten single-make groups:
$results = Make::with('manufacturer')
->get()
->groupBy('manufacturer.name')
->flatMap(function ($makes, $manufacturerName) {
// If only one make, return [id => name] at top level
if ($makes->count() === 1) {
$make = $makes->first();
return [$make->id => $make->name];
}
// Otherwise, return grouped [id => name] under manufacturer name
return [
$manufacturerName => $makes->mapWithKeys(
fn (Make $m) => [$m->id => $m->name]
),
];
})
->toArray();
Enjoyed this article?
Check out more content on our blog or follow us on social media.
Browse more articles