PHPで多次元配列を単純な配列に「フラット化」するにはどうすればよいですか?


83

おそらく初心者の質問ですが、私はすでに長い間ドキュメントを調べていて、解決策を見つけることができません。次元ごとに爆縮を使用し、それらの文字列を元に戻してstr_split新しい単純な配列を作成できると思いました。ただし、結合パターンが値にも含まれていないかどうかはわかりませんstr_split。そのため、実行後に元の値が破損する可能性があります。

combine($array1, $array2)多次元配列内の配列のようなものはありますか?


解決のために、このリンクをご確認くださいstackoverflow.com/questions/14951811/...
Prasanth Bendra

1
おそらくより良い答えを持つ別の良い参照質問多次元配列を平坦化する方法?
hakre 2015

回答:


42

使用する array_walk_recursive

<?php

$aNonFlat = array(
    1,
    2,
    array(
        3,
        4,
        5,
        array(
            6,
            7
        ),
        8,
        9,
    ),
    10,
    11
);

$objTmp = (object) array('aFlat' => array());

array_walk_recursive($aNonFlat, create_function('&$v, $k, &$t', '$t->aFlat[] = $v;'), $objTmp);

var_dump($objTmp->aFlat);

/*
array(11) {
  [0]=>
  int(1)
  [1]=>
  int(2)
  [2]=>
  int(3)
  [3]=>
  int(4)
  [4]=>
  int(5)
  [5]=>
  int(6)
  [6]=>
  int(7)
  [7]=>
  int(8)
  [8]=>
  int(9)
  [9]=>
  int(10)
  [10]=>
  int(11)
}
*/

?>

PHP 5.5.9-1ubuntu4.24(cli)でテスト済み(ビルド:2018年3月16日12:32:06)


参照によって(減価償却された)呼び出し時間パスを使用しない限り、これが機能しない理由を誰かが知っていますか?つまり、array_walk_recursive($ array、create_function( '&$ v、$ k、&$ t'、 '$ t [] = $ v;')、&$ flattened); 関数定義は、参照渡しとして正しく定義されています。しかし、通話中に参照を渡さない限り機能しません。
jskulski 2010

2
@jskilskiオブジェクト($objTmpこの例では)は参照によって自動的に渡されます。配列はそうではありません。の代わりに無名関数(php.net/manual/en/functions.anonymous.php)を使用してみてくださいcreate_function
dave1010 2011

1
- array_walk_recursiveのバグによるPHP 5.3.3でこのdoesntの仕事bugs.php.net/bug.php?id=52719
crazyphoton

1
@crazyphotonキンクも言うThis bug has been fixed in SVN.
Luc M

10
なぜこの回答は使用について言及しているのarray_values()ですか?答えに関係するその機能の使用はまったく見当たりません。
thomasrutter 2016

134
$array  = your array

$result = call_user_func_array('array_merge', $array);

echo "<pre>";
print_r($result);

参照:http//php.net/manual/en/function.call-user-func-array.php

これが別の解決策です(多次元配列で動作します):

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);

この答えは、受け入れられた答えよりもはるかに高速です。
RohamRafii19年

8
:PHP5.3ので、あなたは今スプラット演算子を使用することができます$result = array_merge(...$array); php.net/manual/en/...
Redzarf

1
最初の答えは、多次元配列では機能しません。3v4l.org/tY8vD
dearsina

55

これは1行で、非常に使いやすいです。

$result = array();
array_walk_recursive($original_array,function($v) use (&$result){ $result[] = $v; });

匿名関数/クロージャの内部では、非常に理解しやすいです。$vの値です$original_array


5
これは、2レベルの配列で私のために働いた唯一のものです。
Ciprian Tepes 2017年

18
// $array = your multidimensional array

$flat_array = array();

foreach(new RecursiveIteratorIterator(new RecursiveArrayIterator($array)) as $k=>$v){

$flat_array[$k] = $v;

}

また、文書化されています:http//www.phpro.org/examples/Flatten-Array.html


