Überprüfen Sie, ob sich die Elemente eines Arrays in einem anderen Array in PHP befinden

130

Ich habe zwei Arrays in PHP wie folgt:

Menschen:

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

Gesuchte Kriminelle:

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
)

Wie kann ich überprüfen , ob jede der Menschen Elemente in denen sind Verbrecher Array?

In diesem Beispiel sollte es zurückgegeben werden, trueda 20es sich um gesuchte Kriminelle handelt .

Philip Morton
quelle

Antworten:

203

Sie können verwenden array_intersect().

$result = !empty(array_intersect($people, $criminals));
Greg
quelle
8
Kann empty () nur mit einer Variablen verwenden.
Grantwparks
@grantwparks Warum sagen sie dann in PHP-Dokumenten zu dieser Funktion "Gibt FALSE zurück, wenn var existiert und einen nicht leeren Wert ungleich Null hat. Andernfalls wird TRUE zurückgegeben. Die folgenden Dinge werden als leer betrachtet: array () (ein leeres Array ) "? Quelle: php.net/manual/en/function.empty.php
Pere
5
Auf der Seite, auf die Sie verlinkt haben: "Vor PHP 5.5 unterstützt empty () nur Variablen; alles andere führt zu einem Analysefehler. Mit anderen Worten, Folgendes funktioniert nicht: empty (trim ($ name)). Stattdessen benutze trim ($ name) == false. "
Grantwparks
9
Wie in den Kommentaren erwähnt, stellte ich fest, dass !empty dies nicht wie erwartet funktioniert . Stattdessen habe ich verwendet count():!count(array_intersect($people, $criminals));
Mattios550
3
Warum wird dies als Antwort mit 65 Stimmen markiert, wenn ein schwerwiegender Fehler ausgegeben wird: Der Funktionsrückgabewert kann im Schreibkontext nicht verwendet werden?
Dave Heq
31

Es ist wenig falsch, array_intersect () und count () (anstelle von leer) zu verwenden.

Beispielsweise:

$bFound = (count(array_intersect($criminals, $people))) ? true : false;
papsy
quelle
2
Es ist nichts falsch daran, count()wird aber nicht als performant angesehen (wenn Sie sich für Mikrooptimierung interessieren)
Jake A. Smith
23

Wenn "leer" nicht die beste Wahl ist, was ist dann damit:

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

oder

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

Dieser Code ist ungültig, da Sie nur Variablen an Sprachkonstrukte übergeben können. empty()ist ein Sprachkonstrukt.

Sie müssen dies in zwei Zeilen tun:

$result = array_intersect($people, $criminals);
$result = !empty($result);
Paul Dragoonis
quelle
Das Problem ist nicht, dass es sich um ein Sprachkonstrukt handelt. Das Problem ist, dass es eine Referenz erwartet und Greg einen Wert übergibt.
Artefacto
1
@Artefacto aus dem php.net "Hinweis: Da dies ein Sprachkonstrukt und keine Funktion ist, kann es nicht mit variablen Funktionen aufgerufen werden." Es ist genau so, wie Paul es gesagt hat.
Grantwparks
17

Leistungstest für in_array vs 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;

Hier sind die Ergebnisse:

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 ist mindestens 5 mal schneller. Beachten Sie, dass wir "brechen", sobald ein Ergebnis gefunden wird.

Frank Forte
quelle
Danke für den Benchmark. Wenn Sie also wissen, dass Sie mit kleinen Arrays arbeiten, ist es besser, bei zu bleiben array_intersect().
Tokeeen.com
issetist noch schneller. Und Sie können bool val zum Aktivieren oder Deaktivieren verwenden. Auch die Suchwerte als Schlüssel stellen sicher, dass keine Duplikate vorhanden sind. ´array_intersect avg: 0.52077736854553; in_array avg: 0.015597295761108; isset avg: 0.0077081203460693´
Cottton
1

Sie können in_array auch wie folgt verwenden:

