Czy w PHP można spłaszczyć tablicę wymiarową (bi / multi) bez użycia rekurencji lub referencji?
Interesują mnie tylko wartości, więc klucze można zignorować, myślę w wierszach array_map()i array_values().
Czy w PHP można spłaszczyć tablicę wymiarową (bi / multi) bez użycia rekurencji lub referencji?
Interesują mnie tylko wartości, więc klucze można zignorować, myślę w wierszach array_map()i array_values().
Odpowiedzi:
Możesz użyć Standardowej Biblioteki PHP (SPL), aby „ukryć” rekursję.
$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, " ";
}
odciski
1 2 3 4 5 6 7 8 9
iterator_to_array($it, false)unika potrzeby foreach.
function flatten($arr){ $it = new RecursiveIteratorIterator(new RecursiveArrayIterator($arr)); return iterator_to_array($it, true); }Mam nadzieję, że to pomaga innym.
W PHP 5.3 najkrótszym rozwiązaniem wydaje się być array_walk_recursive()nowa składnia zamknięć:
function flatten(array $array) {
$return = array();
array_walk_recursive($array, function($a) use (&$return) { $return[] = $a; });
return $return;
}
useskładni, aby działała, array_walk_recursiveponieważ nie akceptuje $userdataparametru opcjonalnego przez odniesienie
Rozwiązanie dla 2-wymiarowej tablicy
Spróbuj tego:
$array = your array
$result = call_user_func_array('array_merge', $array);
echo "<pre>";
print_r($result);
EDYCJA: 21 sierpnia-13
Oto rozwiązanie, które działa dla macierzy wielowymiarowej:
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);
Patrz: http://php.net/manual/en/function.call-user-func-array.php
call_user_func_array('array_merge', [])(zwróć uwagę na pustą tablicę) zwraca null i wyzwala błąd ostrzegawczy php. Jest to łatwe rozwiązanie, jeśli wiesz na pewno, że tablica nie będzie pusta, ale nie jest to częste założenie, które wielu może poczynić.
$result = $array ?call_user_func_array('array_merge', $array) : [];
W PHP 5.6 i nowszych możesz spłaszczyć tablice dwuwymiarowe array_mergepo rozpakowaniu tablicy zewnętrznej za pomocą ...operatora. Kod jest prosty i przejrzysty.
array_merge(...$a);
Działa to również z kolekcją tablic asocjacyjnych.
$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
)
Ale to nie działa, gdy zewnętrzna tablica ma klucze nienumeryczne. W takim przypadku najpierw musisz zadzwonić 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
)
Aktualizacja: Na podstawie komentarza @MohamedGharib
To wyrzuci błąd, jeśli tablica zewnętrzna jest pusta, ponieważ array_mergezostałaby wywołana z zerowymi argumentami. Można tego uniknąć, dodając pustą tablicę jako pierwszy argument.
array_merge([], ...$a);
array_merge([], ...$a);
Aby spłaszczyć bez rekurencji (tak jak prosiłeś), możesz użyć stosu . Oczywiście możesz wprowadzić tę funkcję jako własną array_flatten. Oto wersja, która działa bez kluczy :.
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;
}
Elementy przetwarzane są w kolejności. Ponieważ podelementy zostaną przeniesione na stos, zostaną one przetworzone w następnej kolejności.
Możliwe jest również uwzględnienie kluczy, jednak do obsługi stosu potrzebna będzie inna strategia. Jest to konieczne, ponieważ musisz poradzić sobie z możliwymi duplikatami kluczy w pod-tablicach. Podobna odpowiedź w pokrewnym pytaniu: PHP Przejdź przez wielowymiarową tablicę zachowując klucze
Nie jestem do końca pewien, ale II przetestowałem to w przeszłości: RecurisiveIteratorużywa rekurencji, więc zależy to od tego, czego naprawdę potrzebujesz. Powinno być również możliwe utworzenie iteratora rekurencyjnego na podstawie stosów:
foreach(new FlatRecursiveArrayIterator($array) as $key => $value)
{
echo "** ($key) $value\n";
}
Do tej pory nie udało mi się wdrożyć stosu, na podstawie RecursiveIteratorktórego moim zdaniem jest to dobry pomysł.
if(!empty($value)){$flat[] = $value}wewnątrz instrukcji else, aby zapobiec dodaniu pustego do tablicy wyników. Niesamowita funkcja!
Prosta i jednokierunkowa odpowiedź.
function flatten_array(array $array)
{
return iterator_to_array(
new \RecursiveIteratorIterator(new \RecursiveArrayIterator($array)));
}
Stosowanie:
$array = [
'name' => 'Allen Linatoc',
'profile' => [
'age' => 21,
'favourite_games' => [ 'Call of Duty', 'Titanfall', 'Far Cry' ]
]
];
print_r( flatten_array($array) );
Wyjście (w PsySH):
Array
(
[name] => Allen Linatoc
[age] => 21
[0] => Call of Duty
[1] => Titanfall
[2] => Far Cry
)
Teraz zależy od ciebie, jak poradzisz sobie z kluczami. Twoje zdrowie
EDYCJA (2017-03-01)
Cytując problem / problem Nigela Aldertona :
Aby to wyjaśnić, zachowuje klucze (nawet te numeryczne), więc wartości, które mają ten sam klucz, zostają utracone. Na przykład
$array = ['a',['b','c']]staje sięArray ([0] => b, [1] => c ).'a'Jest stracone, ponieważ'b'ma również klucz0
Cytując odpowiedź Svisha :
Po prostu dodaj false jako drugi parametr
($use_keys)do wywołania iterator_to_array
$array = ['a',['b','c']]staje się Array ([0] => b, [1] => c ). 'a'Jest stracone, ponieważ 'b'ma również klucz 0.
Wykorzystuje rekurencję. Mam nadzieję, że po zobaczeniu, jak to nie jest skomplikowane, twój strach przed rekurencją rozproszy się, gdy zobaczysz, jak to nie jest skomplikowane.
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>';
Wynik:
<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>
Pomyślałem, że zwrócę uwagę na to, że jest to fold, więc można użyć array_reduce:
array_reduce($my_array, 'array_merge', array());
EDYCJA: Zauważ, że można to skomponować w celu spłaszczenia dowolnej liczby poziomów. Możemy to zrobić na kilka sposobów:
// 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))));
Oczywiście, moglibyśmy również użyć pętli, ale pytanie wymaga funkcji kombinatorycznej wzdłuż linii array_map lub array_values.
foldją ustawić na 4 poziomy, albo fold . folduzyskać 3 poziomy, albo fold . fold . folduzyskać 2 poziomy itp. Zapobiega to również ukrywaniu się błędów; na przykład. jeśli chcę spłaszczyć tablicę 5D, ale dostaję tablicę 4D, błąd zostanie natychmiast wyzwolony.
$concat, myślę, że powinieneś to po prostu nazwać $flatten. array_mergejest php odpowiednikiem concat. Próbowałem zostać array_concatdodany jako alias dla array_merge.
To rozwiązanie nie jest rekurencyjne. Zauważ, że kolejność elementów będzie nieco mieszana.
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;
}
shiftingwartość poza tablicą i ponowne dołączenie jej na końcu nie ma większego sensu. Chyba array_merge()zamiast tego chciałeś ?
Uważam, że jest to najczystsze rozwiązanie bez użycia mutacji ani nieznanych klas.
<?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));
Wypróbuj następującą prostą funkcję:
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;
}
Więc z tego:
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',
),
),
),
),
),
),
)
dostajesz:
array (
'first_name' => 'First name',
'last_name' => 'Last name',
'thoroughfare' => 'Address 1',
'premise' => 'Address 2',
'locality' => 'Town/City',
'administrative_area' => 'County',
'postal_code' => 'Postcode',
)
Sztuczka polega na przekazywaniu tablic źródłowej i docelowej przez odniesienie.
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));
}
);
Możesz to zrobić za pomocą gadżetów ouzo :
$result = Arrays::flatten($multidimensional);
Zobacz: tutaj
Jeśli naprawdę nie lubisz rekurencji ... spróbuj zamiast tego zmienić :)
$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];
}
}
Uwaga: W tej prostej wersji nie obsługuje kluczy tablicy.
continue, będzie nieco szybszy.
Co powiesz na użycie generatora rekurencyjnego? 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;
}
}
}
Dla 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;
}
Ta wersja może wykonywać głębokie, płytkie lub określoną liczbę poziomów:
/**
* @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;
}
Ponieważ kod tutaj wygląda przerażająco. Oto funkcja, która przekształci również tablicę wielowymiarową w składnię zgodną z formą HTML, ale jest łatwiejsza do odczytania.
/**
* 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;
}
Można to osiągnąć za pomocą 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);
Przykład roboczy: - https://3v4l.org/FpIrG
Oto moje rozwiązanie, korzystając z referencji:
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;
}
?>
Każdy, kto szuka naprawdę czystego rozwiązania tego problemu; oto opcja:
$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) ) ;
Wydruki
0 0 0 0 0 0 derp 0 0 0 0 0 0 0 0 0
Wystarczy opublikować jakieś inne rozwiązanie)
function flatMultidimensionalArray(array &$_arr): array
{
$result = [];
\array_walk_recursive($_arr, static function (&$value, &$key) use (&$result) {
$result[$key] = $value;
});
return $result;
}
Jeśli chcesz zachować swoje klucze, to jest rozwiązanie.
function reduce(array $array) {
$return = array();
array_walk_recursive($array, function($value, $key) use (&$return) { $return[$key] = $value; });
return $return;
}
Niestety generuje tylko końcowe zagnieżdżone tablice, bez środkowych klawiszy. W poniższym przykładzie:
$array = array(
'sweet' => array(
'a' => 'apple',
'b' => 'banana'),
'sour' => 'lemon');
print_r(flatten($fruits));
Dane wyjściowe to:
Array
(
[a] => apple
[b] => banana
[sour] => lemon
)
Musiałem reprezentować wielowymiarową tablicę PHP w formacie wejściowym 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"
}
$var['a']['b']['c'][0] = 'a'; ....
Jeśli masz tablicę obiektów i chcesz spłaszczyć ją za pomocą węzła, po prostu użyj tej funkcji:
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;
}