#php #arrays #recursion
#php #массивы #рекурсия
Вопрос:
Из вложенного массива я хочу сгенерировать одномерный ассоциативный массив, который содержит для каждого листа конкатенацию восходящих ключей.
Краткие сведения
- Пример ожидаемых результатов
1.1. Ввод
1.2. Вывод
- Фактический пример результатов
1.1. Ввод
1.2. Вывод
- Вопрос
- Минимальные, проверяемые исполняемые исходные файлы
4.1. Пояснения
4.2. Источники и выполнение
Пример ожидаемых результатов
Ввод
Следующий вложенный массив:
[
'key1' => 'foo',
'key2' => [
'key3' => [
0 => ['key4' => 'bar' ],
1 => ['key4' => 'azerty']
]
]
]
Вывод
Следующий одномерный ассоциативный массив (символ склеивания для конкатенации ключей: _
):
[
'key1' => 'foo',
'key2_key3_0_key4' => 'bar',
'key2_key3_1_key4' => 'azerty'
]
Фактический пример результатов
Ввод
[
'etat' => 'bar',
'proposition_en_cours' => [
'fichiers' => [
0 => ['url_fichier' => 'foo' ],
1 => ['url_fichier' => 'bar']
]
]
]
Вывод
Array
(
[] => bar
[proposition_en_cours] => Array
(
[fichiers] => Array
(
[0] => Array
(
[url_fichier] => foo
)
[1] => Array
(
[url_fichier] => bar
)
)
)
[proposition_en_cours_fichiers] => Array
(
[0] => Array
(
[url_fichier] => foo
)
[1] => Array
(
[url_fichier] => bar
)
)
[proposition_en_cours_fichiers_0] => foo
[proposition_en_cours_fichiers_0_1] => bar
)
Question
Как вы можете видеть, полученный мной массив отличается по всем пунктам от ожидаемого. Я не могу понять, почему.
Минимальные, проверяемые исполняемые исходные файлы
Пояснения
Я инициализирую массив, который должен содержать все восходящие ключи для каждого листа : $key_in_db_format = [];
.
Я выполняю итерацию по входному массиву. Для каждого элемента (листа или подмассива) я открываю $key_in_db_format
, если и только если текущая глубина равна последней глубине. Если это массив (т. Е. Не лист): Я добавляю ключ к $key_in_db_format
. Я устанавливаю значение (лист) в ключе, который является конкатенацией восходящих ключей.
Источники и выполнение
- Сначала определите этот массив в пустом PHP-скрипте по вашему выбору:
$values = [ 'etat' => 'bar', 'proposition_en_cours' => [ 'fichiers' => [ 0 => [ 'url_fichier' => 'foo' ], 1 => [ 'url_fichier' => 'bar' ] ] ] ];
- Затем скопируйте / вставьте следующий код, и вы сможете его выполнить:
$values_to_insert_in_meta_table = []; $iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($values), RecursiveIteratorIterator::SELF_FIRST); $last_depth = 0; $key_in_db_format = []; foreach ($iterator as $value_key_field => $value_value_field) { if($iterator->getDepth() == $last_depth) { array_pop($key_in_db_format); } if(is_array($value_value_field)) { array_push($key_in_db_format, $value_key_field); } else { $values_to_insert_in_meta_table[implode('_', $key_in_db_format)] = $value_value_field; } $last_depth = $iterator->getDepth(); } echo '<pre>'; print_r($values_to_insert_in_meta_table);
Комментарии:
1. то, как вы определили свой входной массив, вы не получите желаемый формат ключа, например,
key2_key3_0_key4
; вместо этого вы получитеkey2_0_key3_0_key4
. это потому, что если вы не предоставите ключ, по умолчанию будет использоваться целое число с нулевым значением. не было бы никакого способа провести различие между ними. однако вы можете использовать string integer для ваших явных ключей, чтобы ваша функция обнаруживала и включала указанные ключи, т. Е. игнорировала0
,1
, и т. Д. Ключи, включала"0"
, »1"
и т. Д. Ключи.
Ответ №1:
Возможно, я что-то пропустил, но, насколько я понимаю, я бы сделал что-то подобное:
<?php
function flatten(array $array, ?string $prefix = null): array {
$prefix = $prefix === null ? '' : "{$prefix}_";
$output = [];
foreach ($array as $key => $value) {
$key = $prefix . $key;
if (is_array($value)) {
$output = array_merge($output, flatten($value, $key));
} else {
$output[$key] = $value;
}
}
return $output;
}
var_export(flatten([
'key1' => 'foo',
'key2' => [
'key3' => [
0 => ['key4' => 'bar' ],
1 => ['key4' => 'azerty']
]
]
]));
Вывод:
array (
'key1' => 'foo',
'key2_key3_0_key4' => 'bar',
'key2_key3_1_key4' => 'azerty',
)
Комментарии:
1. Это элегантное решение
Ответ №2:
Я думаю, что нашел решение !!! 🙂
$iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($values), RecursiveIteratorIterator::SELF_FIRST);
$key_in_db_format = [];
$current_counter = 0;
foreach($iterator as $value_key_field => $value_value_field) {
if(is_array($value_value_field)) {
$current_counter = 0;
array_push($key_in_db_format, $value_key_field);
}
if(!is_array($value_value_field)) {
$key_to_insert_in_db = !empty($key_in_db_format) ? implode('_', $key_in_db_format) . '_' . $value_key_field : $value_key_field ;
$values_to_insert_in_meta_table[$key_to_insert_in_db] = $value_value_field;
if($current_counter == count($iterator->getSubIterator())) {
array_pop($key_in_db_format);
}
$current_counter ;
}
}
echo '<br /> <pre>';
print_r($values_to_insert_in_meta_table);
exit;
Идея заключается в:
- Мы добавляем к массиву восходящих ключей ключ тогда и только тогда, когда текущий элемент не является листом.
- Если текущий элемент является листом, то мы определяем ключ, равный вложенным восходящим ключам ПЛЮС (конкатенация) ключ текущего элемента. Более того, мы извлекаем массив восходящих ключей, если нет следующих элементов-братьев и сестер.