Как выровнять многомерный массив? - PullRequest
223 голосов
/ 24 августа 2009

Возможно ли в PHP сгладить (bi / multi) размерный массив без использования рекурсии или ссылок?

Меня интересуют только значения, поэтому ключи можно игнорировать, я думаю в строках array_map() и array_values().

Ответы [ 26 ]

266 голосов
/ 24 августа 2009

Начиная с PHP 5.3 самое короткое решение выглядит как array_walk_recursive() с новым синтаксисом замыканий:

function flatten(array $array) {
    $return = array();
    array_walk_recursive($array, function($a) use (&$return) { $return[] = $a; });
    return $return;
}
251 голосов
/ 24 августа 2009

Вы можете использовать Стандартную библиотеку PHP (SPL) , чтобы "скрыть" рекурсию.

$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, " ";
}

печать

1 2 3 4 5 6 7 8 9 
79 голосов
/ 20 февраля 2013

Решение для двумерного массива

Пожалуйста, попробуйте это:

$array  = your array

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

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

РЕДАКТИРОВАТЬ: 21 августа-13

Вот решение, которое работает для многомерного массива:

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

Ссылка: http://php.net/manual/en/function.call-user-func-array.php

23 голосов
/ 22 октября 2011

Чтобы сгладить без рекурсии (как вы просили), вы можете использовать стек . Естественно, вы можете поместить это в свою собственную функцию, например array_flatten. Ниже приведена версия, которая работает без ключей:.

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

Элементы обрабатываются в их порядке. Поскольку подэлементы будут перемещены поверх стека, они будут обработаны следующим.

Можно также учитывать ключи, однако вам потребуется другая стратегия для работы со стеком. Это необходимо, потому что вам нужно иметь дело с возможными дублирующимися ключами в подмассивах. Аналогичный ответ в связанном вопросе: PHP Прогулка по многомерному массиву при сохранении ключей

Я не совсем уверен, но я проверял это в прошлом: RecurisiveIterator использует рекурсию, так что это зависит от того, что вам действительно нужно. Должна быть возможность создания рекурсивного итератора на основе стеков:

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

Демо

Я пока не смог реализовать стек на основе RecursiveIterator, что, на мой взгляд, хорошая идея.

17 голосов
/ 24 августа 2009

Использует рекурсию. Надеюсь, увидев, насколько он не сложный, ваш страх рекурсии рассеется, как только вы увидите, насколько он не сложный.

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

Выход:

<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 голосов
/ 17 июля 2013

Просто подумал, что я укажу, что это сгиб, поэтому можно использовать array_reduce:

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

РЕДАКТИРОВАТЬ: Обратите внимание, что это может быть составлено для выравнивания любого количества уровней. Мы можем сделать это несколькими способами:

// 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))));

Конечно, мы могли бы также использовать циклы, но вопрос требует использования комбинаторной функции вдоль строк array_map или array_values.

15 голосов
/ 28 января 2016

Простой и Однострочный ответ.

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

Использование:

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

print_r( flatten_array($array) );

Вывод (в PsySH):

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

Теперь вам решать, как вы будете обращаться с ключами. Приветствия


РЕДАКТИРОВАТЬ (2017-03-01)

Цитата Найджел Олдертон Проблема / проблема:

Просто для пояснения, при этом сохраняются ключи (даже числовые), поэтому значения с одинаковым ключом теряются. Например, $array = ['a',['b','c']] становится Array ([0] => b, [1] => c ). 'a' потеряно, потому что 'b' также имеет ключ 0

Цитировать Свиш ответ:

Просто добавьте false в качестве второго параметра ($use_keys) к iterator_to_array call

15 голосов
/ 21 октября 2017

В PHP 5.6 и выше вы можете выравнивать двумерные массивы с помощью array_merge после распаковки внешнего массива с помощью оператора ... Код прост и понятен.

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

Но это не работает, когда внешний массив имеет не числовые ключи. В этом случае вам сначала нужно будет позвонить array_values.

$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
)
6 голосов
/ 23 ноября 2016

Сглаживает только двумерные массивы:

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

// Result: [1, 2, 3, 4]
5 голосов
/ 24 августа 2009

Это решение нерекурсивное. Обратите внимание, что порядок элементов будет несколько смешанным.

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;
}
...