Группировка массива по одному полю + помещение оставшихся полей во вложенные массивы

Столкнулся с куском легаси. Понимаю, что задача тривиальная. Имеется массив данных, возвращаемый 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 шт):

Автор решения: teran

Фомируете ключ по 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);
→ Ссылка
Автор решения: Ivan Shatsky

Немного альтернативная версия ответа от @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);
→ Ссылка
Автор решения: Solt

Если подтверждено, что 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);

→ Ссылка
Автор решения: Solt

Если уж предлагать альтернативы, позвольте представить цирковой номер с деструктуризацией. Да, так можно было ))))

Показан самый минимум полей, можно добавить сколько угодно других, но тут уж сами потренируйтесь.

<?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);
→ Ссылка