2
注:プリミティブの配列にのみ使用してください。「RecursiveArrayIteratorはすべてのオブジェクトを子を持つものとして扱い、それらに再帰しようとします。」php.net/manual/en/class.recursivearrayiterator.php#106519
ReactiveRaven

@hakre:+1同意:iterator_to_array()この回答に追加すると、foreachループの必要性がなくなります。単純な1次関数である可能性があります。(やや長い1行ですが)
SDC 2012

2
これは古いですが、それでも便利ですが、$ kはカウンターなどの一意のものに置き換える必要があります。$ kを使用するだけで、内部配列の名前がメイン配列と同じである場合、要素が削除されます。
オースティンベスト

12

特に、深さが1レベルを超えない配列の配列がある場合(私が一般的だと思うユースケース)array_merge、splat演算子を使用する必要があります。

<?php

$notFlat = [[1,2],[3,4]];
$flat = array_merge(...$notFlat);
var_dump($flat);

出力:

array(4) {
  [0]=>
  int(1)
  [1]=>
  int(2)
  [2]=>
  int(3)
  [3]=>
  int(4)
}

splat演算子は、配列の配列をの引数として配列のリストに効果的に変更しarray_mergeます。


これは私にとって最良の答えのように見えます。これは、文字列キーでは動作しませんが、簡単にそうするように変更することができます:$flat = array_merge( array_keys( $notFlat ), ...array_values( $notFlat ) );
イアン・ダン

6

PHP 7では、ジェネレーターとジェネレーター委任(yield from)を使用して配列をフラット化できます。

function array_flatten_iterator (array $array) {
    foreach ($array as $value) {
        if (is_array($value)) {
            yield from array_flatten_iterator($value);
        } else {
            yield $value;
        }
    }
}

function array_flatten (array $array) {
    return iterator_to_array(array_flatten_iterator($array), false);
}

例:

$array = [
    1,
    2,
    [
        3,
        4,
        5,
        [
            6,
            7
        ],
        8,
        9,
    ],
    10,
    11,
];    

var_dump(array_flatten($array));

http://3v4l.org/RU30W


5
function flatten_array($array, $preserve_keys = 0, &$out = array()) {
    # Flatten a multidimensional array to one dimension, optionally preserving keys.
    #
    # $array - the array to flatten
    # $preserve_keys - 0 (default) to not preserve keys, 1 to preserve string keys only, 2 to preserve all keys
    # $out - internal use argument for recursion
    foreach($array as $key => $child)
        if(is_array($child))
            $out = flatten_array($child, $preserve_keys, $out);
        elseif($preserve_keys + is_string($key) > 1)
            $out[$key] = $child;
        else
            $out[] = $child;
    return $out;
}

申し訳ありませんが、多次元配列を適切に処理していないようです-デモ
アルバロゴンサレス

5

PHPのユーザーコメント(簡略化)からの別の方法とここ

function array_flatten_recursive($array) { 
   if (!$array) return false;
   $flat = array();
   $RII = new RecursiveIteratorIterator(new RecursiveArrayIterator($array));
   foreach ($RII as $value) $flat[] = $value;
   return $flat;
}

この方法の大きな利点は、平坦化中に必要になった場合に、再帰の深さを追跡することです。
これは出力します:

$array = array( 
    'A' => array('B' => array( 1, 2, 3)), 
    'C' => array(4, 5) 
); 
print_r(array_flatten_recursive($array)); 

#Returns: 
Array ( 
    [0] => 1 
    [1] => 2 
    [2] => 3 
    [3] => 4 
    [4] => 5 
)

3
注:プリミティブの配列にのみ使用してください。「RecursiveArrayIteratorはすべてのオブジェクトを子を持つものとして扱い、それらに再帰しようとします。」php.net/manual/en/class.recursivearrayiterator.php#106519
ReactiveRaven

4

非再帰的ソリューション(ただし、順序を破壊する):

