Сгруппировать элементы двумерного массива, используя одно из полей как ключ, а второе - как значение

Есть массив:

Array
(
    [0] => Array
        (
            [entry_id] => 3
            [element_id] => 4
            [value] => a:2:{i:2;s:14:"Имя";i:4;s:18:"Фамилия";}
        )

    [1] => Array
        (
            [entry_id] => 3
            [element_id] => 6
            [value] => Тестовый отзыв
        )

    [2] => Array
        (
            [entry_id] => 3
            [element_id] => 11
            [value] => a:1:{i:2;s:16:"г. Москва";}
        )
)

Данный массив собирает плагин из формы, которые заполняют люди на сайте. Все эти данные хранятся в одной таблице БД по entry_id. element_id - в массиве много ( номер вопроса в форме). Как собрать все эти данные, одного заявления в один массив для его дальнейшего вывода на сайте. Чтобы это выглядело, примерно так:

Array
(
    [0] => Array
        (
            [entry_id] => 3
            [value] => a:2:{i:2;s:14:"Имя";i:4;s:18:"Фамилия";}
            [feedback] => Тестовый отзыв
            [city] => a:1:{i:2;s:16:"г. Москва";}
        ))

Либо вставив ключ из element_id

Array
(
    [0] => Array
        (
            [entry_id] => 3
            [4] => a:2:{i:2;s:14:"Имя";i:4;s:18:"Фамилия";}
            [6] => Тестовый отзыв
            [city] => a:1:{i:2;s:16:"г. Москва";}
        ))

Ответы (3 шт):

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

Нет ничего проще, предлагаю даже с entry_id в виде ключей:

<?php
//Пополнить справочник названиями полей.
$fields=[
    4=> ['name'=>'value',   'is_text'=>0],
    6=> ['name'=>'feedback','is_text'=>1],
    11=>['name'=>'city',    'is_text'=>0],

    //Причина, по которой не надо ставить ?? вместо ?: при подстановках,
    //ибо нам нужно конкретное имя поля, а не его "вероятность присутствия"
    //Ведь справочник может быть сгенерирован из каких-то внешних мест.
    12=>['name'=>'',        'is_text'=>1] 
];
// Для повышения производительности, но в ущерб читабельности и отладки 
// справочник может быть в таком виде:
// 4=> ['value',0],

// А также получение данных может быть в виде неассоциативного массива:
// $src=[[3,6,'Тестовый отзыв'] ...
// Такой подход будет требовать значительной внимательности, 
// но иногда может оказаться единственным возможным в условиях недостатка 
// ресурсов (памяти, CPU, пропускной способности и т.д.)


$src=[
    [
        'entry_id' => 3,
        'element_id' => 4,
        //Эта строка сериализована криво
        // Возможная причина: текст мог быть в кодировке типа utf8mb4,
        // в процессе ручного копи-паста из системы в систему,
        // буквы утратили внутреннюю длину и стали utf8mb2 а может 
        // потерялись непечатаемые символы
        // Это хороший пример того, что в БД надо складывать только
        // **надёжно проверенные данные**
        //'value' => 'a:2:{i:2;s:14:"Имя";i:4;s:18:"Фамилия";}'
        'value' => 'a:2:{i:2;s:6:"Имя";i:4;s:14:"Фамилия";}'
    ],
    [
        'entry_id' => 3,
        'element_id' => 6,
        'value' => 'Тестовый отзыв'
    ],
    [
        'entry_id' => 3,
        'element_id' => 11,
        'value' => 'a:1:{i:2;s:16:"г. Москва";}'
    ],
    [
        'entry_id' => 3,
        'element_id' => 12,
        'value' => 'блаблабла'
    ],
    
];
$dst = [];
foreach($src as $row){
    //Версия, исключающая появление предупреждения
    //$field=(isset($fields[$row['element_id']]['name']) && $fields[$row['element_id']]['name']) ? $fields[$row['element_id']]['name'] : $row['element_id'];
    
    //Версия, использующая игнорирование предупреждений о несуществующих переменных, но работающая быстрее и читается легче
    $field=$fields[$row['element_id']]['name'] ?: $row['element_id'];
    
    //Искать через in_array пару десятков значений в миллионном цикле - не лучшее решение.
    //$dst[$row['entry_id']][$field] = (in_array($row['element_id'], $text_fields)) ? $row['value'] : unserialize($row['value']);
    $dst[$row['entry_id']][$field] = $fields[$row['element_id']]['is_text'] ? $row['value'] : unserialize($row['value']);
}


print_r($dst);

https://3v4l.org/j6dZn

А вообще, опыт показывает, что хранить данные удобней в JSON. Он и портируется куда угодно, и запаковка/распаковка работает не медленней.

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

