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

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

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

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

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

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

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

Попробуйте следующее:

 $array = your array $result = call_user_func_array('array_merge', $array); echo "<pre>"; print_r($result); 

EDIT: 21-Aug-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

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

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

 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> 

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

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

EDIT: Обратите внимание, что это может быть составлено для сглаживания любого количества уровней. Мы можем сделать это несколькими способами:

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

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

 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 'a' потерян, потому что 'b' также имеет ключ 0

Цитируя ответ Шивиша:

Просто добавьте false как второй параметр ($use_keys) к вызову iterator_to_array

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

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

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

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

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

 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; } с 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', ) 

Вы можете сделать это с uuzo goodies :

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

См. Здесь :

Хитрость передается как исходным, так и целевым массивами по ссылке.

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

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

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

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

Потому что код здесь выглядит страшно. Вот функция, которая также преобразует многомерный массив в синтаксис, совместимый с форматом 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; } с /** * 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; } 

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

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

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

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

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

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

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

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

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

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

Мне нужно было представить многомерный массив PHP в формате ввода HTML.

 $test = [ 'a' => [ 'b' => [ 'c' => ['a', 'b'] ] ], 'b' => 'c', 'c' => [ 'd' => 'e' ] ]; $flatten = function ($input, $parent = []) use (&$flatten) { $return = []; foreach ($input as $k => $v) { if (is_array($v)) { $return = array_merge($return, $flatten($v, array_merge($parent, [$k]))); } else { if ($parent) { $key = implode('][', $parent) . '][' . $k . ']'; if (substr_count($key, ']') != substr_count($key, '[')) { $key = preg_replace('/\]/', '', $key, 1); } } else { $key = $k; } $return[$key] = $v; } } return $return; }; die(var_dump( $flatten($test) )); array(4) { ["a[b][c][0]"]=> string(1) "a" ["a[b][c][1]"]=> string(1) "b" ["b"]=> string(1) "c" ["c[d]"]=> string(1) "e" } 

Если у вас есть массив объектов и вы хотите сгладить его узлом, просто используйте эту функцию:

 function objectArray_flatten($array,$childField) { $result = array(); foreach ($array as $node) { $result[] = $node; if(isset($node->$childField)) { $result = array_merge( $result, objectArray_flatten($node->$childField,$childField) ); unset($node->$childField); } } return $result; } с function objectArray_flatten($array,$childField) { $result = array(); foreach ($array as $node) { $result[] = $node; if(isset($node->$childField)) { $result = array_merge( $result, objectArray_flatten($node->$childField,$childField) ); unset($node->$childField); } } return $result; } 

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

С PHP 7 я не знаю. Я создал решение, которое я называю array_moonwalk , который использует как ссылки, так и рекурсию, чтобы сгладить и дедуплицировать многомерный массив и сохраняет глубину самого глубокого узла для каждого дубликата, найденного для многомерной реконструкции массива.

Управляет дедупликацией листовых узлов на разных глубинах без использования foreach , и единственным ответом до сих пор является использование array_walk . Не проверен на производительность. YMMV.