function flatten($ar) {
    $toflat = array($ar);
    $res = array();

    while (($r = array_shift($toflat)) !== NULL) {
        foreach ($r as $v) {
            if (is_array($v)) {
                $toflat[] = $v;
            } else {
                $res[] = $v;
            }
        }
    }

    return $res;
}

4

PHP> = 5.3で、Luc Mの回答(最初の回答)に基づいて、次のようなクロージャを利用できます。

array_walk_recursive($aNonFlat, function(&$v, $k, &$t){$t->aFlat[] = $v;}, $objTmp);

create_function()を使用するときのように、関数のコードを引用符で囲む必要がないため、これが大好きです。


1
匿名関数を使用している場合は、このobjTempようなものではなく、キャプチャされたクロージャ変数を直接使用することをお勧めします
user102008 2012年

1
PHP5.3.3には、これをクラッシュさせるバグがあります-bugs.php.net/bug.php?id=52719
crazyphoton 2012

2

高階関数の使用(注:PHP 5.3で登場したインライン匿名関数を使用しています):

function array_flatten($array) {
    return array_reduce(
        $array,
        function($prev, $element) {
            if (!is_array($element))
                $prev[] = $element;
            else
                $prev = array_merge($prev, array_flatten($element));
            return $prev;
        },
        array()
    );
}

1

chaosによって送信された前のサンプル関数に基づく新しいアプローチ。これにより、マルチアレイで文字列キーが上書きされるバグが修正されます。

# Flatten a multidimensional array to one dimension, optionally preserving keys.
# $array - the array to flatten
# $preserve_keys - 0 (default) to not preserve keys, 1 to preserve string keys only, 2 to preserve all keys
# $out - internal use argument for recursion

function flatten_array($array, $preserve_keys = 2, &$out = array(), &$last_subarray_found) 
{
        foreach($array as $key => $child)
        {
            if(is_array($child))
            {
                $last_subarray_found = $key;
                $out = flatten_array($child, $preserve_keys, $out, $last_subarray_found);
            }
            elseif($preserve_keys + is_string($key) > 1)
            {
                if ($last_subarray_found)
                {
                    $sfinal_key_value = $last_subarray_found . "_" . $key;
                }
                else
                {
                    $sfinal_key_value = $key;
                }
                $out[$sfinal_key_value] = $child;
            }
            else
            {
                $out[] = $child;
            }
        }

        return $out;
}

Example:
$newarraytest = array();
$last_subarray_found = "";
$this->flatten_array($array, 2, $newarraytest, $last_subarray_found);

1
/*consider $mArray as multidimensional array and $sArray as single dimensional array
this code will ignore the parent array
*/

function flatten_array2($mArray) {
    $sArray = array();

    foreach ($mArray as $row) {
        if ( !(is_array($row)) ) {
            if($sArray[] = $row){
            }
        } else {
            $sArray = array_merge($sArray,flatten_array2($row));
        }
    }
    return $sArray;
}

1

あなたはこれを試すことができます:

function flat_an_array($a)
{
    foreach($a as $i)
    {
        if(is_array($i)) 
        {
            if($na) $na = array_merge($na,flat_an_array($i));
            else $na = flat_an_array($i);
        }
        else $na[] = $i;
    }
    return $na;
}

1

配列キーを失っても問題ない場合は、次のように、array_values()を利用するコールバックとして再帰的クロージャを使用して多次元配列をフラット化し、このコールバックがarray_walk()のパラメーターであることを確認します。

<?php  

$array = [1,2,3,[5,6,7]];
$nu_array = null;
$callback = function ( $item ) use(&$callback, &$nu_array) {
    if (!is_array($item)) {
    $nu_array[] = $item;
    }
    else
    if ( is_array( $item ) ) {
     foreach( array_values($item) as $v) {
         if ( !(is_array($v))) {
             $nu_array[] = $v;
         }
         else
         { 
             $callback( $v );
         continue;
         }    
     }
    }
};

array_walk($array, $callback);
print_r($nu_array);

前の例の1つの欠点は、単純化されたコールバックとともにarray_walk_recursive()を使用する次のソリューションよりもはるかに多くのコードを記述する必要があることです。

