Группировка массива по одному полю + помещение оставшихся полей во вложенные массивы
Столкнулся с куском легаси. Понимаю, что задача тривиальная. Имеется массив данных, возвращаемый oci_fetch_all следующего вида:
Array(
[0] => Array(
[UUID] => 1
[FOO] => foo1
[BAZ] => bazabc
)
[1] => Array(
[UUID] => 1
[FOO] => foo1
[BAZ] => bazzxc
)
[2] => Array(
[UUID] => 2
[FOO] => foo2
[BAZ] => bazqwe
)
)
| UUID | FOO | BAZ | ... |
|---|---|---|---|
| 1 | foo1 | bazabc | ... |
| 1 | foo1 | bazzxc | ... |
| 2 | foo2 | bazqwe | ... |
| N | ... | ... | ... |
Необходимо преобразовать массив для последующего json_encode таким образом, чтобы получился json-объект следующего вида:
[
{
"UUID":1, "FOO":"foo1", "CONTENT": [
{"BAZ":"bazabc", "...":"..."},
{"BAZ":"bazzxc", "...":"..."}
]
},
{
"UUID":2, "FOO":"foo2", "CONTENT": [
{"BAZ":"bazqwe", "...":"..."}
]
}
]
Иными словами, чтобы выбранные ключи (UUID, FOO) стали set'ом на первом уровне массива, а оставшаяся часть полей исходного массива попала в новое поле в качестве вложенного массива. Т.е. группировка по выбранным полям + помещение оставшихся полей во вложенные массивы. UPD: В отличие от вопроса, который был указан в качестве дубликата при закрытии (PHP сгруппировать массив по значению), в текущем вопросе интересовал вариант группировки по нескольким полям. Пользователями были предложены решения, которые выполняли группировку по одному полю (отмечены как ответ) либо по нескольким с условиями (однозначное соответствие полей друг другу). Однако исходный вопрос был про группировку элементов по нескольким полям. По итогу, вопрос закрыт, опубликовать своё решение с группировкой по нескольким полям я уже не могу.
Ответы (4 шт):
Фомируете ключ по UUID+FOO и закидываете в массив результатов чего надо
$result = [];
foreach ($data as $row) {
$key = "{$row['UUID']}_{$row['FOO']}";
if (!$result[$key] ?? null) {
$result[$key] = [
'uuid' => $row['UUID'],
'foo' => $row['FOO'],
'content' => [],
];
}
$result[$key]['content'][] = ['BAZ' => $row['BAZ'], ...];
// unset($row['UUID'], $row['FOO']);
// $result[$key]['content'][] = $row;
}
$result = array_values($result);
Немного альтернативная версия ответа от @teran, с учётом пожеланий от автора вопроса:
$result = [];
foreach ($data as $row) {
$elem = Array('UUID' => $row['UUID'], 'FOO' => $row['FOO']);
$key = serialize($elem);
if (!isset($result[$key])) {
$result[$key] = array_merge($elem, Array('CONTENT' => []));
}
$result[$key]['CONTENT'][] = array_diff($row, $elem);
}
$result = array_values($result);
Если подтверждено, что UUID уникальные, я бы предложил их в качестве первичных ключей к массиву. Да, json превратит это не в массив, а в объект, но какая разница? А польза может быть большая, особенно если фронт-разработчик, который это получит умеет обращаться с ключами, а не вечно ходить кругами с помощью foreach/reduce/filter/contains и иже с ними.
<?php
$data=[
['UUID'=> 1, 'FOO' => 'foo1', 'BAZ' => 'bazabc'],
['UUID'=> 1, 'FOO' => 'foo1', 'BAZ' => 'bazzxc'],
['UUID'=> 2, 'FOO' => 'foo2', 'BAZ' => 'bazqwe'],
['UUID'=> 2, 'FOO' => 'foo2', 'BAZ' => 'bazeee'],
['UUID'=> 3, 'FOO' => 'foo3', 'BAZ' => 'bazrrr'],
];
$result = [];
foreach ($data as $row) {
//Чтоб не писать во всех строках $result[$key]
// кроме того, обращение по ссылке автоматом создаёт элемент
$group=&$result[$row['UUID']];
if(!$group) //Инициализируем новый блок
$group=['UUID'=>$row['UUID'],'FOO'=>$row['FOO'], 'content'=>[]];
// Если дальше идёт большая куча перекладываний,
// можно еще и content ссылкой сделать.
// $content=&$group['content'];
// Совсем небольшая цена для дальнейшей читабельности кода
// Если планируется много полей,
// то чтобы не перекладывать все элементы из массива в массив,
// удаляем ненужные и перекладываем весь.
unset($row['UUID'],$row['FOO']);
$group['content'][]=$row;
}
unset($group); // Без очистки повторное использование ссылок плохо заканчивается
//Если индексы прям совсем не нужны
//$result = array_values($result);
print_r($result);
//на время отладки советую полезные ключи //JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE
echo $result=json_encode($result);
Если уж предлагать альтернативы, позвольте представить цирковой номер с деструктуризацией. Да, так можно было ))))
Показан самый минимум полей, можно добавить сколько угодно других, но тут уж сами потренируйтесь.
<?php
$data=[
['UUID'=> 1, 'FOO' => 'foo1', 'BAZ' => 'bazabc'],
['UUID'=> 1, 'FOO' => 'foo1', 'BAZ' => 'bazzxc'],
['UUID'=> 2, 'FOO' => 'foo2', 'BAZ' => 'bazqwe'],
['UUID'=> 2, 'FOO' => 'foo2', 'BAZ' => 'bazeee'],
['UUID'=> 3, 'FOO' => 'foo3', 'BAZ' => 'bazrrr'],
];
foreach ($data as ['UUID'=>&$key,'FOO'=>&$result[$key]['FOO'], 'BAZ'=>&$result[$key]['content'][]]);
print_r($result);