Я хочу запрашивать пользователей и группировать их по возрастному диапазону
Это то, что я сделал до сих пор
User::applicant()->get()
->groupBy(function ($item) {
return Carbon::parse($item->dob)->age;
})
->map(function ($collection) {
return $collection->count();
});
Это то, что я получил из запроса выше
Теперь хотите получить коллекцию и упорядочить по возрастному диапазону
18-24: 1,
25-35: 5,
36-45: 89,
46+ : 84
Есть ли также 0-18?
Да, я отредактировал свой вопрос @thisiskelvin
Нет 0-18, потому что это сайт агентства по подбору персонала, мы не ожидаем, что соискатели моложе 18 лет.
Чтобы было ясно, это для подсчета людей в этих возрастных группах, а не для того, чтобы фактические люди были сгруппированы вместе?
Да, мне нужно только количество людей в этих диапазонах для целей аналитики @thisiskelvin






Я собираюсь использовать комбинацию map() и mapToGroups(), я почти уверен, что должен быть более простой способ, но мне это понравилось:
$ranges = [ // the start of each age-range.
'18-24' => 18,
'25-35' => 25,
'36-45' => 36,
'46+' => 46
];
$output = User::applicant()
->get()
->map(function ($user) use ($ranges) {
$age = Carbon::parse($user->dob)->age;
foreach($ranges as $key => $breakpoint)
{
if ($breakpoint >= $age)
{
$user->range = $key;
break;
}
}
return $user;
})
->mapToGroups(function ($user, $key) {
return [$user->range => $user];
})
->map(function ($group) {
return count($group);
})
->sortKeys();
dd($output);
Идея этого состоит в том, чтобы добавить к каждой записи атрибут со значением, соответствующим их возрастному диапазону, а затем сгруппировать их по этому ключу, создав набор массивов пользователей, сгруппированных по диапазону, чтобы, наконец, подсчитать элементы для каждого подмассива внутри этого ключа.
Это должно вернуть что-то вроде:
=> Illuminate\Support\Collection {#2948 all: [ "25-35" => 1, "36-45" => 2, "46+" => 1, ], }
Я хочу объединить их в диапазоне 25-46 => 5 @HCK.
@TheodoryFaustine, как вы можете видеть в начале моего поста, я указал набор контрольных точек по возрасту, если вы хотите диапазон 25-46, просто включите: [ ..., 25, 47, ... ] в массив $ranges.
Кстати, забыл отсортировать ключи, исправил в ответ.
@TheodoryFaustine Я обновил свой ответ, чтобы вернуть ожидаемый результат.
@HCK Не знал о ->mapToGroups(). Приятно знать
@thisiskelvin да, у класса Collection есть несколько действительно интересных методов. Вот почему такого рода вопросы привлекают мое внимание, это маленькие головоломки, которые нужно решить. У Адама Уотэма есть хороший курс (Рефакторинг в коллекции, если я не ошибаюсь), когда он использует эту библиотеку, не видел ее, но у нее отличные отзывы.
@HCK Прочитай pdf! Очень хорошо, еще нужно пересмотреть несколько частей.
@thisiskelvin не знал, что есть PDF. теперь точно посмотрю ;)
@HCK добавил вас в твиттер, кстати
Большое спасибо, ребята, за вашу помощь @HCK
@TheodoryFaustine рад помочь. Хорошего дня
Что делать, если в таблице около 100 тыс. записей? Вы делаете ->get(), который извлечет все записи в памяти.
@Abhishek, конечно, в примере я сделал это простым способом, но проблема, которую вы подчеркиваете, реальна, если у вас есть тысячи записей (в памяти). В этом случае вы можете использовать курсоры/итераторы, которые теперь изначально поддерживаются с помощью класса LazyCollection. Подробнее об этом здесь.
@KennyHorna ага. Я бы посоветовал вам добавить это и в свой ответ. Хотя бы просто предупреждение.
Это непроверенное решение (которое потребует рефакторинга:
$groups = ['18-24' =>, '25-35', ..., '45'];
$applicants = User::applicant()->get();
$groups = collect($groups)
->map(function ($range, $key) use ($applicants) {
$rangeLimits = explode('-', $range);
$count = $applicants->filter(function ($applicant, $key) use ($rangeLimits) {
$age = Carbon::parse($applicant->dob)->age;
$verdict = $age >= $rangeLimits[0];
if (isset($rangeLimits[1])) {
$verdict = $age <= $rangeLimits[1];
}
return $verdict
})->count();
$range = ! isset($rangeLimits[1]) ? $range . '+' : $range;
return [ $range => $count ];
})->flatten()->all();
Сначала вам нужно создать массив нужных вам групп, последняя группа не должна иметь +.
Затем вы получаете всех претендентов.
Затем вы просматриваете каждую группу и выясняете, входят ли кандидаты в ее диапазон, и получаете подсчет. Это сопоставляется с результирующим массивом.
Спасибо, @thisisKelvin, но мой запрос не удался из-за переменной $max, нужно ли мне инициализировать ее значением 0?
@TheodoryFaustine Обновлено, должно было быть $rangeLimits[1],
Большое спасибо @thisiskelvin
Попробуй это
$users = \DB::table('users')
->select(DB::raw('concat(10*floor(age/10), \'-\', 10*floor(age/10) + 9) as `range`, count(*) as `numberofusers`'))
->groupBy('range')
->get();
У вас есть определенные возрастные диапазоны, которые вы хотите?