PHPで1つの配列の要素が別の配列にあるかどうかを確認する


130

PHPには次の2つの配列があります。

人:

Array
(
    [0] => 3
    [1] => 20
)

指名手配犯:

Array
(
    [0] => 2
    [1] => 4
    [2] => 8
    [3] => 11
    [4] => 12
    [5] => 13
    [6] => 14
    [7] => 15
    [8] => 16
    [9] => 17
    [10] => 18
    [11] => 19
    [12] => 20
)

People要素のいずれかWanted Criminals配列にあるかどうかを確認するにはどうすればよいですか?

この例では、Wanted Criminalsにいるtrueため、戻ります。20

回答:


204

使用できますarray_intersect()

$result = !empty(array_intersect($people, $criminals));

8
empty()は変数以外では使用できません。
grantwparks 2009

@grantwparksでは、なぜこの関数についてのPHPドキュメントでは、「varが存在し、空ではなく、ゼロ以外の値がある場合はFALSEを返します。それ以外の場合はTRUEを返します。次のものが空と見なされます:array()(空の配列)」?出典:php.net/manual/en/function.empty.php
Pere

5
リンクしたページから:「PHP 5.5より前では、empty()は変数のみをサポートしています。それ以外の場合は解析エラーが発生します。つまり、次は機能しません:empty(trim($ name))。代わりに、 trim($ name)== falseを使用してください。」
grantwparks 2013

9
コメントで述べたように、!empty 期待どおりに動作しないことわかりました。代わりに、私はcount()次を使用しました:!count(array_intersect($people, $criminals));
Mattios550

3
これが致命的なエラーをスローするときに65票の回答としてマークされるのはなぜですか:関数の戻り値を書き込みコンテキストで使用できませんか?
Dave Heq 2017年

31

(空の代わりに)array_intersect()とcount()を使用することに少し問題があります。

例えば:

$bFound = (count(array_intersect($criminals, $people))) ? true : false;

2
それにcount()は何も問題はありませんが、パフォーマンスとは見なされていません(マイクロ最適化に関心がある場合)
ジェイクA.スミス

23

「空」が最良の選択ではない場合、これについてはどうでしょうか。

if (array_intersect($people, $criminals)) {...} //when found

または

if (!array_intersect($people, $criminals)) {...} //when not found

22

変数は言語構成体にのみ渡すことができるため、そのコードは無効です。empty()言語構造です。

これは2行で行う必要があります。

$result = array_intersect($people, $criminals);
$result = !empty($result);

問題は、それが言語構造ではないということです。問題は、参照とGregが値を渡すことを期待していることです。
Artefacto 2010

1
@ Artefacto、php.netから「注:これは言語構造であり関数ではないため、変数関数を使用して呼び出すことはできません。」パウロが言ったとおりです。
grantwparks 2013

17

in_arrayとarray_intersectのパフォーマンステスト:

$a1 = array(2,4,8,11,12,13,14,15,16,17,18,19,20);

$a2 = array(3,20);

$intersect_times = array();
$in_array_times = array();
for($j = 0; $j < 10; $j++)
{
    /***** TEST ONE array_intersect *******/
    $t = microtime(true);
    for($i = 0; $i < 100000; $i++)
    {
        $x = array_intersect($a1,$a2);
        $x = empty($x);
    }
    $intersect_times[] = microtime(true) - $t;


    /***** TEST TWO in_array *******/
    $t2 = microtime(true);
    for($i = 0; $i < 100000; $i++)
    {
        $x = false;
        foreach($a2 as $v){
            if(in_array($v,$a1))
            {
                $x = true;
                break;
            }
        }
    }
    $in_array_times[] = microtime(true) - $t2;
}

echo '<hr><br>'.implode('<br>',$intersect_times).'<br>array_intersect avg: '.(array_sum($intersect_times) / count($intersect_times));
echo '<hr><br>'.implode('<br>',$in_array_times).'<br>in_array avg: '.(array_sum($in_array_times) / count($in_array_times));
exit;

結果は次のとおりです。

0.26520013809204
0.15600109100342
0.15599989891052
0.15599989891052
0.1560001373291
0.1560001373291
0.15599989891052
0.15599989891052
0.15599989891052
0.1560001373291
array_intersect avg: 0.16692011356354

0.015599966049194
0.031199932098389
0.031200170516968
0.031199932098389
0.031200885772705
0.031199932098389
0.031200170516968
0.031201124191284
0.031199932098389
0.031199932098389
in_array avg: 0.029640197753906

in_arrayは少なくとも5倍高速です。結果が見つかるとすぐに「壊れる」ことに注意してください。


ベンチマークをありがとう。そのため、小さな配列を処理していることがわかっている場合は、を使用することをお勧めしarray_intersect()ます。
Tokeeen.com 2017年

issetさらに高速です。また、bool valを使用して有効または無効にすることもできます。また、キーとしての検索値は、重複がないことを確認してください。´array_intersect avg:0.52077736854553; in_array平均:0.015597295761108; isset avg:0.0077081203460693´
cottton

1

次のようにin_arrayを使用することもできます。

