web-dev-qa-db-ger.com

So reduzieren Sie ein mehrdimensionales Array

Ist es in PHP möglich, ein (zwei-/mehr-) dimensionales Array zu glätten, ohne Rekursion oder Referenzen zu verwenden?

Ich interessiere mich nur für die Werte, so dass die Schlüssel ignoriert werden können, ich denke in den Zeilen von array_map() und array_values().

208
Alix Axel

Sie können die Standard PHP Library (SPL) verwenden, um die Rekursion "auszublenden".

$a = array(1,2,array(3,4, array(5,6,7), 8), 9);
$it = new RecursiveIteratorIterator(new RecursiveArrayIterator($a));
foreach($it as $v) {
  echo $v, " ";
}

druckt

1 2 3 4 5 6 7 8 9 
240
VolkerK

Ab PHP 5.3 scheint die kürzeste Lösung array_walk_recursive() mit der neuen Schließungssyntax zu sein:

function flatten(array $array) {
    $return = array();
    array_walk_recursive($array, function($a) use (&$return) { $return[] = $a; });
    return $return;
}
259
too much php

Lösung für zweidimensionales Array

Bitte versuchen Sie das: 

$array  = your array

$result = call_user_func_array('array_merge', $array);

echo "<pre>";
print_r($result);

EDIT: 21-Aug-13

Hier ist die Lösung, die für mehrdimensionale Arrays funktioniert:

function array_flatten($array) {
    $return = array();
    foreach ($array as $key => $value) {
        if (is_array($value)){
            $return = array_merge($return, array_flatten($value));
        } else {
            $return[$key] = $value;
        }
    }

    return $return;
}

$array  = Your array

$result = array_flatten($array);

echo "<pre>";
print_r($result);

Ref: http://php.net/manual/de/function.call-user-func-array.php

73
Prasanth Bendra

Um die Rekursion (wie Sie es gewünscht haben) zu vereinfachen, können Sie ein stack verwenden. Natürlich können Sie dies in eine eigene Funktion wie array_flatten setzen. Die folgende Version funktioniert ohne Tasten:.

function array_flatten(array $array)
{
    $flat = array(); // initialize return array
    $stack = array_values($array); // initialize stack
    while($stack) // process stack until done
    {
        $value = array_shift($stack);
        if (is_array($value)) // a value to further process
        {
            $stack = array_merge(array_values($value), $stack);
        }
        else // a value to take
        {
           $flat[] = $value;
        }
    }
    return $flat;
}

Elemente werden in ihrer Reihenfolge verarbeitet. Da Unterelemente auf den Stapel verschoben werden, werden sie als Nächstes verarbeitet.

Es ist auch möglich, Schlüssel zu berücksichtigen. Sie benötigen jedoch eine andere Strategie, um den Stack zu verarbeiten. Dies ist erforderlich, da Sie mit möglicherweise doppelten Schlüsseln in den Unterarrays umgehen müssen. Eine ähnliche Antwort in einer verwandten Frage: PHP Gehe durch ein mehrdimensionales Array und bewahre die Schlüssel auf

Ich bin mir nicht besonders sicher, aber ich hatte dies in der Vergangenheit getestet: RecurisiveIterator verwendet Rekursion, es hängt also davon ab, was Sie wirklich brauchen. Sollte es möglich sein, einen rekursiven Iterator basierend auf Stapeln zu erstellen:

foreach(new FlatRecursiveArrayIterator($array) as $key => $value)
{
    echo "** ($key) $value\n";
}

Demo

Ich habe es bisher nicht geschafft, den Stack basierend auf RecursiveIterator zu implementieren, was meiner Meinung nach eine schöne Idee ist.

23
hakre

Verwendet Rekursion. Wenn Sie sehen, wie nicht komplex es ist, wird sich Ihre Angst vor Rekursion auflösen, sobald Sie sehen, wie nicht komplex es ist.

