PHPは色んな便利標準関数があって助かります。
そんな中でも、配列のマージ(合体)で希望通りの動きを実現するまでに
結構時間がかかってしまったのでメモ。
こちらも参考にしましたので、合わせてご覧ください。
前提
- 多次元配列
- キーは数字・文字混合で、変化させない(これが厄介でした)
- 配列
$before
を元として、$artisan
をマージする - キーが
$before
になく$artisan
にあるものは単純に加える - キー
$before
にも$artisan
にもあるものは、$artisan
の値で上書きする - 結果は出来る限り
$after
に($before
や$artisan
を変える形になっても良し)
こんな配列を想定する
$before = array(
'0' => 0,
'1' => array(
'0' => 0,
'1' => 1,
),
'2' => 2,
'string' => 'string',
);
$artisan = array(
'0' => '000',
'1' => array(
'0' => '000',
'2' => '222',
),
'string' => 'STRING',
'3' => 3,
);
結果の想定
$after = array(
'0' => '000',
'1' => array(
'0' => '000',
'1' => 1,
'2' => '222',
),
'2' => 2,
'string' => 'STRING',
'3' => 3,
);
array_merge
$after = array(
'0' => 0,
'1' => array(
'0' => 0,
'1' => 1,
),
'2' => 2,
'string' => 'STRING',
'3' => '000',
'4' => array(
'0' => '000',
'2' => '222',
),
'5' => 3,
);
キーが文字列の場合はきちんとマージされているが、
キーが数字の場合は添え字を振り直している。
array_merge_recursive
再帰的にマージする関数。
$after = array(
'0' => 0,
'1' => array(
'0' => 0,
'1' => 1,
),
'2' => 2,
'string' => array(
'0' => 'string',
'1' => 'STRING',
),
'3' => '000',
'4' => array(
'0' => '000',
'2' => '222',
),
'5' => 3,
);
数字のキーは先ほどと変わらず、重複があった場合は振り直し。
文字のキーは重複があった場合、配列として全て保持するように変更されている。
+(プラス)演算子
$after = array(
'0' => 0,
'1' => array(
'0' => 0,
'1' => 1,
),
'2' => 2,
'string' => string',
'3' => 3,
);
キーは文字数字問わず、重複する場合は+演算子の前にある配列が優先され、
子要素などを持っていようと、評価していない。
一次元目のキーだけを見て、重複していないもののみ追加している様子。
foreachで再帰的に回してみる
こちらより。
function mymerge($arr1, $arr2)
{
foreach($arr2 as $key=>$value) {
if(!is_array($arr1)) {
$arr1 = array($arr1);
}
if(is_array($value)) {
$arr1[$key] = mymerge($arr1[$key], $value);
} else {
$arr1[$key] = $value;
}
}
return $arr1;
}
$after = array(
'0' => '000',
'1' => array(
'0' => '000',
'1' => 1,
'2' => '222',
),
'2' => 2,
'string' => 'STRING',
'3' => 3,
);
素晴らしい。要件通りです。
けれど、foreachで回すとなると大規模データでの速度が気になります。
(速度の検証はしませんが)他の手段はないものか探していたら…
array_replace_recursive
$after = array(
'0' => '000',
'1' => array(
'0' => '000',
'1' => 1,
'2' => '222',
),
'2' => 2,
'string' => 'STRING',
'3' => 3,
);
おお、まさかのreplace。盲点でしたが要件通りで完璧な挙動です。
同じく検証をしていないため、ひょっとしたらforeachと大差ないかもしれませんが、
標準関数なので信用しちゃいます。
追記
array_diff
とかarray_intersect
とかでも考えていたのですが、頭がパンクしたのでストップ。
これ以外にも上手いやり方がありましたらぜひご教示下さい。