Несколько альтернативных способов решения этой задачи, используя вместо итерирования элементов массива внутри цикла встроенные возможности PHP для работы с такими массивами. Я привожу их не потому, что решение Сергея чем-то плохо, а просто для демонстрации этих возможностей.

Во-первых, в PHP есть функция array_column(), которая позволяет перестроить один массив в другой, используя в качестве индексов одни поля элементов массива, а в качестве значений другие. И если мы гарантированно знаем, что все элементы массива содержат только одно, уникальное значение поля entry_id, как это показано в примере исходных данных в исходном вопросе, то можно сделать так:

$dst = array_column($src, 'value', 'element_id');
print_r($dst);

Пример вывода:

Array
(
    [4] => a:2:{i:2;s:14:"Имя";i:4;s:18:"Фамилия";}
    [6] => Тестовый отзыв
    [11] => a:1:{i:2;s:16:"г. Москва";}
    [12] => блаблабла
)

Если это не так, то данный способ не подойдёт, каждый очередной элемент исходного массива будет перезаписывать в результирующем массиве элемент с соответствующим индексом element_id. Так, например, на таком наборе исходных данных:

$src = [
    [
        'entry_id' => 3,
        'element_id' => 4,
        'value' => 'a:2:{i:2;s:14:"Имя";i:4;s:18:"Фамилия";}'
    ],
    [
        'entry_id' => 3,
        'element_id' => 6,
        'value' => 'Тестовый отзыв'
    ],
    [
        'entry_id' => 3,
        'element_id' => 11,
        'value' => 'a:1:{i:2;s:16:"г. Москва";}'
    ],
    [
        'entry_id' => 3,
        'element_id' => 12,
        'value' => 'блаблабла'
    ],
    [
        'entry_id' => 4,
        'element_id' => 12,
        'value' => 'блинблинблин'
    ],
];

мы получим такой результат:

Array
(
    [4] => a:2:{i:2;s:14:"Имя";i:4;s:18:"Фамилия";}
    [6] => Тестовый отзыв
    [11] => a:1:{i:2;s:16:"г. Москва";}
    [12] => блинблинблин
)

Но если наша задача - получить такой массив только для одного конкретного значения entry_id, то можно совместить эту функцию с функцией фильтрации массива array_filter() по данному конкретному значению entry_id:

function filter_by_entry($entry) {
    return function($item) use ($entry) {
        return $item['entry_id'] == $entry;
    };
}

$dst = array_column(array_filter($src, filter_by_entry(3)), 'value', 'element_id');
print_r($dst);

Если же наша задача действительно получить полное перестроенное представление исходного массива, с сохранением всех возможных значений entry_id, то для этого мы можем воспользоваться функцией array_reduce():

$dst = array_reduce($src, function($acc, $item) {
    $acc[$item['entry_id']][$item['element_id']] = $item['value'];
    return $acc;
}, []); // начинаем с пустого массива как начального значения результата
print_r($dst);

Пример вывода:

Array
(
    [3] => Array
        (
            [4] => a:2:{i:2;s:14:"Имя";i:4;s:18:"Фамилия";}
            [6] => Тестовый отзыв
            [11] => a:1:{i:2;s:16:"г. Москва";}
            [12] => блаблабла
        )

    [4] => Array
        (
            [12] => блинблинблин
        )

)
→ Ссылка
Автор решения: Ипатьев

Общий ответ на этот вопрос дал Ivan Shatsky (всегда надо помнить, что хороший ответ должен быть более общим, чтобы в первую очередь быть полезным всем тем, кто придёт в поисках ответа на такой же вопрос) - array_column() с дополнительным параметром для ключа прекрасно справляется с задачей.

Если же говорить про конкретную проблему автора вопроса, которая кроме завяленной группировки включает ещё и другие элементарные задачи - такие как сопоставление числовых индексов строковым, возможное наличие разных entry_id, необходимость распаковки сериализованного массива и превращение этого массива в строку - то можно посоветовать вот такой простой код, без заморочек и выдуманных условий:

<?php
// Справочник строковых значений для element_id
$fields=[4=>'value', 6=>'feedback', 11=>'city'];
// Справочник для element_id, которым не нужно делать unserialize
$text_fields = [6];
// массив надо всегда надо инициализировать перед использованием
$dst = [];
foreach($src as $row){
    $field = $fields[$row['element_id']];
    if (in_array($row['element_id'], $text_fields)) {
        $dst[$row['entry_id']][$field] = $row['value'];
    } else {
        $dst[$row['entry_id']][$field] = implode(" ", unserialize($row['value']));
    }
}

Здесь мы перебираем массив в цикле, и группируем результат так, чтобы для каждого entry_id собирался массив соответствующих значений.

Для корректной работы этого кода необходимо указать строковые значения для всех возможных element_id, а так же указать, для каких element_id не нужно применять unserialize().

→ Ссылка