function flatten($array) {
    if (!is_array($array)) {
        // nothing to do if it's not an array
        return array($array);
    }

    $result = array();
    foreach ($array as $value) {
        // explode the sub-array, and add the parts
        $result = array_merge($result, flatten($value));
    }

    return $result;
}


$arr = array('foo', array('nobody', 'expects', array('another', 'level'), 'the', 'Spanish', 'Inquisition'), 'bar');
echo '<ul>';
foreach (flatten($arr) as $value) {
    echo '<li>', $value, '</li>';
}
echo '<ul>';

Ausgabe:

<ul><li>foo</li><li>nobody</li><li>expects</li><li>another</li><li>level</li><li>the</li><li>Spanish</li><li>Inquisition</li><li>bar</li><ul>
17
nilamo

Ich dachte nur, ich würde darauf hinweisen, dass dies eine Falte ist, also kann array_reduce verwendet werden:

array_reduce($my_array, 'array_merge', array());

BEARBEITEN: Beachten Sie, dass dies zusammengestellt werden kann, um eine beliebige Anzahl von Ebenen zu reduzieren. Wir können dies auf verschiedene Arten tun:

// Reduces one level
$concat   = function($x) { return array_reduce($x, 'array_merge', array()); };

// We can compose $concat with itself $n times, then apply it to $x
// This can overflow the stack for large $n
$compose  = function($f, $g) {
    return function($x) use ($f, $g) { return $f($g($x)); };
};
$identity = function($x) { return $x; };
$flattenA = function($n) use ($compose, $identity, $concat) {
    return  function($x) use ($compose, $identity, $concat, $n) {
        return ($n === 0)? $x
                         : call_user_func(array_reduce(array_fill(0, $n, $concat),
                                                       $compose,
                                                       $identity),
                                          $x);
    };
};

// We can iteratively apply $concat to $x, $n times
$uncurriedFlip     = function($f) {
    return  function($a, $b) use ($f) {
        return $f($b, $a);
    };
};
$iterate  = function($f) use ($uncurriedFlip) {
    return  function($n) use ($uncurriedFlip, $f) {
    return  function($x) use ($uncurriedFlip, $f, $n) {
        return ($n === 0)? $x
                         : array_reduce(array_fill(0, $n, $f),
                                        $uncurriedFlip('call_user_func'),
                                        $x);
    }; };
};
$flattenB = $iterate($concat);

// Example usage:
$apply    = function($f, $x) {
    return $f($x);
};
$curriedFlip = function($f) {
    return  function($a) use ($f) {
    return  function($b) use ($f, $a) {
        return $f($b, $a);
    }; };
};

var_dump(
    array_map(
        call_user_func($curriedFlip($apply),
                       array(array(array('A', 'B', 'C'),
                                   array('D')),
                             array(array(),
                                   array('E')))),
        array($flattenA(2), $flattenB(2))));

Natürlich könnten wir auch Schleifen verwenden, aber die Frage fragt nach einer Kombinatorfunktion entlang der Linien array_map oder array_values.

16
Warbo

Ab PHP 5.6 können Sie zweidimensionale Arrays mit array_merge abflachen, nachdem Sie das äußere Array mit dem ...-Operator ausgepackt haben. Der Code ist einfach und klar.

$a = [[10, 20], [30, 40]];
$b = [["x" => "X", "y" => "Y"], ["p" => "P", "q" => "Q"]];

print_r(array_merge(...$a));
print_r(array_merge(...$b));

Array
(
    [0] => 10
    [1] => 20
    [2] => 30
    [3] => 40
)
Array
(
    [x] => X
    [y] => Y
    [p] => P
    [q] => Q
)

Es funktioniert jedoch nicht, wenn das äußere Array nicht numerische Schlüssel enthält. In diesem Fall müssen Sie zuerst array_values aufrufen.

$c = ["a" => ["x" => "X", "y" => "Y"], "b" => ["p" => "P", "q" => "Q"]];
print_r(array_merge(...array_values($c)));