<?php
$found = null;
$people = array(3,20,2);
$criminals = array( 2, 4, 8, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20);
foreach($people as $num) {
    if (in_array($num,$criminals)) {
        $found[$num] = true;
    } 
}
var_dump($found);
// array(2) { [20]=> bool(true)   [2]=> bool(true) }

array_intersectの方が確かに使いやすいですが、パフォーマンスの点ではそれほど優れていないことがわかります。私もこのスクリプトを作成しました:

<?php
$found = null;
$people = array(3,20,2);
$criminals = array( 2, 4, 8, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20);
$fastfind = array_intersect($people,$criminals);
var_dump($fastfind);
// array(2) { [1]=> int(20)   [2]=> int(2) }

次に、両方のスニペットをそれぞれhttp://3v4l.org/WGhO7/perf#tabsおよびhttp://3v4l.org/g1Hnu/perf#tabsで実行し、それぞれのパフォーマンスを確認しました。興味深いのは、合計CPU時間、つまりユーザー時間+システム時間はPHP5.6で同じであり、メモリも同じであるということです。PHP5.4での合計CPU時間は、わずかにではありますが、in_arrayの方がarray_intersectよりも短くなっています。


結果はだまされています。一度だけ実行すると、違いを測定するには速すぎます。1秒あたり数百または数千のリクエストがある場合、数分の1秒がすぐに加算されるため、アプリケーションをスケーリングする必要があると思われる場合は、in_array実装に固執します。
フランクフォルテ2017

1

しばらく調べた後のやり方です。フィールドが「使用中」かどうかをチェックするLaravel APIエンドポイントを作成したかったので、重要な情報は次のとおりです。1)どのDBテーブルですか?2)どのDB列ですか?3)その列に検索語と一致する値がありますか?

これを知って、連想配列を作成できます:

$SEARCHABLE_TABLE_COLUMNS = [
    'users' => [ 'email' ],
];

次に、確認する値を設定できます。

$table = 'users';
$column = 'email';
$value = 'alice@bob.com';

次に、とを使用array_key_exists()in_array()て、1、2ステップのコンボを実行し、truthy条件に基づいて動作します。

// step 1: check if 'users' exists as a key in `$SEARCHABLE_TABLE_COLUMNS`
if (array_key_exists($table, $SEARCHABLE_TABLE_COLUMNS)) {

    // step 2: check if 'email' is in the array: $SEARCHABLE_TABLE_COLUMNS[$table]
    if (in_array($column, $SEARCHABLE_TABLE_COLUMNS[$table])) {

        // if table and column are allowed, return Boolean if value already exists
        // this will either return the first matching record or null
        $exists = DB::table($table)->where($column, '=', $value)->first();

        if ($exists) return response()->json([ 'in_use' => true ], 200);
        return response()->json([ 'in_use' => false ], 200);
    }

    // if $column isn't in $SEARCHABLE_TABLE_COLUMNS[$table],
    // then we need to tell the user we can't proceed with their request
    return response()->json([ 'error' => 'Illegal column name: '.$column ], 400);
}

// if $table isn't a key in $SEARCHABLE_TABLE_COLUMNS,
// then we need to tell the user we can't proceed with their request
return response()->json([ 'error' => 'Illegal table name: '.$table ], 400);

Laravel固有のPHPコードをお詫びしますが、疑似コードとして読み取れると思うので、そのままにしておきます。重要な部分は、if同期的に実行される2つのステートメントです。

array_key_exists()そして、in_array()PHP関数です。

ソース:

上で示したアルゴリズムの良いGET /in-use/{table}/{column}/{value}ところtablecolumn、(、、valueは変数)などのRESTエンドポイントを作成できることです。

あなたが持つことができます:

$SEARCHABLE_TABLE_COLUMNS = [
    'accounts' => [ 'account_name', 'phone', 'business_email' ],
    'users' => [ 'email' ],
];

その後、次のようなGETリクエストを作成できます。

GET /in-use/accounts/account_name/Bob's Drywall (最後の部分をuriエンコードする必要があるかもしれませんが、通常はそうではありません)

GET /in-use/accounts/phone/888-555-1337

GET /in-use/users/email/alice@bob.com

また、誰も実行できないことにも注意してください。

GET /in-use/users/password/dogmeat1337理由passwordのための許可された列のリストに記載されていませんuser

あなたの旅に頑張ってください。


これが質問とどう関係しているのかわかりませんが、調べてみました$SEARCHABLE_TABLE_COLUMNS。動的データを絶対に使用しないでください!これはインジェクションを叫びます-テーブルと列の文字列をマスクしてフィルタリングしようとする「超安全なフレームワーククエリビルダー」の間にあるかどうかに関係なく!最後に、テーブルと列の文字列はプレースホルダー(準備されたステートメント)を介して追加できず、のように直接挿入する必要がありますSELECT ... FROM {$table} WHERE {$column} = :placeholder ....。Ofcはアダプター(mysql、mongoなど)に依存しますが、保存する引数はありません!Pls静的またはリストなし=)
cotton
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.