Возможно ли, в PHP, сгладить a (bi/multi) размерный массив без использования рекурсии или ссылок?
Меня интересуют только значения, поэтому ключи можно игнорировать, я думаю в строках array_map()
и array_values()
.
Возможно ли, в PHP, сгладить a (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-авг-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
В PHP 5.6 и выше вы можете сгладить двумерные массивы с помощью array_merge
после распаковки внешнего массива с помощью оператора ...
. Код прост и понятен.
array_merge(...$a);
Это работает и с коллекцией ассоциативных массивов.
$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
)
Чтобы сгладить без рекурсии (как вы просили), вы можете использовать stack. Естественно, вы можете поместить это в свою функцию как 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
, который, по моему мнению, является хорошей идеей.
Просто подумал, что я бы сказал, что это сгиб, поэтому можно использовать массив_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) {
if (!is_array($array)) {
// nothing to do if it 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>
Простой и однострочный ответ.
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'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;
}
Я считаю, что это самое чистое решение без использования каких-либо мутаций или незнакомых классов.
<?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));
Попробуйте выполнить следующую простую функцию:
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',
)
Хитрость передается как исходным, так и целевым массивами по ссылке.
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));
}
);
Вы можете сделать это с помощью ouzo goodies:
$result = Arrays::flatten($multidimensional);
Смотрите: Здесь
Если вам действительно не нравится рекурсия... попробуйте вместо этого переключиться :)
$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];
}
}
Примечание. В этой простой версии это не поддерживает ключи массива.
Как насчет использования рекурсивного генератора? 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;
}
}
}
Для 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;
}
Ах, без рекурсии !!! даже карта, уменьшить, фильтр включает рекурсию. Это невозможно, если вы не используете некоторые хакские php-пути, как показано ниже. Серьезно, ни один цикл не участвует в коде ниже, извините, если он убил хорошие функции массива :) :)
$arr=array(1,2,array(3,4, array(5,6,7), 8), 9);
$json=json_encode($arr);
$hacked_json=str_replace(['[',']'],"",$json);
$hacked_array=json_decode('['.$hacked_json.']');
echo "<pre>";
print_r($hacked_array);
вывод
Array
(
[0] => 1
[1] => 2
[2] => 3
[3] => 4
[4] => 1
[5] => 2
[6] => 6
[7] => 7
[8] => 8
[9] => 9
)
работает также для пары вложенных ключей
$arr=array(1,2,array(3,4, array(array("s"=>array(1),"w"=>array('q'=>1,'v'=>12)),6,7), 8), 9);
echo "<pre>";
print_r($arr);
$json=json_encode($arr);
$hacked_json=str_replace(['[',']'],"",$json);
$hacked_array=json_decode('['.$hacked_json.']');
echo "<pre>";
print_r(json_decode(json_encode($hacked_array), True));
вывод
Array
(
[0] => 1
[1] => 2
[2] => 3
[3] => 4
[4] => Array
(
[s] => 1
[w] => Array
(
[q] => 1
[v] => 12
)
)
[5] => 6
[6] => 7
[7] => 8
[8] => 9
)
Это мое решение, используя ссылку:
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;
}
?>
Здесь упрощенный подход:
$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);
Любой, кто ищет действительно чистое решение для этого; здесь вариант:
$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
Просто опубликуйте другое решение)
function flatMultidimensionalArray(array &$_arr): array
{
$result = [];
\array_walk_recursive($_arr, static function (&$value, &$key) use (&$result) {
$result[$key] = $value;
});
return $result;
}
Это может быть достигнуто с помощью array_walk_recursive
$a = array(1,2,array(3,4, array(5,6,7), 8), 9);
array_walk_recursive($a, function($v) use (&$r){$r[]=$v;});
print_r($r);
Рабочий пример: - https://3v4l.org/FpIrG
Мне нужно было представить многомерный массив 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"
}
Если у вас есть массив объектов и вы хотите сгладить его с помощью node, просто используйте эту функцию:
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, сгладить a (bi/multi) размерный массив без использования рекурсии или ссылок?
Как и PHP 7, я не знаю. Я создал решение, которое я называю array_moonwalk
, который использует как ссылки, так и рекурсию для сглаживания и дедупликации многомерного массива и сохраняет глубину самого глубокого node для каждого дубликата, найденного для многомерной реконструкции массива.
Управляет дедупликацией листовых узлов на разных глубинах без использования foreach
, и единственным ответом до сих пор является использование array_walk
. Не проверен на производительность. YMMV.