Array
(
    [x] => X
    [y] => Y
    [p] => P
    [q] => Q
)
14
Joyce Babu

Direkt und One-Liner antworten.

function flatten_array(array $array)
{
    return iterator_to_array(
         new \RecursiveIteratorIterator(new \RecursiveArrayIterator($array)));
}

Verwendungszweck:

$array = [
    'name' => 'Allen Linatoc',
    'profile' => [
        'age' => 21,
        'favourite_games' => [ 'Call of Duty', 'Titanfall', 'Far Cry' ]
    ]
];

print_r( flatten_array($array) );

Ausgabe (in PsySH):

Array
(
    [name] => Allen Linatoc
    [age] => 21
    [0] => Call of Duty
    [1] => Titanfall
    [2] => Far Cry
)

Nun liegt es an Ihnen, wie Sie mit den Tasten umgehen. Prost


EDIT (2017-03-01)

Zitieren Nigel Alderton Anliegen/Problem:

Nur um dies zu verdeutlichen, bleiben Schlüssel (auch numerische) erhalten, so dass Werte, die denselben Schlüssel haben, verloren gehen. Zum Beispiel wird $array = ['a',['b','c']]Array ([0] => b, [1] => c ). Der 'a' geht verloren, da 'b' auch einen Schlüssel von 0 hat.

Zitieren Svish s Antwort:

Fügen Sie einfach false als zweiten Parameter ($use_keys) zum Aufruf " iterator_to_array " hinzu

14
Allen Linatoc

Flacht nur zweidimensionale Arrays ab:

$arr = [1, 2, [3, 4]];
$arr = array_reduce($arr, function ($a, $b) {
     return array_merge($a, (array) $b);
}, []);

// Result: [1, 2, 3, 4]
5
artnikpro

Diese Lösung ist nicht rekursiv. Beachten Sie, dass die Reihenfolge der Elemente etwas gemischt ist.

function flatten($array) {
    $return = array();
    while(count($array)) {
        $value = array_shift($array);
        if(is_array($value))
            foreach($value as $sub)
                $array[] = $sub;
        else
            $return[] = $value;
    }
    return $return;
}
4
too much php

Ich glaube, das ist die sauberste Lösung ohne Mutationen oder unbekannte Klassen.

<?php

function flatten($array)
{
    return array_reduce($array, function($acc, $item){
        return array_merge($acc, is_array($item) ? flatten($item) : [$item]);
    }, []);
}


// usage
$array = [1, 2, [3, 4], [5, [6, 7]], 8, 9, 10];
print_r(flatten($array));
4
Dariush Alipour

Versuchen Sie die folgende einfache Funktion:

function _flatten_array($arr) {
  while ($arr) {
    list($key, $value) = each($arr); 
    is_array($value) ? $arr = $value : $out[$key] = $value;
    unset($arr[$key]);
  }
  return (array)$out;
}

Also hiervon:

array (
  'und' => 
  array (
    'profiles' => 
    array (
      0 => 
      array (
        'commerce_customer_address' => 
        array (
          'und' => 
          array (
            0 => 
            array (
              'first_name' => 'First name',
              'last_name' => 'Last name',
              'thoroughfare' => 'Address 1',
              'premise' => 'Address 2',
              'locality' => 'Town/City',
              'administrative_area' => 'County',
              'postal_code' => 'Postcode',
            ),
          ),
        ),
      ),
    ),
  ),
)

du kriegst:

array (
  'first_name' => 'First name',
  'last_name' => 'Last name',
  'thoroughfare' => 'Address 1',
  'premise' => 'Address 2',
  'locality' => 'Town/City',
  'administrative_area' => 'County',
  'postal_code' => 'Postcode',
)
3
kenorb

Sie können es mit Ouzo Goodies tun:

 $result = Arrays::flatten($multidimensional);

Siehe: Here

2
woru