<?php  

$array = [1,2,3,[5,6,7]];

$nu_array = [];
array_walk_recursive($array, function ( $item ) use(&$nu_array )
                     {
                         $nu_array[] = $item;
                     }
);
print_r($nu_array);

ライブコードを見る

この例は前の例よりも好ましいようで、多次元配列から値を抽出する方法の詳細を隠しています。確かに、反復は発生しますが、再帰または制御構造のどちらを伴う場合でも、array.cを熟読することによってのみわかります。関数型プログラミングは、結果を得るという細かな点ではなく、入力と出力に焦点を合わせているので、舞台裏での反復がどのように発生するか、つまり、遠近法の雇用主がそのような質問をするまでは、確かに無関心のままです。


1

マルチレベル配列を1つに変換する簡単な方法を見つけました。配列をURL文字列に変換する関数「http_build_query」を使用します。次に、文字列をexplodeで分割し、値をデコードします。

これがサンプルです。

$converted = http_build_query($data);
$rows = explode('&', $converted);
$output = array();
foreach($rows AS $k => $v){
   list($kk, $vv) = explode('=', $v);
   $output[ urldecode($kk) ] =  urldecode($vv);
}
return $output;

1

ネクロバンピングして申し訳ありませんが、提供された回答のいずれも、「多次元配列をフラット化する」と直感的に理解したことを実行しませんでした。つまり、この場合:

[
  'a' => [
    'b' => 'value',
  ]
]

提供されたすべてのソリューションは、それを ['value']に、それはキーと深さに関する情報を失い、さらに別の「b」キーがどこかにある場合、それらを上書きします。

私はこのような結果を得たかった:

[
  'a_b' => 'value',
]

array_walk_recursive 現在再帰しているキーに関する情報を渡さないので、単純な再帰で渡しました。

function flatten($array, $prefix = '') {
    $return = [];
    foreach ($array as $key => $value) {
        if (is_array($value)) {
            $return = array_merge($return, flatten($value, $prefix . $key . '_'));
        } else {
            $return[$prefix . $key] = $value;
        }
    }
    return $return;
}

$ prefixと '_'区切り文字をお好みに合わせて変更します。

ここの遊び場:https//3v4l.org/0B8hf



0

単純なアプローチ..再帰を介してそれを参照してください..

<?php

function flatten_array($simple){
static $outputs=array();
foreach ( $simple as $value)
{
if(is_array($value)){
    flatten_array($value);
}
else{
    $outputs[]=$value;
}

}
return $outputs;
}

$eg=['s'=>['p','n'=>['t']]];
$out=flatten_array($eg);
print_r($out);

?>

staticこのタスクに潜在的に悪い考えを使用しているのはなぜですか?意図しないデータの保持。彼らがこの振る舞いを知らない/期待しないならば、これは確かにプログラマーを驚かせるでしょう。 このデモを見てください
mickmackusa 2018

あなたは「コードのみ」の回答を投稿しました。これらはOPや将来の研究者を教育できないため、StackOverflowでは価値が低くなっています。あなたの答えがどのように機能するか、そしてなぜそれが以前の答えよりも良い考えであると思うのかを含めることによって、この答えを改善するために少し時間を取ってください。
mickmackusa 2018

0

誰かがこれが役に立つと思うかもしれません、私はある次元で配列を平坦化するのに問題がありました、私はそれを最後の次元と呼ぶでしょう、例えば私が次のような配列を持っているなら。

array (
  'germany' => 
  array (
    'cars' => 
    array (
      'bmw' => 
      array (
        0 => 'm4',
        1 => 'x3',
        2 => 'x8',
      ),
    ),
  ),
  'france' => 
  array (
    'cars' => 
    array (
      'peugeot' => 
      array (
        0 => '206',
        1 => '3008',
        2 => '5008',
      ),
    ),
  ),
)

または:

