CakePHPのHash::flatten()が気になったので読んでみました。
みなさんごきげんよう。yamashitaです。
最近はグランなハンバーガーを提供しているお店に通っています。
理由は聞かないでください。
日々の業務に取り組んでる中でCakePHPの機能を少しずつ覚えていますが、Hashって便利ですよね。
よく使いそうなものから使う機会がなさそうなものまで様々ですが、今回はflattenってどんな風に実現しているんだろうと思って中身を見てみました。
Hash::flatten()とは?
以下にある通り多次元配列を1次元配列へと平坦化するメソッドです。
https://book.cakephp.org/2.0/ja/core-utility-libraries/hash.html#Hash::flatten
\cakephp\lib\Cake\Utility\Hash.php
ソースはこちらになります
※今回参考にするCakeのバージョンは2.10.3になります。
読んでみました
public static function flatten(array $data, $separator = '.') {
$result = array();
$stack = array();
$path = null;
reset($data);
while (!empty($data)) {
ここだけ見てポインタで順番に始めから最後まで回していくだけだな!と勘違いしました。
そんなことはなかったのでwhileの中を更に読んでいきたいと思います。
$key = key($data);
$element = $data[$key];
unset($data[$key]);
if (is_array($element) && !empty($element)) {
if (!empty($data)) {
$stack[] = array($data, $path);
}
$data = $element;
reset($data);
$path .= $key . $separator;
}
3行目でunsetしており、いきなり私のポインタを進めていく予想は外れました。
指定して抜き出した値が配列だった時の処理ですね。私ならその場で一番下の階層に行くまで追い続けると思います。
ところが6行目で$dataを別に残しておいて8行目で始めに抜き出した値で塗り替えてます。
ポインタを進めるどころかループさせる配列が別の物になって驚きを隠せません。
更に読んでみましょう
else {
$result[$path . $key] = $element;
}
上の抜き出した値が配列ではなかった時の処理ですね。
掘り下げていって配列が無くなったら一番下の階層なので呼び出し元に返す値に追加しているようです。
なるほど多次元配列が一次元になる動きがわかってきました。
whileの中最後です。
if (empty($data) && !empty($stack)) {
list($data, $path) = array_pop($stack);
reset($data);
}
empty($data)とあるので一個目のキーの下の階層を掘りつくした後の処理ですね。
$stackに保存していた値を$dataに戻しています。このあとまた先頭のキーから掘り下げて以下繰り返しです。
まとめ
ざっくりとした流れとしては
- 一番上の一個目のキーを取得
- 配列から取得したキーを削除
- 下があれば掘り下げていく
- 下が無ければ返り値に保存
- 1.に戻って先頭(先頭を削除してるので二個目)のキーから同じ処理を繰り返し
といったところでしょうか。中身を見るまでは再帰等ですごくスマートにやっているのだと思っていたので結構力技でビックリしました。
メソッドの中身を読むと知ってる機能知らない機能色々あり、知ってても使いどころがわからない機能が使われていて勉強になる気がします。