Wenn Sie eine Rekursion wirklich nicht mögen, versuchen Sie stattdessen zu verschieben :)

$a = array(1,2,array(3,4, array(5,6,7), 8), 9);
$o = [];
for ($i=0; $i<count($a); $i++) {
    if (is_array($a[$i])) {
        array_splice($a, $i+1, 0, $a[$i]);
    } else {
        $o[] = $a[$i];
    }
}

Hinweis: In dieser einfachen Version werden keine Array-Schlüssel unterstützt.

2
BurninLeo

Der Trick besteht darin, die Quell- und Ziel-Arrays als Referenz zu übergeben.

function flatten_array(&$arr, &$dst) {
    if(!isset($dst) || !is_array($dst)) {
        $dst = array();
    }
    if(!is_array($arr)) {
        $dst[] = $arr;
    } else {
        foreach($arr as &$subject) {
            flatten_array($subject, $dst);
        }
    }
}

$recursive = array('1', array('2','3',array('4',array('5','6')),'7',array(array(array('8'),'9'),'10')));
echo "Recursive: \r\n";
print_r($recursive);
$flat = null;
flatten_array($recursive, $flat);

echo "Flat: \r\n";
print_r($flat);

// If you change line 3 to $dst[] = &$arr; , you won't waste memory,
// since all you're doing is copying references, and imploding the array 
// into a string will be both memory efficient and fast:)

echo "String:\r\n";
echo implode(',',$flat);
2
Rick Garcia
/**
 * For merging values of a multidimensional array into one 
 *
 * $array = [
 *     0 => [
 *         0 => 'a1',
 *         1 => 'b1',
 *         2 => 'c1',
 *         3 => 'd1'
 *     ],
 *     1 => [
 *         0 => 'a2',
 *         1 => 'b2',
 *         2 => 'c2',
 *     ]
 * ];
 *
 * becomes : 
 *
 * $array = [
 *     0 => 'a1',
 *     1 => 'b1',
 *     2 => 'c1',
 *     3 => 'd1',
 *     4 => 'a2',
 *     5 => 'b2',
 *     6 => 'c2',
 *     
 * ]
 */
array_reduce
(
    $multiArray
    , function ($lastItem, $currentItem) {
        $lastItem = $lastItem ?: array();
        return array_merge($lastItem, array_values($currentItem));
    }
);

Gist-Ausschnitt

2
Arsham

Diese Version kann tiefe, flache oder eine bestimmte Anzahl von Ebenen ausführen:

/**
 * @param  array|object $array  array of mixed values to flatten
 * @param  int|boolean  $level  0:deep, 1:shallow, 2:2 levels, 3...
 * @return array
 */
function flatten($array, $level = 0) {
    $level = (int) $level;
    $result = array();
    foreach ($array as $i => $v) {
        if (0 <= $level && is_array($v)) {
            $v = flatten($v, $level > 1 ? $level - 1 : 0 - $level);
            $result = array_merge($result, $v);
        } elseif (is_int($i)) {
            $result[] = $v;
        } else {
            $result[$i] = $v; 
        }
    }
    return $result;
}
1
ryanve

Weil der Code in here unheimlich aussieht. Hier ist eine Funktion, die auch ein mehrdimensionales Array in eine mit HTML-Form kompatible Syntax konvertiert, jedoch einfacher zu lesen ist.

/**
 * Flattens a multi demensional array into a one dimensional
 * to be compatible with hidden html fields.
 *
 * @param array $array
 *  Array in the form:
 *  array(
 *    'a' => array(
 *      'b' => '1'
 *    )
 *  )
 *
 * @return array
 *  Array in the form:
 *  array(
 *    'a[b]' => 1,
 *  )
 */
