PHPで、再帰や参照を使用せずに(2/多)次元配列を平坦化することは可能ですか?
私は値にのみ興味があるので、キーは無視できます。array_map()
との行で考えていarray_values()
ます。
PHPで、再帰や参照を使用せずに(2/多)次元配列を平坦化することは可能ですか?
私は値にのみ興味があるので、キーは無視できます。array_map()
との行で考えていarray_values()
ます。
PHP 5.3の時点で、最短の解決策array_walk_recursive()
は新しいクロージャー構文のようです:
function flatten(array $array) {
$return = array();
array_walk_recursive($array, function($a) use (&$return) { $return[] = $a; });
return $return;
}
標準 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
array_merge
PHP 5.6以降では、外側の配列を...
演算子で展開した後、 2 次元配列を平坦化できます。コードは単純明快です。
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
)
PHP 8.0 以下では、外側の配列に数値以外のキーがある場合、配列のアンパックは機能しません。文字列キーを使用した配列のアンパックのサポートは、PHP 8.1 から利用できます。8.0 以下をサポートするには、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
)
更新: @MohamedGharib のコメントに基づく
引数なしで呼び出されるため、外側の配列が空の場合、これはエラーをスローしarray_merge
ます。最初の引数として空の配列を追加することで回避できます。
array_merge([], ...$a);
2次元配列のソリューション
これを試してください:
$array = your array
$result = call_user_func_array('array_merge', $array);
echo "<pre>";
print_r($result);
編集:2013年8月21日
多次元配列で機能するソリューションは次のとおりです。
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
(要求されたように) 再帰なしで平坦化するには、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
{
array_unshift($stack, ...$value);
}
else // a value to take
{
$flat[] = $value;
}
}
return $flat;
}
要素はその順序で処理されます。サブエレメントはスタックの上に移動されるため、次に処理されます。
キーも考慮に入れることができますが、スタックを処理するには別の戦略が必要になります。サブ配列内の重複キーの可能性に対処する必要があるため、これが必要です。関連する質問の同様の回答: PHP Walk through multidimensional array while preserving keys
具体的にはわかりませんが、過去にこれをテストしたRecurisiveIterator
ことがあります。再帰を使用するため、本当に必要なものに依存します。スタックに基づいて再帰イテレータも作成できるはずです。
foreach(new FlatRecursiveArrayIterator($array) as $key => $value)
{
echo "** ($key) $value\n";
}
RecursiveIterator
私はこれまでのところ、良いアイデアだと思うスタックを実装することはできませんでした。
これは折り畳みであることを指摘したいと思ったので、array_reduce を使用できます。
array_reduce($my_array, 'array_merge', array());
編集:これは、任意の数のレベルを平坦化するように構成できることに注意してください。これにはいくつかの方法があります。
// 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'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>
2 次元配列のみを平坦化します。
$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;
}
だからこれから:
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 $array) {
$return = array();
array_walk_recursive($array, function($value, $key) use (&$return) { $return[$key] = $value; });
return $return;
}
残念ながら、中間キーなしで、最終的なネストされた配列のみを出力します。したがって、次の例では:
$array = array(
'sweet' => array(
'a' => 'apple',
'b' => 'banana'),
'sour' => 'lemon');
print_r(flatten($fruits));
出力は次のとおりです。
Array
(
[a] => apple
[b] => banana
[sour] => lemon
)
秘訣は、参照によってソース配列と宛先配列の両方を渡すことです。
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);
このバージョンでは、深い、浅い、または特定の数のレベルを実行できます。
/**
* @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;
}
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;
}
これは、参照を使用した私のソリューションです。
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>";
オブジェクトの配列があり、ノードでフラット化したい場合は、次の関数を使用してください。
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;
}