Apakah mungkin, dalam PHP, untuk meratakan array dimensi (bi / multi) tanpa menggunakan rekursi atau referensi?
Saya hanya tertarik pada nilai-nilai sehingga kunci dapat diabaikan, saya berpikir dalam garis array_map()
dan array_values()
.
Apakah mungkin, dalam PHP, untuk meratakan array dimensi (bi / multi) tanpa menggunakan rekursi atau referensi?
Saya hanya tertarik pada nilai-nilai sehingga kunci dapat diabaikan, saya berpikir dalam garis array_map()
dan array_values()
.
Jawaban:
Anda dapat menggunakan Perpustakaan PHP Standar (SPL) untuk "menyembunyikan" rekursi.
$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, " ";
}
cetakan
1 2 3 4 5 6 7 8 9
iterator_to_array($it, false)
menghindari perlunya foreach.
function flatten($arr){ $it = new RecursiveIteratorIterator(new RecursiveArrayIterator($arr)); return iterator_to_array($it, true); }
Semoga ini bisa membantu orang lain.
Pada PHP 5.3 solusi terpendek tampaknya adalah array_walk_recursive()
dengan sintaks penutupan baru:
function flatten(array $array) {
$return = array();
array_walk_recursive($array, function($a) use (&$return) { $return[] = $a; });
return $return;
}
use
sintaks untuk membuatnya bekerja array_walk_recursive
karena tidak akan menerima $userdata
parameter opsional dengan referensi
Solusi untuk array 2 dimensi
Silakan coba ini:
$array = your array
$result = call_user_func_array('array_merge', $array);
echo "<pre>";
print_r($result);
EDIT: 21-Aug-13
Inilah solusi yang berfungsi untuk array multi dimensi:
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);
Ref: http://php.net/manual/en/function.call-user-func-array.php
call_user_func_array('array_merge', [])
(perhatikan array kosong) mengembalikan nol dan memicu kesalahan peringatan php. Ini adalah solusi yang apik jika Anda tahu fakta bahwa array Anda tidak akan kosong, tapi itu bukan asumsi umum yang bisa dibuat banyak orang.
$result = $array ?call_user_func_array('array_merge', $array) : [];
Dalam PHP 5.6 dan di atasnya Anda dapat meratakan array dua dimensi dengan array_merge
setelah membongkar array luar dengan ...
operator. Kode ini sederhana dan jelas.
array_merge(...$a);
Ini bekerja dengan kumpulan array asosiatif juga.
$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
)
Tapi itu tidak berfungsi ketika array luar memiliki kunci non numerik. Dalam hal ini, Anda harus menelepon array_values
dulu.
$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
)
Pembaruan: Berdasarkan komentar oleh @MohamedGharib
Ini akan menimbulkan kesalahan jika array luar kosong, karena array_merge
akan dipanggil dengan argumen nol. Ini bisa dihindari dengan menambahkan array kosong sebagai argumen pertama.
array_merge([], ...$a);
array_merge([], ...$a);
Untuk meratakan tanpa rekursi (seperti yang Anda minta), Anda dapat menggunakan tumpukan . Secara alami Anda dapat menempatkan ini ke dalam fungsi itu sendiri seperti array_flatten
. Berikut ini adalah versi yang berfungsi tanpa kunci :.
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;
}
Elemen diproses dalam urutannya. Karena subelemen akan dipindahkan di atas tumpukan, mereka akan diproses berikutnya.
Dimungkinkan juga untuk memasukkan kunci, tetapi Anda memerlukan strategi berbeda untuk menangani tumpukan. Itu diperlukan karena Anda perlu menangani kemungkinan kunci duplikat di sub-array. Jawaban serupa dalam pertanyaan terkait: PHP Berjalan melalui array multidimensi sambil mempertahankan kunci
Saya tidak terlalu yakin, tetapi saya telah menguji ini di masa lalu: RecurisiveIterator
Menggunakan rekursi, jadi itu tergantung pada apa yang Anda butuhkan. Seharusnya dimungkinkan untuk membuat iterator rekursif berdasarkan tumpukan juga:
foreach(new FlatRecursiveArrayIterator($array) as $key => $value)
{
echo "** ($key) $value\n";
}
Saya tidak sampai sejauh ini, untuk mengimplementasikan stack berdasarkan RecursiveIterator
yang menurut saya adalah ide yang bagus.
if(!empty($value)){$flat[] = $value}
di dalam pernyataan lain untuk mencegah kosong yang ditambahkan ke array hasil. Fungsi luar biasa!
Langsung dan Satu-liner jawaban.
function flatten_array(array $array)
{
return iterator_to_array(
new \RecursiveIteratorIterator(new \RecursiveArrayIterator($array)));
}
Pemakaian:
$array = [
'name' => 'Allen Linatoc',
'profile' => [
'age' => 21,
'favourite_games' => [ 'Call of Duty', 'Titanfall', 'Far Cry' ]
]
];
print_r( flatten_array($array) );
Output (dalam PsySH):
Array
(
[name] => Allen Linatoc
[age] => 21
[0] => Call of Duty
[1] => Titanfall
[2] => Far Cry
)
Sekarang terserah Anda sekarang bagaimana Anda akan menangani kunci. Bersulang
EDIT (2017-03-01)
Mengutip kekhawatiran / masalah Nigel Alderton :
Hanya untuk memperjelas, ini mempertahankan kunci (bahkan angka) sehingga nilai yang memiliki kunci yang sama hilang. Misalnya
$array = ['a',['b','c']]
menjadiArray ([0] => b, [1] => c )
. The'a'
hilang karena'b'
juga memiliki kunci0
Mengutip jawaban Svish :
Tambahkan saja false sebagai parameter kedua
($use_keys)
ke panggilan iterator_to_array
$array = ['a',['b','c']]
menjadi Array ([0] => b, [1] => c )
. The 'a'
hilang karena 'b'
juga memiliki kunci 0
.
false
sebagai parameter kedua ( $use_keys
) ke iterator_to_array
panggilan.
Gunakan rekursi. Mudah-mudahan setelah melihat betapa tidak rumitnya hal itu, ketakutan Anda akan rekursi akan hilang begitu Anda melihat betapa tidak rumitnya itu.
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>';
Keluaran:
<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>
Hanya berpikir saya akan menunjukkan bahwa ini adalah lipatan, jadi array_reduce dapat digunakan:
array_reduce($my_array, 'array_merge', array());
EDIT: Perhatikan bahwa ini dapat disusun untuk meratakan sejumlah level. Kita dapat melakukan ini dalam beberapa cara:
// 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))));
Tentu saja, kita juga bisa menggunakan loop tetapi pertanyaannya meminta fungsi kombinator di sepanjang baris array_map atau array_values.
fold
membuatnya menjadi 4 level, atau fold . fold
untuk mendapatkan 3 level, atau fold . fold . fold
untuk mendapatkan 2 level, dll. Ini juga mencegah bug disembunyikan; misalnya. jika saya ingin meratakan array 5D tapi saya diberi array 4D, kesalahan akan langsung terpicu.
$concat
, saya pikir Anda harus menyebutnya saja $flatten
. array_merge
adalah setara dengan php dari concat. Saya mencoba untuk array_concat
ditambahkan sebagai alias untuk array_merge
.
Solusi ini non-rekursif. Perhatikan bahwa urutan elemen akan agak tercampur.
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;
}
shifting
nilai dari array dan menambahkannya lagi sampai akhir tidak masuk akal. Saya kira Anda ingin array_merge()
bukan?
Saya percaya ini adalah solusi terbersih tanpa menggunakan mutasi atau kelas yang tidak dikenal.
<?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));
Coba fungsi sederhana berikut ini:
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;
}
Jadi dari ini:
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',
),
),
),
),
),
),
)
Anda mendapatkan:
array (
'first_name' => 'First name',
'last_name' => 'Last name',
'thoroughfare' => 'Address 1',
'premise' => 'Address 2',
'locality' => 'Town/City',
'administrative_area' => 'County',
'postal_code' => 'Postcode',
)
Triknya adalah melewati array sumber dan tujuan dengan referensi.
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));
}
);
Anda dapat melakukannya dengan barang ouzo :
$result = Arrays::flatten($multidimensional);
Lihat: Di Sini
Jika Anda benar-benar tidak menyukai rekursi ... coba ganti saja :)
$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];
}
}
Catatan: Dalam versi sederhana ini, ini tidak mendukung kunci array.
continue
, itu agak lebih cepat.
Bagaimana dengan menggunakan generator rekursif? 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;
}
}
}
Untuk 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;
}
Versi ini dapat melakukan level yang dalam, dangkal, atau jumlah tertentu:
/**
* @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;
}
Karena kode di sini terlihat menyeramkan. Berikut adalah fungsi yang juga akan mengubah array multidimensi menjadi sintaks yang kompatibel dengan bentuk html, tetapi lebih mudah dibaca.
/**
* 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;
}
Ini bisa dicapai dengan menggunakan 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);
Contoh kerja: - https://3v4l.org/FpIrG
Ini solusi saya, menggunakan referensi:
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;
}
?>
Inilah pendekatan sederhana:
$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);
Siapa pun yang mencari solusi yang sangat bersih untuk ini; inilah pilihannya:
$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) ) ;
Cetakan
0 0 0 0 0 0 derp 0 0 0 0 0 0 0 0 0
Hanya memposting solusi lain)
function flatMultidimensionalArray(array &$_arr): array
{
$result = [];
\array_walk_recursive($_arr, static function (&$value, &$key) use (&$result) {
$result[$key] = $value;
});
return $result;
}
Jika Anda ingin menyimpan juga kunci Anda, itu adalah solusi.
function reduce(array $array) {
$return = array();
array_walk_recursive($array, function($value, $key) use (&$return) { $return[$key] = $value; });
return $return;
}
Sayangnya ini hanya menghasilkan array bersarang akhir, tanpa tombol tengah. Jadi untuk contoh berikut:
$array = array(
'sweet' => array(
'a' => 'apple',
'b' => 'banana'),
'sour' => 'lemon');
print_r(flatten($fruits));
Output adalah:
Array
(
[a] => apple
[b] => banana
[sour] => lemon
)
Saya perlu mewakili array multidimensi PHP dalam format input 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'; ...
.
Jika Anda memiliki array objek dan ingin meratakannya dengan sebuah simpul, cukup gunakan fungsi ini:
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;
}