16
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

配列で存在確認する関数といえば、どこに行っても第三引数trueを指定しろと書かれているin_arrayarray_searchと、isset($array[$key])って書いた方が短い謎関数array_key_existsくらいしかありませんでした。
いずれもまあそれなりに便利ではあるのですが、たとえば正規表現検索できないなど色々と使い勝手の足りないところがありました。

ということでコールバックで任意に検索できる関数を追加しようというRFCが提出されました。

既に受理されており、PHP8.4から使用可能になります。

以下は該当のRFC、array_findの日本語訳です。

PHP RFC: array_find

Introduction

このRFCでは、特定の条件に一致する要素が存在するかをチェックする新しい配列関数array_findarray_find_keyarray_anyarray_allを追加します。

コールバックを使って配列を処理する関数がいくつか存在します。
しかし、条件に一致する要素をコールバックを使って検索する関数はまだありません。
これらをユーザランドに実装するのは比較的簡単ですが、よく使われるため車輪の再発明になりがちです。
また、Rust・JavaScript・C++などでは既に言語として実装されています。
従って、これらの関数をPHPに実装する意義はあるでしょう。

また、関数の実装はarray_filterとよく似ているため、メンテナンスの労力も大きくありません。

Proposal

以下の4関数、array_findarray_find_keyarray_anyarray_allを追加します。

array_find

array_findは、引数$callbackがtrueを返す最初の要素の値を返します。
一致する要素が存在しない場合はnullが返ります。

function array_find(array $array, callable $callback): mixed {
    foreach ($array as $key => $value) {
        if ($callback($value, $key)) {
            return $value;
        }
    }
 
    return null;
}

以下は例です。

$array = [
    'a' => 'dog',
    'b' => 'cat',
    'c' => 'cow',
    'd' => 'duck',
    'e' => 'goose',
    'f' => 'elephant'
];
 
// 5文字以上の値を検索
var_dump(array_find($array, function (string $value) {
    return strlen($value) > 4;
})); // string(5) "goose"
 
// fで始まる値を検索
var_dump(array_find($array, function (string $value) {
    return str_starts_with($value, 'f');
})); // NULL
 
// 1文字目がキーになっている値を検索
var_dump(array_find($array, function (string $value, $key) {
   return $value[0] === $key;
})); // string(3) "cow"
 
// 正規表現に一致するキーを検索
var_dump(array_find($array, function ($value, $key) {
   return preg_match('/^([a-f])$/', $key);
})); // string(3) "dog"

array_find_key

array_findは、引数$callbackがtrueを返す最初の要素のキーを返します。
一致する要素が存在しない場合はnullが返ります。

function array_find_key(array $array, callable $callback): mixed {
    foreach ($array as $key => $value) {
        if ($callback($value, $key)) {
            return $key;
        }
    }
 
    return null;
}

以下は例です。

// 5文字以上の値を検索してキーを返す
var_dump(array_find_key($array, function (string $value) {
    return strlen($value) > 4;
})); // string(1) "e"
 
// fで始まる値を検索してキーを返す
var_dump(array_find_key($array, function (string $value) {
    return str_starts_with($value, 'f');
})); // NULL
 
// 1文字目がキーになっている値を検索してキーを返す
var_dump(array_find_key($array, function (string $value, $key) {
   return $value[0] === $key;
})); // string(1) "c"
 
// 正規表現に一致するキーを検索してキーを返す
var_dump(array_find_key($array, function (string $value, $key) {
   return preg_match('/^([a-f])$/', $key);
})); // string(1) "a"

array_any

array_anyは、引数$callbackがいずれかの要素に対してtrueを返す場合にtrueを返します。
そうでなければfalseを返します。

function array_any(array $array, callable $callback): bool {
    foreach ($array as $key => $value) {
        if ($callback($value, $key)) {
            return true;
        }
    }
 
    return false;
}

以下は例です。

// 5文字以上の値が存在するか
var_dump(array_any($array, function (string $value) {
    return strlen($value) > 5;
})); // bool(true)
 
// 3文字未満の値が存在するか
var_dump(array_any($array, function (string $value) {
    return strlen($value) < 3;
})); // bool(false)
 
// string型でないキーが存在するか
var_dump(array_any($array, function (string $value, $key) {
   return !is_string($key);
})); // bool(false)

array_all

array_allは、引数$callbackが全ての要素に対してtrueを返す場合にtrueを返します。
そうでなければfalseを返します。

function array_all(array $array, callable $callback): bool {
    foreach ($array as $key => $value) {
        if (!$callback($value, $key)) {
            return false;
        }
    }
 
    return true;
}

以下は例です。

// 全ての値が12文字未満か
var_dump(array_all($array, function (string $value) {
    return strlen($value) < 12;
})); // bool(true)
 

// 全ての値が6文字以上か
var_dump(array_all($array, function (string $value) {
    return strlen($value) > 5;
})); // bool(false)
 
// 全てのキーがstring型か
var_dump(array_all($array, function (string $value, $key) {
   return is_string($key);
})); // bool(true)

Backward Incompatible Changes

下位互換性のない変更点。

ユーザランドで同名の関数を作っていた場合動作しなくなります。
GitHubで軽く検索したところ、array_findが656件、array_find_keyが28件、array_anyが127件、array_allが284件ありました。

Proposed PHP Version(s)

PHP8.4

RFC Impact

SAPIやエクステンションへの影響は特にありません。
新しい定数やini設定はありません。

Open Issues

この関数は必ずコールバックにキーと値を渡します。
コールバックに渡すパラメータを設定するパラメータがあるarray_filterとは動作が異なります。
キーを渡せないかわりに複数の配列を渡せるarray_mapとも動作が異なります。

Unaffected PHP Functionality

このRFCは新しい関数を追加するだけであり、他のPHP機能に影響は与えません。

Proposed Voting Choices

投票期間は2024/05/15~2024/05/29、投票の2/3の賛成で受理されます。

array_find()/array_find_key()は賛成21反対0、array_any()/array_all()は賛成20反対0の全会一致で可決されました。

Implementation

感想

これまでこういうことをやる場合は素直にforeachでぐるぐる回すか、array_walkあたりで芸術的な記述をしていたわけですが、なんかいいかんじに書けるようになりますね。

まあ私はおそらく今後もforeach使うと思いますが。

ちなみに『引数$callbackがtrueを返す全ての要素を返す』関数はarray_filterです。
名前が揃ってないのがそこはかとなくわかりにくい。

16
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?