Hash
class Hash
Added in version 2.2
配列マネジメントはとても強力かつ便利なツールであり、適切に使いさえすれば、 よりスマートでより最適化されたコードを書くことができるようになるものです。 CakePHP ではとても便利なユーティリティ群を Hash クラスの中に static で用意しており、まさにこれをするのに使えます。
CakePHP の Hash クラスは Inflector クラスと同様で、どのモデルや コントローラからでも呼ぶことができます。 例: Hash::combine()。
Hash パス構文
下記のパス構文は Hash が持つすべてのメソッドで使われるものです。 ただし、すべてのパス構文が、すべてのメソッドで使用可能であるとは限りません。 パスの式はいくつものトークンで構成されます。トークンは、配列データの移動に使う『式』と、 要素を絞り込む『マッチャー』の2つのグループに大きく分けられます。 マッチャーは要素の式に対して適用することができます。
式の種類
| 式 | 説明 |
|---|---|
{n} | 数値キーを意味する。どんな文字列キーでも 数値型のキーでも一致する。 |
{s} | 文字列キーを意味する。数値文字列を含め、 どんな文字列でも一致する。 |
{*} | 型に関係なく任意の値を表す。 |
Foo | 完全に同じ値だった場合のみ一致する。 |
要素の式はいずれも、すべてのメソッドで使うことができます。特定のメソッドでは、 要素の式に加え、 属性で絞り込むこともできます。該当するメソッドは、 extract(), combine(), format(), check(), map(), reduce(), apply(), sort(), insert(), remove() と nest() です。
属性の絞り込み種別
| マッチャー | 説明 |
|---|---|
[id] | 記述されたキーと一致する要素に絞り込む。 |
[id=2] | id が 2 となっている要素に絞り込む。 |
[id!=2] | id が 2 ではない要素に絞り込む。 |
[id>2] | id が 2 より大きい要素に絞り込む。 |
[id>=2] | id が 2 以上の要素に絞り込む。 |
[id<2] | id が 2 より小さい要素に絞り込む。 |
[id<=2] | id が 2 以下の要素に絞り込む。 |
[text=/.../] | 正規表現 ... と合致する値を持っている 要素に絞り込む。 |
Changed in version 2.5
insert() と remove() にマッチャーのサポートが追加されました。
static Hash::get(array $data, $path, $default = null)
戻り値の型: mixed
get() は extract() のシンプル版で、直接的に指定するパス式のみがサポートされます。 {n} や {s} 、マッチャーを使ったパスはサポートされません。 配列から1つの値だけを取り出したい場合に get() を使ってください。
static Hash::extract(array $data, $path)
戻り値の型: array
Hash::extract() は Hash Path Syntax にあるすべての式とマッチャーを サポートします。extract を使うことで、配列から好きなパスに沿ったデータを手早く 取り出すことができます。もはやデータ構造をループする必要はありません。 その代わりに欲しい要素を絞り込むパス式を使うのです:
// 共通の使い方:
$users = $this->user->find('all');
$results = Hash::extract($users, '{n}.User.id');
// $results は以下のとおり:
// [1,2,3,4,5,...];static Hash::insert(array $data, $path, $values = null)
戻り値の型: array
$data を $path の定義に従って配列の中に挿入します。 :
$a = [
'pages' => ['name' => 'page'],
];
$result = Hash::insert($a, 'files', ['name' => 'files']);
// $result は以下のようになります:
[
[pages] =>
[
[name] => page
]
[files] =>
[
[name] => files
]
]{n} や {s} を使ったパスを使うことで、複数のポイントにデータを挿入することが できます:
$users = $this->User->find('all');
$users = Hash::insert($users, '{n}.User.new', 'value');Changed in version 2.5
2.5.0 から、 insert() で属性のマッチャー表現が動作します。
static Hash::remove(array $data, $path)
戻り値の型: array
$path に合致するすべての要素を配列から削除します。
$a = [
'pages' => ['name' => 'page'],
'files' => ['name' => 'files'],
];
$result = Hash::remove($a, 'files');
/* $result は以下のようになります:
[
[pages] => [
[name] => page
]
]
*/{n} や {s} を使うことで、複数の値を一度に削除することができます。
Changed in version 2.5
2.5.0 から、 insert() で属性のマッチャー表現が動作します。
static Hash::combine(array $data, $keyPath, $valuePath = null, $groupPath = null)
戻り値の型: array
$keyPath のパスをキー、$valuePath (省略可) のパスを値として使って連想配列を作ります。 $valuePath が省略された場合や、$valuePath に合致するものが無かった場合は、値は null で初期化されます。 $groupPath が指定された場合は、そのパスにしたがって生成したものをグルーピングします:
$a = [
[
'User' => [
'id' => 2,
'group_id' => 1,
'Data' => [
'user' => 'mariano.iglesias',
'name' => 'Mariano Iglesias',
],
],
],
[
'User' => [
'id' => 14,
'group_id' => 2,
'Data' => [
'user' => 'phpnut',
'name' => 'Larry E. Masters',
],
],
],
];
$result = Hash::combine($a, '{n}.User.id');
/* $result は以下のようになります:
[
[2] =>
[14] =>
]
*/
$result = Hash::combine($a, '{n}.User.id', '{n}.User.Data');
/* $result は以下のようになります:
[
[2] => [
[user] => mariano.iglesias
[name] => Mariano Iglesias
]
[14] => [
[user] => phpnut
[name] => Larry E. Masters
]
]
*/
$result = Hash::combine($a, '{n}.User.id', '{n}.User.Data.name');
/* $result は以下のようになります:
[
[2] => Mariano Iglesias
[14] => Larry E. Masters
]
*/
$result = Hash::combine($a, '{n}.User.id', '{n}.User.Data', '{n}.User.group_id');
/* $result は以下のようになります:
[
[1] => [
[2] => [
[user] => mariano.iglesias
[name] => Mariano Iglesias
]
]
[2] => [
[14] => [
[user] => phpnut
[name] => Larry E. Masters
]
]
]
*/
$result = Hash::combine($a, '{n}.User.id', '{n}.User.Data.name', '{n}.User.group_id');
/* $result は以下のようになります:
[
[1] => [
[2] => Mariano Iglesias
]
[2] => [
[14] => Larry E. Masters
]
]
*/$keyPath と $valuePath の両方とも、配列を指定することができます。 その場合は、配列の1要素目はフォーマット文字列とみなされ、 2要素目以降のパスで取得された値のために使われます:
$result = Hash::combine(
$a,
'{n}.User.id',
['%s: %s', '{n}.User.Data.user', '{n}.User.Data.name'],
'{n}.User.group_id',
);
/* $result は以下のようになります:
[
[1] => [
[2] => mariano.iglesias: Mariano Iglesias
]
[2] => [
[14] => phpnut: Larry E. Masters
]
]
*/
$result = Hash::combine(
$a,
['%s: %s', '{n}.User.Data.user', '{n}.User.Data.name'],
'{n}.User.id',
);
/* $result は以下のようになります:
[
[mariano.iglesias: Mariano Iglesias] => 2
[phpnut: Larry E. Masters] => 14
]
*/static Hash::format(array $data, array $paths, $format)
戻り値の型: array
配列から取り出し、フォーマット文字列でフォーマットされた文字列の配列を返します:
$data = [
[
'Person' => [
'first_name' => 'Nate',
'last_name' => 'Abele',
'city' => 'Boston',
'state' => 'MA',
'something' => '42',
],
],
[
'Person' => [
'first_name' => 'Larry',
'last_name' => 'Masters',
'city' => 'Boondock',
'state' => 'TN',
'something' => '{0}',
],
],
[
'Person' => [
'first_name' => 'Garrett',
'last_name' => 'Woodworth',
'city' => 'Venice Beach',
'state' => 'CA',
'something' => '{1}',
],
],
];
$res = Hash::format($data, ['{n}.Person.first_name', '{n}.Person.something'], '%2$d, %1$s');
/*
[
[0] => 42, Nate
[1] => 0, Larry
[2] => 0, Garrett
]
*/
$res = Hash::format($data, ['{n}.Person.first_name', '{n}.Person.something'], '%1$s, %2$d');
/*
[
[0] => Nate, 42
[1] => Larry, 0
[2] => Garrett, 0
]
*/static Hash::contains(array $data, array $needle)
戻り値の型: boolean
一方のハッシュや配列の中に、もう一方のキーと値が厳密に見てすべて存在しているかを判定します:
$a = [
0 => ['name' => 'main'],
1 => ['name' => 'about'],
];
$b = [
0 => ['name' => 'main'],
1 => ['name' => 'about'],
2 => ['name' => 'contact'],
'a' => 'b',
];
$result = Hash::contains($a, $a);
// true
$result = Hash::contains($a, $b);
// false
$result = Hash::contains($b, $a);
// truestatic Hash::check(array $data, string $path = null)
戻り値の型: boolean
配列の中に特定のパスがセットされているかをチェックします:
$set = [
'My Index 1' => ['First' => 'The first item'],
];
$result = Hash::check($set, 'My Index 1.First');
// $result == True
$result = Hash::check($set, 'My Index 1');
// $result == True
$set = [
'My Index 1' => ['First' =>
['Second' =>
['Third' =>
['Fourth' => 'Heavy. Nesting.']]]],
];
$result = Hash::check($set, 'My Index 1.First.Second');
// $result == True
$result = Hash::check($set, 'My Index 1.First.Second.Third');
// $result == True
$result = Hash::check($set, 'My Index 1.First.Second.Third.Fourth');
// $result == True
$result = Hash::check($set, 'My Index 1.First.Seconds.Third.Fourth');
// $result == Falsestatic Hash::filter(array $data, $callback = ['Hash', 'filter'])
戻り値の型: array
配列から空の要素(ただし '0' 以外)を取り除きます。 また、カスタム引数 $callback を指定することで配列の要素を抽出することができます。 コールバック関数が false を返した場合、その要素は配列から取り除かれます:
$data = [
'0',
false,
true,
0,
['one thing', 'I can tell you', 'is you got to be', false],
];
$res = Hash::filter($data);
/* $data は以下のとおり:
[
[0] => 0
[2] => true
[3] => 0
[4] => [
[0] => one thing
[1] => I can tell you
[2] => is you got to be
]
]
*/static Hash::flatten(array $data, string $separator = '.')
戻り値の型: array
多次元配列を1次元配列へと平坦化します:
$arr = [
[
'Post' => ['id' => '1', 'title' => 'First Post'],
'Author' => ['id' => '1', 'user' => 'Kyle'],
],
[
'Post' => ['id' => '2', 'title' => 'Second Post'],
'Author' => ['id' => '3', 'user' => 'Crystal'],
],
];
$res = Hash::flatten($arr);
/* $res は以下のようになります:
[
[0.Post.id] => 1
[0.Post.title] => First Post
[0.Author.id] => 1
[0.Author.user] => Kyle
[1.Post.id] => 2
[1.Post.title] => Second Post
[1.Author.id] => 3
[1.Author.user] => Crystal
]
*/static Hash::expand(array $data, string $separator = '.')
戻り値の型: array
Hash::flatten():: によって前もって平坦化された配列を再構築します:
$data = [
'0.Post.id' => 1,
'0.Post.title' => First Post,
'0.Author.id' => 1,
'0.Author.user' => Kyle,
'1.Post.id' => 2,
'1.Post.title' => Second Post,
'1.Author.id' => 3,
'1.Author.user' => Crystal,
];
$res = Hash::expand($data);
/* $res は以下のとおり:
[
[
'Post' => ['id' => '1', 'title' => 'First Post'],
'Author' => ['id' => '1', 'user' => 'Kyle'],
],
[
'Post' => ['id' => '2', 'title' => 'Second Post'],
'Author' => ['id' => '3', 'user' => 'Crystal'],
],
];
*/static Hash::merge(array $data, array $merge[, array $n])
戻り値の型: array
この関数は PHP の array_merge と array_merge_recursive の 両方の機能を持っていると考えることができます。この2つの関数との違いは、一方の配列キーが もう一方に含まれていた場合には (array_merge と違って) 再帰的に動きますが、 含まれていなかった場合には (array_merge_recursive と違って) 再帰的には動きません。
NOTE
この関数の引数の個数に制限はありません。また、配列以外が引数に指定された場合は 配列へとキャストされます。
$array = [
[
'id' => '48c2570e-dfa8-4c32-a35e-0d71cbdd56cb',
'name' => 'mysql raleigh-workshop-08 < 2008-09-05.sql ',
'description' => 'Importing an sql dump',
],
[
'id' => '48c257a8-cf7c-4af2-ac2f-114ecbdd56cb',
'name' => 'pbpaste | grep -i Unpaid | pbcopy',
'description' => 'Remove all lines that say "Unpaid".',
],
];
$arrayB = 4;
$arrayC = [0 => 'test array', 'cats' => 'dogs', 'people' => 1267];
$arrayD = ['cats' => 'felines', 'dog' => 'angry'];
$res = Hash::merge($array, $arrayB, $arrayC, $arrayD);
/* $res は以下のとおり:
[
[0] => [
[id] => 48c2570e-dfa8-4c32-a35e-0d71cbdd56cb
[name] => mysql raleigh-workshop-08 < 2008-09-05.sql
[description] => Importing an sql dump
]
[1] => [
[id] => 48c257a8-cf7c-4af2-ac2f-114ecbdd56cb
[name] => pbpaste | grep -i Unpaid | pbcopy
[description] => Remove all lines that say "Unpaid".
]
[2] => 4
[3] => test array
[cats] => felines
[people] => 1267
[dog] => angry
]
*/static Hash::numeric(array $data)
戻り値の型: boolean
配列内のすべての値が数値であるかをチェックします:
$data = ['one'];
$res = Hash::numeric(array_keys($data));
// $res は true
$data = [1 => 'one'];
$res = Hash::numeric($data);
// $res は falsestatic Hash:😗*dimensions **(array $data)
戻り値の型: integer
配列の次元数を数えます。このメソッドは配列の1つ目の要素だけを見て次元を判定します:
$data = ['one', '2', 'three'];
$result = Hash::dimensions($data);
// $result == 1
$data = ['1' => '1.1', '2', '3'];
$result = Hash::dimensions($data);
// $result == 1
$data = ['1' => ['1.1' => '1.1.1'], '2', '3' => ['3.1' => '3.1.1']];
$result = Hash::dimensions($data);
// $result == 2
$data = ['1' => '1.1', '2', '3' => ['3.1' => '3.1.1']];
$result = Hash::dimensions($data);
// $result == 1
$data = ['1' => ['1.1' => '1.1.1'], '2', '3' => ['3.1' => ['3.1.1' => '3.1.1.1']]];
$result = Hash::countDim($data);
// $result == 2static Hash::maxDimensions(array $data)
Hash::dimensions() に似ていますが、このメソッドは配列内にあるもっとも大きな次元数を返します:
$data = ['1' => '1.1', '2', '3' => ['3.1' => '3.1.1']];
$result = Hash::dimensions($data, true);
// $result == 2
$data = ['1' => ['1.1' => '1.1.1'], '2', '3' => ['3.1' => ['3.1.1' => '3.1.1.1']]];
$result = Hash::countDim($data, true);
// $result == 3static Hash::map(array $data, $path, $function)
$path で抽出し、各要素に $function を割り当て(map)ることで新たな配列を作ります。 このメソッドでは式とマッチャーの両方を使うことができます。
static Hash::reduce(array $data, $path, $function)
$path で抽出し、抽出結果を $function で縮小(reduce)することでを単一の値を作ります。 このメソッドでは式とマッチャーの両方を使うことができます。
static Hash::sort(array $data, $path, $dir, $type = 'regular')
戻り値の型: array
Hash Path Syntax によって、どの次元のどの値によってでもソートすることができます。 このメソッドでは式のみがサポートされます:
$a = [
0 => ['Person' => ['name' => 'Jeff']],
1 => ['Shirt' => ['color' => 'black']],
];
$result = Hash::sort($a, '{n}.Person.name', 'asc');
/* $result は以下のとおり:
[
[0] => [
[Shirt] => [
[color] => black
]
]
[1] => [
[Person] => [
[name] => Jeff
]
]
]
*/$dir には asc もしくは desc を指定することができます。 $type には次のいずれかを指定することができます。
regular: 通常のソート。numeric: 数値とみなしてソート。string: 文字列としてソート。natural: ヒューマン・フレンドリー・ソート。例えば、foo10がfoo2の下に配置される。
Added in version 2.8
$type オプションは、配列に対応しました。 ignoreCase オプションは、 大文字小文字の区別しないソートを有効にします。
static Hash::diff(array $data, array $compare)
戻り値の型: array
2つの配列の差分を計算します:
$a = [
0 => ['name' => 'main'],
1 => ['name' => 'about'],
];
$b = [
0 => ['name' => 'main'],
1 => ['name' => 'about'],
2 => ['name' => 'contact'],
];
$result = Hash::diff($a, $b);
/* $result は以下のとおり:
[
[2] => [
[name] => contact
]
]
*/static Hash::mergeDiff(array $data, array $compare)
戻り値の型: array
この関数は2つの配列をマージし、差分は、その結果の配列の下部に push します。
例1 :
$array1 = ['ModelOne' => ['id' => 1001, 'field_one' => 'a1.m1.f1', 'field_two' => 'a1.m1.f2']];
$array2 = ['ModelOne' => ['id' => 1003, 'field_one' => 'a3.m1.f1', 'field_two' => 'a3.m1.f2', 'field_three' => 'a3.m1.f3']];
$res = Hash::mergeDiff($array1, $array2);
/* $res は以下のとおり:
[
[ModelOne] => [
[id] => 1001
[field_one] => a1.m1.f1
[field_two] => a1.m1.f2
[field_three] => a3.m1.f3
]
]
*/例2 :
$array1 = ['a' => 'b', 1 => 20938, 'c' => 'string'];
$array2 = ['b' => 'b', 3 => 238, 'c' => 'string', ['extra_field']];
$res = Hash::mergeDiff($array1, $array2);
/* $res は以下のとおり:
[
[a] => b
[1] => 20938
[c] => string
[b] => b
[3] => 238
[4] => [
[0] => extra_field
]
]
*/static Hash::normalize(array $data, $assoc = true)
戻り値の型: array
配列を正規化します。 $assoc が true なら、連想配列へと正規化された配列が 返ります。値を持つ数値キーは null を持つ文字列キーへと変換されます。 配列を正規化すると、 Hash::merge() で扱いやすくなります:
$a = ['Tree', 'CounterCache',
'Upload' => [
'folder' => 'products',
'fields' => ['image_1_id', 'image_2_id'],
],
];
$result = Hash::normalize($a);
/* $result は以下のとおり:
[
[Tree] => null
[CounterCache] => null
[Upload] => [
[folder] => products
[fields] => [
[0] => image_1_id
[1] => image_2_id
]
]
]
*/
$b = [
'Cacheable' => ['enabled' => false],
'Limit',
'Bindable',
'Validator',
'Transactional',
];
$result = Hash::normalize($b);
/* $result は以下のとおり:
[
[Cacheable] => [
[enabled] => false
]
[Limit] => null
[Bindable] => null
[Validator] => null
[Transactional] => null
]
*/static Hash::nest(array $data, array $options = [])
平坦な配列から、多次元配列もしくはスレッド状(threaded)の構造化データを生成します。 Model::find('threaded') のようなメソッドで使われます。
オプション:
children: 子の配列のために使われる戻り値のキー名。デフォルトは 'children'。idPath: 各要素を識別するためのキーを指すパス。Hash::extract()と同様に指定する。デフォルトは{n}.$alias.idparentPath: 各要素の親を識別するためのキーを指すパス。Hash::extract()と同様に指定する。デフォルトは{n}.$alias.parent_idroot: 最上位となる要素の id 。
例:
$data = [
['ModelName' => ['id' => 1, 'parent_id' => null]],
['ModelName' => ['id' => 2, 'parent_id' => 1]],
['ModelName' => ['id' => 3, 'parent_id' => 1]],
['ModelName' => ['id' => 4, 'parent_id' => 1]],
['ModelName' => ['id' => 5, 'parent_id' => 1]],
['ModelName' => ['id' => 6, 'parent_id' => null]],
['ModelName' => ['id' => 7, 'parent_id' => 6]],
['ModelName' => ['id' => 8, 'parent_id' => 6]],
['ModelName' => ['id' => 9, 'parent_id' => 6]],
['ModelName' => ['id' => 10, 'parent_id' => 6]],
];
$result = Hash::nest($data, ['root' => 6]);
/* $result は以下のとおり:
[
(int) 0 => [
'ModelName' => [
'id' => (int) 6,
'parent_id' => null
],
'children' => [
(int) 0 => [
'ModelName' => [
'id' => (int) 7,
'parent_id' => (int) 6
],
'children' => []
],
(int) 1 => [
'ModelName' => [
'id' => (int) 8,
'parent_id' => (int) 6
],
'children' => []
],
(int) 2 => [
'ModelName' => [
'id' => (int) 9,
'parent_id' => (int) 6
],
'children' => []
],
(int) 3 => [
'ModelName' => [
'id' => (int) 10,
'parent_id' => (int) 6
],
'children' => []
]
]
]
]
*/