it-swarm.com.ru

Как выровнять многомерный массив?

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

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

208
Alix Axel

Вы можете использовать стандартную библиотеку 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 
240
VolkerK

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

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

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

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

$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

73
Prasanth Bendra

Чтобы сгладить без рекурсии (как вы просили), вы можете использовать стек . Естественно, вы можете поместить это в свою собственную функцию, такую ​​как 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, что я считаю хорошей идеей.

23
hakre

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

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
nilamo

Просто подумал, что укажу, что это фолд, поэтому можно использовать 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.

16
Warbo

В 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
)
14
Joyce Babu

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

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
)

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


EDIT (2017-03-01)

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

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

Цитируя Svish ответ:

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

14
Allen Linatoc

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

$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

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

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

Я считаю, что это самое чистое решение без использования каких-либо мутаций или незнакомых классов.

<?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

Попробуйте следующую простую функцию:

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

Итак, из этого:

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',
            ),
          ),
        ),
      ),
    ),
  ),
)

ты получаешь:

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

Вы можете сделать это с помощью ouzo goodies :

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

Смотрите: Здесь

2
woru

Если вам действительно не нравится рекурсия ... попробуйте вместо этого переключиться :)

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

Примечание: В этой простой версии это не поддерживает ключи массива.

2
BurninLeo

Хитрость заключается в передаче массива источника и назначения по ссылке.

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

2
Arsham

Эта версия может делать глубокие, мелкие или определенное количество уровней:

/**
 * @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

Потому что код в здесь выглядит страшно. Вот функция, которая также преобразует многомерный массив в синтаксис, совместимый с HTML-формой, но который легче читать.

/**
 * 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

Для 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

Вот упрощенный подход:

$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

Любой, кто ищет действительно чистое решение для этого; вот вариант: 

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

Печать 

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

Это мое решение, используя ссылку:

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

Как насчет использования рекурсивного генератора? 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;
        }
    }
}
0
Andriy