function flatten_array($array) {
  // Continue until $array is a one-dimensional array.
  $continue = TRUE;
  while ($continue) {
    $continue = FALSE;

    // Walk through top and second level of $array and move 
    // all values in the second level up one level.
    foreach ($array as $key => $value) {
      if (is_array($value)) {
        // Second level found, therefore continue.
        $continue = TRUE;

        // Move each value a level up.
        foreach ($value as $child_key => $child_value) {
          $array[$key . '[' . $child_key . ']'] = $child_value;
        }

        // Remove second level array from top level.
        unset($array[$key]);
      }
    }
  }

  return $array;
}
1
Gellweiler

Wie wäre es mit einem rekursiven Generator? https://ideone.com/d0TXCg

<?php

$array = [
    'name' => 'Allen Linatoc',
    'profile' => [
        'age' => 21,
        'favourite_games' => [ 'Call of Duty', 'Titanfall', 'Far Cry' ]
    ]
];

foreach (iterate($array) as $item) {
    var_dump($item);
};

function iterate($array)
{
    foreach ($array as $item) {
        if (is_array($item)) {
            yield from iterate($item);
        } else {
            yield $item;
        }
    }
}
1
Andriy

Für PHP 5.2

function flatten(array $array) {
    $result = array();

    if (is_array($array)) {
        foreach ($array as $k => $v) {
            if (is_array($v)) {
                $result = array_merge($result, flatten($v));
            } else {
                $result[] = $v;
            }
        }
    }

    return $result;
}
1
Alexei T

Einfach eine andere Lösung posten)

function flatMultidimensionalArray(array &$_arr): array
{
    $result = [];
    \array_walk_recursive($_arr, static function (&$value, &$key) use (&$result) {
        $result[$key] = $value;
    });

    return $result;
}
0
James Bond

Hier ist ein vereinfachter Ansatz:

$My_Array = array(1,2,array(3,4, array(5,6,7), 8), 9);

function checkArray($value) {
    foreach ($value as $var) {
        if ( is_array($var) ) {
            checkArray($var);
        } else {
            echo $var;
        }
    }
}

checkArray($My_Array);
0
Jack Nicholson
<?php
//recursive solution

//test array
$nested_array = [[1,2,[3]],4,[5],[[[6,[7=>[7,8,9,10]]]]]];

/*-----------------------------------------
function call and return result to an array
------------------------------------------*/
$index_count = 1;
$flatered_array = array();
$flatered_array = flat_array($nested_array, $index_count);

/*-----------------------------------------
Print Result
-----------------------------------------*/
echo "<pre>";
print_r($flatered_array);


/*-----------------------------------------
function to flaten an array 
-----------------------------------------*/
function flat_array($nested_array, & $index_count, & $flatered_array) {

  foreach($nested_array AS $key=>$val) {
      if(is_array($val)) {
        flat_array($val, $index_count, $flatered_array);
      }
      else {
        $flatered_array[$index_count] = $val;
        ++$index_count;
      }      
  }

return $flatered_array;
}
?>
0
Furqan Freed

Jeder, der nach einer wirklich sauberen Lösung sucht; Hier ist eine Option: 

$test_array = array(
    array('test' => 0, 0, 0, 0),
    array(0, 0, 'merp' => array('herp' => 'derp'), 0),
    array(0, 0, 0, 0),
    array(0, 0, 0, 0)
);
$it = new RecursiveIteratorIterator(new RecursiveArrayIterator($test_array));
var_dump( iterator_to_array($it, false) ) ; 

Drucke 

 0 0 0 0 0 0 derp 0 0 0 0 0 0 0 0 0
0
Lewis

Dies ist meine Lösung mit einer Referenz:

function arrayFlatten($array_in, &$array_out){

    if(is_array($array_in)){
        foreach ($array_in as $element){
               arrayFlatten($element, $array_out);
        }
    }
    else{
        $array_out[] = $array_in; 
    }
}

$arr1 = array('1', '2', array(array(array('3'), '4', '5')), array(array('6')));

arrayFlatten($arr1, $arr2);

echo "<pre>";
print_r($arr2);
echo "</pre>";
0
Martyn Shutt