Verifica se gli elementi di un array sono in un altro array in PHP


130

Ho due array in PHP come segue:

Persone:

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

Ricercati criminali:

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
)

Come posso verificare se qualcuno degli elementi People si trova nell'array Wanted Criminals ?

In questo esempio, dovrebbe tornare trueperché 20è in Wanted Criminals .

Risposte:


204

È possibile utilizzare array_intersect().

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

8
Impossibile usare empty () con qualcosa di diverso da una variabile.
grantwparks,

@grantwparks allora perché nei documenti PHP su questa funzione dicono "Restituisce FALSE se var esiste e ha un valore non vuoto, diverso da zero. Altrimenti restituisce VERO. Le seguenti cose sono considerate vuote: array () (un array vuoto ) "? Fonte: php.net/manual/en/function.empty.php
Pere

5
Dalla pagina a cui ti sei collegato: "Prima di PHP 5.5, empty () supporta solo variabili; qualsiasi altra cosa comporterà un errore di analisi. In altre parole, non funzionerà: vuoto (trim ($ name)). Invece, usa trim ($ name) == false. "
grantwparks,

9
Come menzionato nei commenti, ho scoperto che !empty non funziona come previsto . Invece, ho usato count():!count(array_intersect($people, $criminals));
Mattios550 il

3
Perché questo è contrassegnato come risposta con 65 voti in più quando genera un errore irreversibile: non è possibile utilizzare il valore restituito dalla funzione nel contesto di scrittura?
Dave Heq,

31

C'è un piccolo errore nell'uso di array_intersect () e count () (anziché vuoto).

Per esempio:

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

2
Non c'è nulla di sbagliato in questo, ma count()non è considerato performante (se ti interessa la micro ottimizzazione, cioè)
Jake A. Smith

23

se 'vuoto' non è la scelta migliore, che dire di questo:

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

o

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

22

Quel codice non è valido poiché puoi solo passare variabili nei costrutti del linguaggio. empty()è un costrutto linguistico.

Devi farlo in due righe:

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

Il problema non è che è un costrutto linguistico. Il problema è che si aspetta un riferimento e Greg sta passando un valore.
Artefacto,

1
@Artefacto, da php.net "Nota: poiché si tratta di un costrutto di linguaggio e non di una funzione, non può essere chiamato utilizzando funzioni variabili." È esattamente come ha detto Paul.
grantwparks,

17

Test delle prestazioni per 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;

Ecco i risultati:

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 è almeno 5 volte più veloce. Nota che "rompiamo" non appena viene trovato un risultato.


Grazie per il benchmark. Quindi, se sai che stai gestendo piccoli array, è meglio restare con array_intersect().
Tokeeen.com,

issetè ancora più veloce. E potresti usare bool val per abilitare o disabilitare. Inoltre i valori di ricerca come chiave assicurano di non avere duplicati. ´array_intersect avg: 0.52077736854553; in_array avg: 0.015597295761108; isset avg: 0.0077081203460693´
cottton,

1

Puoi anche usare in_array come segue:

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

Mentre array_intersect è sicuramente più comodo da usare, si scopre che non è davvero superiore in termini di prestazioni. Ho creato anche questo script:

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

Quindi, ho eseguito entrambi i frammenti rispettivamente su: http://3v4l.org/WGhO7/perf#tabs e http://3v4l.org/g1Hnu/perf#tabs e ho verificato le prestazioni di ciascuno. La cosa interessante è che il tempo totale della CPU, ovvero il tempo dell'utente + il tempo di sistema è lo stesso per PHP5.6 e anche la memoria è la stessa. Il tempo totale della CPU in PHP5.4 è inferiore per in_array rispetto a array_intersect, anche se in modo marginale.


I risultati sono ingannevoli. Eseguirlo solo una volta è troppo veloce per misurare qualsiasi differenza. Se hai centinaia o migliaia di richieste al secondo, quelle frazioni di secondo si sommano rapidamente, quindi se pensi che la tua applicazione debba ridimensionarsi, mi atterrerei con l' in_arrayimplementazione.
Frank Forte,

1

Ecco un modo in cui lo sto facendo dopo averlo cercato per un po '. Volevo creare un endpoint API Laravel che controlla se un campo è "in uso", quindi le informazioni importanti sono: 1) quale tabella DB? 2) quale colonna DB? e 3) esiste un valore in quella colonna che corrisponde ai termini di ricerca?

Sapendo questo, possiamo costruire il nostro array associativo:

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

Quindi, possiamo impostare i nostri valori che controlleremo:

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

Quindi, possiamo usare array_key_exists()e in_array()vicendevolmente per eseguire una combinazione di uno, due passaggi e quindi agire sulla truthycondizione:

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

Mi scuso per il codice PHP specifico di Laravel, ma lo lascerò perché penso che tu possa leggerlo come pseudo-codice. La parte importante sono le due ifistruzioni che vengono eseguite in modo sincrono.

array_key_exists()e in_array()sono funzioni PHP.

fonte:

La cosa bella l'algoritmo che ho mostrato sopra è che si può fare un endpoint REST, come GET /in-use/{table}/{column}/{value}(dove table, columne valuesono variabili).

Potresti avere:

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

e quindi è possibile effettuare richieste GET come:

GET /in-use/accounts/account_name/Bob's Drywall (potrebbe essere necessario codificare uri l'ultima parte, ma di solito no)

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

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

Si noti inoltre che nessuno può fare:

GET /in-use/users/password/dogmeat1337perché passwordnon è elencato nel tuo elenco di colonne consentite per user.

Buona fortuna per il tuo viaggio


Non ho idea di cosa abbia a che fare con la domanda, ma ho dato un'occhiata e spero davvero che non utilizziate MAI i dati dinamici $SEARCHABLE_TABLE_COLUMNS! Questo urla per un'iniezione - non importa se c'è un "generatore di query framework ultra sicuro" tra quello che cerca di mascherare e filtrare le stringhe di tabella e colonna! Alla fine la tabella e le stringhe di colonna non possono essere aggiunte tramite segnaposto (istruzioni preparate) e devono essere inserite direttamente come SELECT ... FROM {$table} WHERE {$column} = :placeholder ..... Ofc dipende dagli adattatori (mysql, mongo, ...) MA questo non è un argomento da salvare! Pls statico o nessun elenco =)
cottton
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.