<?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 ist sicherlich bequemer zu bedienen, es stellt sich jedoch heraus, dass es in Bezug auf die Leistung nicht wirklich überlegen ist. Ich habe auch dieses Skript erstellt:

<?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) }

Dann habe ich beide Snippets unter http://3v4l.org/WGhO7/perf#tabs und http://3v4l.org/g1Hnu/perf#tabs ausgeführt und die Leistung der einzelnen Snippets überprüft. Das Interessante ist, dass die Gesamt-CPU-Zeit, dh Benutzerzeit + Systemzeit, für PHP5.6 gleich ist und der Speicher auch gleich ist. Die Gesamt-CPU-Zeit unter PHP5.4 ist für in_array geringer als für array_intersect, wenn auch nur geringfügig.

slevy1
quelle
Die Ergebnisse täuschen. Es nur einmal auszuführen, ist zu schnell, um einen Unterschied zu messen. Wenn Sie Hunderte oder Tausende von Anfragen pro Sekunde haben, summieren sich diese Sekundenbruchteile schnell. Wenn Sie also der Meinung sind, dass Ihre Anwendung skaliert werden muss, bleibe ich bei der in_arrayImplementierung.
Frank Forte
1

Hier ist eine Möglichkeit, wie ich es mache, nachdem ich es eine Weile recherchiert habe. Ich wollte einen Laravel-API-Endpunkt erstellen, der prüft, ob ein Feld "verwendet" wird. Die folgenden wichtigen Informationen lauten daher: 1) Welche DB-Tabelle? 2) Welche DB-Spalte? und 3) gibt es in dieser Spalte einen Wert, der mit den Suchbegriffen übereinstimmt?

Wenn wir das wissen, können wir unser assoziatives Array konstruieren:

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

Dann können wir unsere Werte festlegen, die wir überprüfen werden:

$table = 'users';
$column = 'email';
$value = '[email protected]';

Dann können wir array_key_exists()und zusammen in_array()eine ein-, zweistufige Kombination ausführen und dann auf die truthyBedingung reagieren:

// 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);

Ich entschuldige mich für den Laravel-spezifischen PHP-Code, aber ich werde ihn verlassen, weil ich denke, dass Sie ihn als Pseudocode lesen können. Der wichtige Teil sind die beiden ifAnweisungen, die synchron ausgeführt werden.

array_key_exists()und in_array()sind PHP-Funktionen.

Quelle:

Das Schöne an dem Algorithmus , dass ich oben zeigte , ist , dass Sie einen REST - Endpunkt wie machen können GET /in-use/{table}/{column}/{value}(wo table, columnund valuesind Variablen).

Du könntest haben:

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

und dann könnten Sie GET-Anfragen stellen wie:

GET /in-use/accounts/account_name/Bob's Drywall (Möglicherweise müssen Sie den letzten Teil uri-codieren, normalerweise jedoch nicht.)

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

GET /in-use/users/email/[email protected]

Beachten Sie auch, dass niemand tun kann:

GET /in-use/users/password/dogmeat1337weil passwordist nicht in Ihrer Liste der zulässigen Spalten für aufgeführt user.

Viel Glück auf Ihrer Reise.

agm1984
quelle
Ich habe keine Ahnung, was dies mit der Frage zu tun hat, aber ich habe einen Blick darauf geworfen und: Ich hoffe wirklich, dass Sie NIEMALS dynamische Daten verwenden $SEARCHABLE_TABLE_COLUMNS! Dies schreit nach einer Injektion - egal ob es einen "ultra sicheren Framework-Abfrage-Builder" gibt, der versucht, Tabellen- und Spaltenzeichenfolgen zu maskieren und zu filtern! Am Ende können Tabellen- und Spaltenzeichenfolgen nicht über Platzhalter (vorbereitete Anweisungen) hinzugefügt werden und müssen direkt wie eingefügt werden SELECT ... FROM {$table} WHERE {$column} = :placeholder ..... Ofc hängt von Adaptern ab (MySQL, Mongo, ...), ABER das ist kein Argument, um sicher zu sein! Pls statische oder keine Liste =)
Cottton