PHP'de özyineleme veya referans kullanmadan bir (bi / multi) boyutlu diziyi düzleştirmek mümkün mü?
Sadece tuşları göz ardı edilebilir, böylece ben hatlarında düşünüyorum değerlere ilgilenen kulüpler array_map()
ve array_values()
.
PHP'de özyineleme veya referans kullanmadan bir (bi / multi) boyutlu diziyi düzleştirmek mümkün mü?
Sadece tuşları göz ardı edilebilir, böylece ben hatlarında düşünüyorum değerlere ilgilenen kulüpler array_map()
ve array_values()
.
Yanıtlar:
Sen kullanabilirsiniz Standart PHP Kütüphanesi (SPL) "gizlemek" özyinelemeye.
$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, " ";
}
baskılar
1 2 3 4 5 6 7 8 9
iterator_to_array($it, false)
foreach ihtiyacını ortadan kaldırır.
function flatten($arr){ $it = new RecursiveIteratorIterator(new RecursiveArrayIterator($arr)); return iterator_to_array($it, true); }
Umarım bu başkalarına yardımcı olur.
İtibariyle PHP 5.3 en kısa çözüm gibi görünüyor array_walk_recursive()
yeni kapanışları söz dizimi ile:
function flatten(array $array) {
$return = array();
array_walk_recursive($array, function($a) use (&$return) { $return[] = $a; });
return $return;
}
use
bu çalışmayı yapmak için sözdizimine ihtiyacınız var, array_walk_recursive
çünkü isteğe bağlı $userdata
parametreyi referans olarak kabul
2 boyutlu dizi için çözüm
Lütfen şunu deneyin:
$array = your array
$result = call_user_func_array('array_merge', $array);
echo "<pre>";
print_r($result);
DÜZENLEME: 21-Ağu-13
İşte çok boyutlu dizi için çalışan çözüm:
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/tr/function.call-user-func-array.php
call_user_func_array('array_merge', [])
(boş diziye dikkat edin) null değerini döndürür ve bir php uyarı hatasını tetikler. Dizinizin boş kalmayacağı gerçeğini biliyorsanız, kaygan bir çözümdür, ancak bu birçok kişinin yapabileceği ortak bir varsayım değildir.
$result = $array ?call_user_func_array('array_merge', $array) : [];
PHP 5.6 ve üzeri array_merge
sürümlerde, dış diziyi ...
operatörle birlikte açtıktan sonra iki boyutlu dizileri düzleştirebilirsiniz . Kod basit ve anlaşılır.
array_merge(...$a);
Bu ilişkisel dizilerin toplanmasıyla da çalışır.
$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
)
Ancak, dış dizide sayısal olmayan anahtarlar olduğunda çalışmaz. Bu durumda, array_values
önce aramanız gerekir .
$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
)
Güncelleme: @MohamedGharib tarafından yapılan yoruma göre
Dış dizi boşsa bu hata verir, çünkü array_merge
sıfır argümanıyla çağrılır. İlk argüman olarak boş bir dizi eklenerek önlenebilir.
array_merge([], ...$a);
array_merge([], ...$a);
Özyinelemesiz (istediğiniz gibi) düzleştirmek için bir yığın kullanabilirsiniz . Doğal olarak bunu kendi işlevine sokabilirsiniz array_flatten
. Aşağıda anahtarsız çalışan bir sürüm bulunmaktadır:
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;
}
Öğeler sıralarına göre işlenir. Alt öğeler yığının üstüne taşınacağından, bir sonraki işleme alınacaktır.
Anahtarları da hesaba katmak mümkündür, ancak yığını işlemek için farklı bir stratejiye ihtiyacınız olacaktır. Alt dizilerdeki olası yinelenen anahtarlarla uğraşmanız gerektiğinden bu gereklidir. İlgili bir soruya benzer bir cevap: PHP Anahtarları korurken çok boyutlu dizi boyunca yürüyün
Özellikle emin değilim, ama II bunu geçmişte test etmişti: RecurisiveIterator
Özyineleme kullanıyor, bu yüzden gerçekten neye ihtiyacınız olduğuna bağlı. Yığınlara dayalı bir özyinelemeli yineleyici oluşturmak mümkün olmalıdır:
foreach(new FlatRecursiveArrayIterator($array) as $key => $value)
{
echo "** ($key) $value\n";
}
Şimdiye kadar, RecursiveIterator
güzel bir fikir olduğunu düşündüğüm yığını uygulamaya koymadım.
if(!empty($value)){$flat[] = $value}
Sonuç dizisine boş eklenmesini önlemek için else deyimi içinde eklemek zorunda kaldı . Müthiş fonksiyon!
Basit ve Tek astarlı cevap.
function flatten_array(array $array)
{
return iterator_to_array(
new \RecursiveIteratorIterator(new \RecursiveArrayIterator($array)));
}
Kullanımı:
$array = [
'name' => 'Allen Linatoc',
'profile' => [
'age' => 21,
'favourite_games' => [ 'Call of Duty', 'Titanfall', 'Far Cry' ]
]
];
print_r( flatten_array($array) );
Çıktı (PsySH cinsinden):
Array
(
[name] => Allen Linatoc
[age] => 21
[0] => Call of Duty
[1] => Titanfall
[2] => Far Cry
)
Şimdi tuşları nasıl kullanacağınız size kalmış. Şerefe
DÜZENLE (2017-03-01)
Nigel Alderton'un endişesi / sorunundan alıntı :
Açıklığa kavuşturmak için, bu anahtarları (sayısal olanlar bile) korur, böylece aynı anahtara sahip değerler kaybolur. Örneğin
$array = ['a',['b','c']]
olurArray ([0] => b, [1] => c )
.'a'
Çünkü kaybolur'b'
da bir anahtara sahip0
Svish'in cevabından alıntı :
Sadece ikinci parametre olarak false eklemek
($use_keys)
için iterator_to_array çağrısı
$array = ['a',['b','c']]
olur Array ([0] => b, [1] => c )
. 'a'
Çünkü kaybolur 'b'
da bir anahtar vardır 0
.
false
ikinci parametre ( $use_keys
) olarak eklemeniz yeterlidir iterator_to_array
.
Özyineleme kullanır. Umarım ne kadar karmaşık olmadığını gördükten sonra, ne kadar karmaşık olmadığını gördüğünüzde, tekrarlama korkunuz dağılacaktır.
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>';
Çıktı:
<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>
Sadece bu bir kat olduğunu işaret düşündüm, böylece array_reduce kullanılabilir:
array_reduce($my_array, 'array_merge', array());
EDIT: Bunun herhangi bir sayıda seviyeyi düzleştirmek için oluşturulabileceğini unutmayın. Bunu birkaç şekilde yapabiliriz:
// 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))));
Tabii ki, döngüler de kullanabiliriz, ancak soru dizi_map veya dizi_değerleri çizgileri boyunca bir birleştirici işlevi ister.
fold
4 seviyeye, ya fold . fold
da 3 seviyeleri almak için, ya fold . fold . fold
da 2 seviyeleri almak için, vb. Bu da hataların gizlenmesi önler; Örneğin. bir 5D dizisini düzleştirmek istiyorum ama bir 4D dizisi verildi, hata hemen tetikleyecektir.
$concat
düşünüyorum, sadece çağırmanız gerektiğini düşünüyorum $flatten
. array_merge
concat'ın php eşdeğeri. Denedim almak array_concat
için bir takma ad olarak eklendi array_merge
.
Bu çözüm özyinelemeli değildir. Elemanların sırasının biraz karışık olacağını unutmayın.
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
dizinin dışındaki değer ve sonuna sonuna eklemek pek mantıklı değil. Sanırım array_merge()
onun yerine istedin mi?
Bunun herhangi bir mutasyon veya yabancı sınıf kullanmadan en temiz çözüm olduğuna inanıyorum.
<?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));
Aşağıdaki basit işlevi deneyin:
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;
}
Yani bundan:
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',
),
),
),
),
),
),
)
olsun:
array (
'first_name' => 'First name',
'last_name' => 'Last name',
'thoroughfare' => 'Address 1',
'premise' => 'Address 2',
'locality' => 'Town/City',
'administrative_area' => 'County',
'postal_code' => 'Postcode',
)
Hile, hem kaynak hem de hedef dizileri referans olarak geçiyor.
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));
}
);
Gerçekten bir özyinelemeyi sevmiyorsanız ... bunun yerine kaydırmayı deneyin :)
$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];
}
}
Not: Bu basit sürümde, dizi anahtarları desteklenmez.
continue
, biraz daha hızlıdır.
Özyinelemeli bir jeneratör kullanmaya ne dersiniz? 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 için
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;
}
Bu sürüm derin, sığ veya belirli sayıda seviye yapabilir:
/**
* @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;
}
Kod Çünkü burada korkunç görünüyor. Ayrıca çok boyutlu bir diziyi html formuyla uyumlu sözdizimine dönüştürecek, ancak okunması daha kolay olan bir işlev.
/**
* 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;
}
Bunu kullanarak elde edilebilir 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);
Çalışma örneği: - https://3v4l.org/FpIrG
Bu benim referans, bir referans kullanarak:
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;
}
?>
Buna gerçekten temiz bir çözüm arayan herkes; İşte bir seçenek:
$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) ) ;
Baskılar
0 0 0 0 0 0 derp 0 0 0 0 0 0 0 0 0
Sadece başka bir çözüm yayınlamak)
function flatMultidimensionalArray(array &$_arr): array
{
$result = [];
\array_walk_recursive($_arr, static function (&$value, &$key) use (&$result) {
$result[$key] = $value;
});
return $result;
}
Anahtarlarınızı da tutmak istiyorsanız çözüm budur.
function reduce(array $array) {
$return = array();
array_walk_recursive($array, function($value, $key) use (&$return) { $return[$key] = $value; });
return $return;
}
Ne yazık ki, orta anahtarlar olmadan yalnızca son iç içe diziler çıkarır. Aşağıdaki örnek için:
$array = array(
'sweet' => array(
'a' => 'apple',
'b' => 'banana'),
'sour' => 'lemon');
print_r(flatten($fruits));
Çıktı:
Array
(
[a] => apple
[b] => banana
[sour] => lemon
)
PHP çok boyutlu diziyi HTML giriş biçiminde temsil etmem gerekiyordu.
$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'; ...
.
Bir dizi nesneniz varsa ve bunu bir düğümle düzleştirmek istiyorsanız, şu işlevi kullanın:
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;
}