Вложенные массивы PHP: сжатие всех ключей дерева каждого листа приводит к многомерному массиву вместо одномерного ассоциативного

#php #arrays #recursion

#php #массивы #рекурсия

Вопрос:

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

Краткие сведения

  1. Пример ожидаемых результатов

    1.1. Ввод

    1.2. Вывод

  2. Фактический пример результатов

    1.1. Ввод

    1.2. Вывод

  3. Вопрос
  4. Минимальные, проверяемые исполняемые исходные файлы

    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 . Я устанавливаю значение (лист) в ключе, который является конкатенацией восходящих ключей.

Источники и выполнение

  1. Сначала определите этот массив в пустом PHP-скрипте по вашему выбору:
     $values = [
    
        'etat' => 'bar',
    
        'proposition_en_cours' => [
            'fichiers' => [
                0 => [ 'url_fichier' => 'foo' ],
                1 => [ 'url_fichier' => 'bar' ]
             ]
        ]
    
    ];
     
  2. Затем скопируйте / вставьте следующий код, и вы сможете его выполнить:
      $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;
 

Идея заключается в:

  1. Мы добавляем к массиву восходящих ключей ключ тогда и только тогда, когда текущий элемент не является листом.
  2. Если текущий элемент является листом, то мы определяем ключ, равный вложенным восходящим ключам ПЛЮС (конкатенация) ключ текущего элемента. Более того, мы извлекаем массив восходящих ключей, если нет следующих элементов-братьев и сестер.