Transparently flatten an array

后端 未结 2 1370
耶瑟儿~
耶瑟儿~ 2020-12-11 04:21

Reading this question Merge and group by several arrays i got the following idea: when working with multilevel arrays, with possibly repeating keys, it would be practical to

相关标签:
2条回答
  • 2020-12-11 04:29

    Example usage of RecursiveArrayIterator

    $array = array( 
        0 => 'a', 
        1 => array('subA','subB',array(0 => 'subsubA', 1 => 'subsubB', 2 => array(0 => 'deepA', 1 => 'deepB'))), 
        2 => 'b', 
        3 => array('subA','subB','subC'), 
        4 => 'c' 
    );
    
    foreach (return new RecursiveIteratorIterator(new RecursiveArrayIterator($array))
             as $key => $val) {
    
        printf(
            '%s: %s' . "\n",
            $key, $val
        );
    }
    
    /* Output:
    0: a
    0: subA
    1: subB
    0: subsubA
    1: subsubB
    0: deepA
    1: deepB
    2: b
    0: subA
    1: subB
    2: subC
    4: c
    */
    

    extending RecursiveIteratorIterator to return the current key-stack

    class MyRecursiveIteratorIterator extends RecursiveIteratorIterator
    {
      public function key() {
        return json_encode($this->getKeyStack());
      }
    
      public function getKeyStack() {
        $result = array();
        for ($depth = 0, $lim = $this->getDepth(); $depth < $lim; $depth += 1) {
          $result[] = $this->getSubIterator($depth)->key();
        }
        $result[] = parent::key();
        return $result;
      }
    }
    
    foreach ($it = new MyRecursiveIteratorIterator(new RecursiveArrayIterator($array))
             as $key => $val) {
    
      printf('%s (%s): %s' . "\n", implode('.', $it->getKeyStack()), $key, $val);
    }
    
    /* Output:
    0 ([0]): a
    1.0 ([1,0]): subA
    1.1 ([1,1]): subB
    1.2.0 ([1,2,0]): subsubA
    1.2.1 ([1,2,1]): subsubB
    1.2.2.0 ([1,2,2,0]): deepA
    1.2.2.1 ([1,2,2,1]): deepB
    2 ([2]): b
    3.0 ([3,0]): subA
    3.1 ([3,1]): subB
    3.2 ([3,2]): subC
    4 ([4]): c
    */
    

    Yet another version, using no RecursiveArrayIterator this time:

    function flatten(array $array = array(), $keyStack = array(), $result = array()) {
      foreach ($array as $key => $value) {
        $keyStack[] = $key;
    
        if (is_array($value)) {
          $result = flatten($value, $keyStack, $result);
        }
        else {
          $result[] = array(
            'keys' => $keyStack,
            'value' => $value
          );
        }
    
        array_pop($keyStack);
      }
    
      return $result;
    }
    
    foreach (flatten($array) as $element) {
      printf(
        '%s: %s (depth: %s)' . "\n",
        implode('.', $element['keys']),
        $element['value'],
        sizeof($element['keys'])
      );
    }
    
    /*
    0: a (depth: 1)
    1.0: subA (depth: 2)
    1.1: subB (depth: 2)
    1.2.0: subsubA (depth: 3)
    1.2.1: subsubB (depth: 3)
    1.2.2.0: deepA (depth: 4)
    1.2.2.1: deepB (depth: 4)
    2: b (depth: 1)
    3.0: subA (depth: 2)
    3.1: subB (depth: 2)
    3.2: subC (depth: 2)
    4: c (depth: 1)
    */
    
    0 讨论(0)
  • 2020-12-11 04:40

    You can also write a simple traversal function:

    function flatten($node, $fn, $keys = array()) {
        if (! is_array($node)) {
            $fn($node, $keys);
        } else {
            foreach ($node as $k => $v) {
                $new_keys   = $keys;
                $new_keys[] = $k;
                flatten($v, $fn, $new_keys);
            }
        }
    }
    
    $array = array( 
        0 => 'a', 
        1 => array('subA','subB',array(0 => 'subsubA', 1 => 'subsubB', 2 => array(0 => 'deepA', 1 => 'deepB'))), 
        2 => 'b', 
        3 => array('subA','subB','subC'), 
        4 => 'c' 
    );
    // will output: a subA subB subsubA subsubB deepA deepB b subA subB subC c 
    flatten($array, function($v, $k) {
        echo $v . ' ';
    });
    

    If you don't want to call it each time with another function as a parameter, I've also written an adapter that will return an array:

    function flatten_array($node) {
        $acc = array();
        flatten($node, function($node, $keys) use (&$acc) {
            $acc[implode('.', $keys)] = $node;
        });
        return $acc;
    }
    
    // will spit out the same output as that in Yoshi's answer:
    foreach (flatten_array($array) as $k => $v) {
        echo $k .' => ' . $v . "\n";
    }
    

    Notes:

    • array_walk_recursive cannot be used/is not the same thing, as it skips over the keys that hold an array
    • I've written my examples with anonymous functions; if your PHP isn't new enough, you have to name the functions and call them with call_user_func.
    0 讨论(0)
提交回复
热议问题