連想配列で検索したい!!!
(2018-09-13:書いてるとおりには動いてたけど、実用を考えると破綻してたので、まるっと書き直しました)
array_columnを使えないバージョンを使わざるを得ない時用。
使えるならこちらをご参照ください。
今回は以下のような連想配列から検索することを考えます。
<?php $data_list = array( 1 => array('hoge' => "a" , 'fuga' => "A" ), 2 => array('hoge' => "b" , 'fuga' => "B" ), 3 => array('hoge' => "a" , 'fuga' => "C" ), // hogeが1と重複していることに注意 4 => array('hoge' => null , 'fuga' => "D" ), 5 => array('hoge' => false, 'fuga' => "E" ), 6 => array('hoge' => "f" , 'fuga' => null ), 7 => array('hoge' => "g" , 'fuga' => false), );
検索パターンは次の3通り。
これを全部まとめて関数化したい!と思うのですが、一つ問題が。
「検索結果が存在しないこと」と「検索結果がfalseであること」の戻り値を両立するの難しくない?
上記の配列で、「キーが7のfugaの値が欲しい!」となったとき、欲しい結果はもちろんfalseなんですが、
じゃあ「キーが8のfugaの値が欲しい!」と言われたら? 8は存在しないんだから、関数としてはfalseを返すべきじゃないの?
まあ、戻り値をこんな感じにすることもできますが
array( 'not_found_flag' => 1, // 見つからなかったよフラグ 'result' => null, // 検索結果 )
お手軽に検索したいから関数化するというのに、戻り値がいちいち配列なのはちょっとつらい。あとなんかかっこわるい。
連想配列の中身を検索し、キーを返す関数
ということで、そもそもひとつにまとめないパターンを考えてみました。
キーはfalseにならないのでこれなら関数としてもすっきり。
ただし、ここでは「hoge」やら「fuga」やらが存在してないケースまでは考慮してないので
必要に応じて更にバリデートを入れることになります。
あと、比較は厳密に行っています。場合によってはゆるい比較にしてもOK。
<?php function getKey($list, $val, $key) { foreach ($list as $k => $v) { if ($v[$key] === $val) { return $k; } } return false; }
さっきの配列で検索してみるとこんな感じ。
<?php var_dump( getKey($data_list, "a" , "hoge") ); // 1 : 重複してると最初のキーを返す var_dump( getKey($data_list, null , "hoge") ); // 4 : 値がnullの場合もOK var_dump( getKey($data_list, false, "hoge") ); // 5 : 値がfalseの場合もOK var_dump( getKey($data_list, "h" , "hoge") ); // false : 存在しない var_dump( getKey($data_list, "A" , "fuga") ); // 1 : もちろんfugaからも検索可
あとは適宜必要に応じて戻り値がfalseかどうかをチェックすればOK。
検索結果が存在しないということはあり得ない、という場合は見なくていいし。
ということで実際に検索してみましょう。
さっきの検索したいパターンに当てはめると、
1. キーからhogeの値を求める
これはキーが分かっている状態なのでそもそもさっきの関数すら使わない。
<?php // キー「1」の「hoge」を求める:$resは「a」 $res = $data_list[1]['hoge'];
レコードが存在しない可能性があるならこう。
<?php if (isset($data_list[1]['hoge'])) { // 検索結果は $data_list[1]['hoge'] } else { // 検索結果なし }
2. hogeからキーの値を求める
これはつまりさっきの関数の戻り値。
<?php // hogeが「a」のキーを求める:$resは「1」 $res = getKey($data_list, "a" , "hoge");
レコードが存在しない可能性があるならこう。
<?php $res = getKey($data_list, "a" , "hoge"); if ($res !== false) { // 検索結果は $res } else { // 検索結果なし }
3. hogeからfugaの値を求める
1と2の合わせ技。
<?php // hogeが「a」の「fuga」を求める:$resは「A」 $res = $data_list[getKey($data_list, "a" , "hoge")]['fuga'];
レコードが存在しない可能性があるならこう。
<?php $key = getKey($data_list, "a" , "hoge"); if ($key !== false) { // 検索結果は $data_list[$key]['fuga']; } else { // 検索結果なし }
falseのデータとかそもそもレコードが存在しないとかありえないから一発で済ませたい!!!
という場合はこちら。
hogeの値「a」を元に、キーの値「1」を求める
ほい。
<?php // $resは「1」 $res = key(array_filter(array_map(function($l){return $l['hoge'] === "a";}, $data_list)));
いきなりややこしくなりました。
ちょっと分解しましょう。
<?php // 配列[hoge]が「a」ならtrueを返す無名関数 function($l){return $l['hoge'] === "a";} // …をarray_mapにぶちこむ array_map(function($l){return $l['hoge'] === "a";}, $data_list); // その結果はtrueかfalseでできた一次元配列 // array(1 => true, 2=> false, 3=> true) // になるので、それをtrueでフィルタリングする array_filter(array_map(function($l){return $l['hoge'] === "a";}, $data_list)); // trueだけになった配列 // array(1 => true, 3=> true) // の、現在の…っつーか何もしてないのでつまりは先頭のキーを取得 key(array_filter(array_map(function($l){return $l['hoge'] === "a";}, $data_list))); // 完成 $res = key(array_filter(array_map(function($l){return $l['hoge'] === "a";}, $data_list)));
ちなみに存在しない値で検索しようとした場合はNULLになります。
ただ、キーがNULLになることはあり得ないので(NULLに設定しようとすると勝手に空文字になります)
NULLなら検索結果なしと判断してOK。
hogeの値「a」を元に、fugaの値「A」を求める
上記2つの組み合わせ。
<?php // $resは「A」 $res = $data_list[key(array_filter(array_map(function($l){return $l['hoge'] === "a";}, $data_list)))]['fuga'];
これも存在しない場合はNoticeが出ます。