331

PHPで、再帰や参照を使用せずに(2/多)次元配列を平坦化することは可能ですか?

私は値にのみ興味があるので、キーは無視できます。array_map()との行で考えていarray_values()ます。

4

31 に答える 31

339

PHP 5.3の時点で、最短の解決策array_walk_recursive()は新しいクロージャー構文のようです:

function flatten(array $array) {
    $return = array();
    array_walk_recursive($array, function($a) use (&$return) { $return[] = $a; });
    return $return;
}
于 2009-08-24T01:59:35.683 に答える
322

標準 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 
于 2009-08-24T02:39:06.863 に答える
178

array_mergePHP 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);
于 2017-10-21T09:41:08.960 に答える
98

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

于 2013-02-20T04:38:36.750 に答える
27

(要求されたように) 再帰なしで平坦化するには、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私はこれまでのところ、良いアイデアだと思うスタックを実装することはできませんでした。

于 2011-10-22T10:55:09.247 に答える
23

これは折り畳みであることを指摘したいと思ったので、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 の行に沿ってコンビネーター関数が求められます。

于 2013-07-17T09:05:17.133 に答える
18

再帰を使用します。うまくいけば、それがどれほど複雑ではないかがわかれば、それがどれほど複雑ではないかがわかると、再帰への恐れが消えるでしょう。

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>
于 2009-08-24T01:37:14.580 に答える
12

2 次元配列のみを平坦化します。

$arr = [1, 2, [3, 4]];
$arr = array_reduce($arr, function ($a, $b) {
     return array_merge($a, (array) $b);
}, []);

// Result: [1, 2, 3, 4]
于 2016-11-23T13:21:20.273 に答える
7

このソリューションは非再帰的です。要素の順序が多少異なることに注意してください。

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;
}
于 2009-08-24T00:09:21.657 に答える
3

次の単純な関数を試してください。

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',
)
于 2015-11-02T18:41:27.110 に答える
3

あなたはouzoグッズでそれを行うことができます:

 $result = Arrays::flatten($multidimensional);

参照:こちら

于 2015-01-09T14:46:10.297 に答える
2

キーも保管したい場合は、それが解決策です。

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
)
于 2020-05-02T10:00:22.723 に答える
2

秘訣は、参照によってソース配列と宛先配列の両方を渡すことです。

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);
于 2012-11-04T19:31:33.170 に答える
0

このバージョンでは、深い、浅い、または特定の数のレベルを実行できます。

/**
 * @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;
}
于 2013-03-07T08:41:38.353 に答える
0

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;
}
于 2012-07-04T15:19:22.910 に答える
-1

これは、参照を使用した私のソリューションです。

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>";
于 2015-09-19T13:33:54.440 に答える
-1

オブジェクトの配列があり、ノードでフラット化したい場合は、次の関数を使用してください。

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;
}
于 2013-10-01T07:25:09.540 に答える