array (
  'earth' => 
  array (
    'germany' => 
    array (
      'cars' => 
      array (
        'bmw' => 
        array (
          0 => 'm4',
          1 => 'x3',
          2 => 'x8',
        ),
      ),
    ),
  ),
  'mars' => 
  array (
    'france' => 
    array (
      'cars' => 
      array (
        'peugeot' => 
        array (
          0 => '206',
          1 => '3008',
          2 => '5008',
        ),
      ),
    ),
  ),
)

以下のメソッドを呼び出すと、これらの配列の両方で結果が得られます。

array (
  0 => 
  array (
    0 => 'm4',
    1 => 'x3',
    2 => 'x8',
  ),
  1 => 
  array (
    0 => '206',
    1 => '3008',
    2 => '5008',
  ),
)

したがって、同じままである必要がある最後の配列次元にフラット化しています。以下のメソッドは、実際には任意の種類のレベルで停止するようにリファクタリングできます。

function flattenAggregatedArray($aggregatedArray) {
    $final = $lvls = [];
    $counter = 1;
    $lvls[$counter] = $aggregatedArray;


    $elem = current($aggregatedArray);

    while ($elem){
        while(is_array($elem)){
            $counter++;
            $lvls[$counter] = $elem;
            $elem =  current($elem);
        }

        $final[] = $lvls[$counter];
        $elem = next($lvls[--$counter]);
        while ( $elem  == null){
            if (isset($lvls[$counter-1])){
                $elem = next($lvls[--$counter]);
            }
            else{
                return $final;
            }
        }
    }
}

-1

1つの特定のキーの値だけに関心がある場合は、このアプローチが役立つ場合があります。

function valuelist($array, $array_column) {
    $return = array();
    foreach($array AS $row){
        $return[]=$row[$array_column];
    };
    return $return;
};

例:

与えられた$ get_role_action =

array(3) {
  [0]=>
  array(2) {
    ["ACTION_CD"]=>
    string(12) "ADD_DOCUMENT"
    ["ACTION_REASON"]=>
    NULL
  }
  [1]=>
  array(2) {
    ["ACTION_CD"]=>
    string(13) "LINK_DOCUMENT"
    ["ACTION_REASON"]=>
    NULL
  }
  [2]=>
  array(2) {
    ["ACTION_CD"]=>
    string(15) "UNLINK_DOCUMENT"
    ["ACTION_REASON"]=>
    NULL
  }
}

$variables['role_action_list']=valuelist($get_role_action, 'ACTION_CD');結果よりも:

$variables["role_action_list"]=>
  array(3) {
    [0]=>
    string(12) "ADD_DOCUMENT"
    [1]=>
    string(13) "LINK_DOCUMENT"
    [2]=>
    string(15) "UNLINK_DOCUMENT"
  }

そこから、次のような値のルックアップを実行できます。

if( in_array('ADD_DOCUMENT', $variables['role_action_list']) ){
    //do something
};

これは、同じ名前のCFML関数のPHPノックオフです。
ジェロミーフランス語2014

それが間違った質問に対する正しい答えであるため、私は反対票を投じました。
mickmackusa 2018

-1

これはどれも私にはうまくいきませんでした...だから自分で実行しなければなりませんでした。正常に動作します:

function arrayFlat($arr){
$out = '';
    foreach($arr as $key => $value){

        if(!is_array($value)){
            $out .= $value.',';
        }else{
            $out .= $key.',';
            $out .= arrayFlat($value);
        }

    }
    return trim($out,',');
}


$result = explode(',',arrayFlat($yourArray));
echo '<pre>';
print_r($result);
echo '</pre>';

このコードのみの回答は、期待どおりに機能しません。 3v4l.org/U3bfp<-証明これが私の反対票の理由です。
mickmackusa 2018

-1

多次元配列を指定してそれを1次元に変換するには、配列を持つすべての値の設定を解除し、それらを1次元に保存します。次に例を示します。

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;
}

どのバージョンでも機能しないため、この回答に反対票を投じました。3v4l.org/7cO9N(Proof)また、each()php7.2から非推奨になりました。
mickmackusa 2018
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.