3

From a nested array, I want to generate the 1D associative array which contains, for each leaf, its ascending keys concatenation.

Summary

  1. Expected results example

    1.1. Input

    1.2. Output

  2. Actual results example

    1.1. Input

    1.2. Output

  3. Question

  4. Minimal, Testable Executable Sources

    4.1. Explanations

    4.2. Sources & Execution


Expected results example

Input

The following nested array:

[
   'key1' => 'foo',
   'key2' => [
         'key3' => [
               0 => ['key4' => 'bar' ],
               1 => ['key4' => 'azerty']
         ]
   ]    
]

Output

The following 1D associative array (glue character for the concatenation of the keys: _):

[
   'key1' => 'foo', 
   'key2_key3_0_key4' => 'bar',
   'key2_key3_1_key4' => 'azerty'
]

Actual results example

Input

[
   'etat' => 'bar',
   'proposition_en_cours' => [
         'fichiers' => [
               0 => ['url_fichier' => 'foo' ],
               1 => ['url_fichier' => 'bar']
         ]
   ]    
]

Output

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

As you can see, the array I get differs in all points from the expected one. I can't figure out why.

Minimal, Testable Executable Sources

Explanations

I initialize an array that must contain all the ascending keys for each leaf: $key_in_db_format = [];.

I iterate on the input array. For each element (leaf or subarray), I pop $key_in_db_format if, and only if, the current depth equals the last depth. If it's an array (i.e.: not a leaf): I add the key to $key_in_db_format. I set a value (the leaf) at the key that is the concatenation of the ascending keys.

Sources & Execution

  1. First, define this array in an empty PHP script of your choice:

    $values = [
    
        'etat' => 'bar',
    
        'proposition_en_cours' => [
            'fichiers' => [
                0 => [ 'url_fichier' => 'foo' ],
                1 => [ 'url_fichier' => 'bar' ]
             ]
        ]
    
    ];
    
  2. Then, copy/paste the following code and you will be able to execute it:

     $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
  • the way you defined your input array, you won't get the desired key format, eg, key2_key3_0_key4; instead you will get key2_0_key3_0_key4. this is because if you don't provide a key, the default used will be zero-based integer. there would be no way to distinguish between the two. however you can use string integer for your explicit keys for your function to detect and include said keys, ie, ignore 0, 1, etc keys, include "0", "1", etc keys. Commented Dec 5, 2020 at 17:21

2 Answers 2

1

Maybe I missed something, but as far as I understand, I would do something like that:

<?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']
        ]
    ]
]));

Output:

array (
  'key1' => 'foo',
  'key2_key3_0_key4' => 'bar',
  'key2_key3_1_key4' => 'azerty',
)
Sign up to request clarification or add additional context in comments.

1 Comment

That is an elegant solution ++
1

I think I've found a solution!!! :-)

$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;

The idea is:

  1. We add to the array of ascendent keys the key if, and only if, the current element is not a leaf.

  2. If the current element is a leaf, then we define the key equalled to the imploded ascendent keys PLUS (concatenation) the current element's key. Moreover we pop the array of ascendent keys if there are not following siblings elements.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.