Vérifier si les éléments d'un tableau sont dans un autre tableau en PHP

130

J'ai deux tableaux en PHP comme suit:

Gens:

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

Criminels recherchés:

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
)

Comment vérifier si l' un des éléments People se trouve dans le tableau Wanted Criminals ?

Dans cet exemple, il devrait revenir truecar il 20est dans Wanted Criminals .

Philip Morton
la source

Réponses:

204

Vous pouvez utiliser array_intersect().

$result = !empty(array_intersect($people, $criminals));
Greg
la source
8
Impossible d'utiliser empty () avec autre chose qu'une variable.
grantwparks
@grantwparks alors pourquoi dans la documentation PHP à propos de cette fonction, ils disent: "Renvoie FALSE si var existe et a une valeur non vide, non nulle. Sinon, renvoie TRUE. Les choses suivantes sont considérées comme vides: array () (un tableau vide ) "? Source: php.net/manual/en/function.empty.php
Pere
5
À partir de la page à laquelle vous avez lié: "Avant PHP 5.5, empty () ne prend en charge que les variables; tout le reste entraînera une erreur d'analyse. En d'autres termes, ce qui suit ne fonctionnera pas: empty (trim ($ name)). Au lieu de cela, utilisez trim ($ name) == false. "
grantwparks
9
Comme mentionné dans les commentaires, j'ai trouvé que !empty cela ne fonctionne pas comme prévu . Au lieu de cela, j'ai utilisé count():!count(array_intersect($people, $criminals));
Mattios550
3
Pourquoi est-ce marqué comme la réponse avec 65 voix en plus quand il lève Erreur fatale: Impossible d'utiliser la valeur de retour de fonction dans le contexte d'écriture?
Dave Heq
31

Il y a peu de mal à utiliser array_intersect () et count () (au lieu de empty).

Par exemple:

$bFound = (count(array_intersect($criminals, $people))) ? true : false;
papsy
la source
2
Il n'y a rien de mal à cela mais count()n'est pas considéré comme performant (si vous vous souciez de la micro-optimisation, c'est-à-dire)
Jake A. Smith
L'opérateur ternaire est redondant dans ce code, vous pouvez simplement l'analyser comme un booléen, en appelant (bool) devant count
Ben Gooding il y a
23

si «vide» n'est pas le meilleur choix, qu'en est-il de ceci:

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

ou

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

Ce code n'est pas valide car vous ne pouvez passer des variables que dans des constructions de langage. empty()est une construction de langage.

Vous devez le faire en deux lignes:

$result = array_intersect($people, $criminals);
$result = !empty($result);
Paul Dragoonis
la source
Le problème n'est pas que ce soit une construction de langage. Le problème est qu'il attend une référence et que Greg passe une valeur.
Artefacto
1
@Artefacto, du php.net "Remarque: Comme il s'agit d'une construction de langage et non d'une fonction, elle ne peut pas être appelée à l'aide de fonctions variables." C'est exactement comme Paul l'a dit.
grantwparks
17

Test de performances pour 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;

Voici les résultats:

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 est au moins 5 fois plus rapide. Notez que nous "cassons" dès qu'un résultat est trouvé.

Frank Forte
la source
Merci pour la référence. Donc, si vous savez que vous manipulez de petites baies, il vaut mieux rester avec array_intersect().
Tokeeen.com
issetest encore plus rapide. Et vous pouvez utiliser bool val pour activer ou désactiver. Les valeurs de recherche comme clé garantissent également qu'il n'y a pas de doublons. ´array_intersect moy: 0,52077736854553; in_array avg: 0,015597295761108; isset avg: 0,0077081203460693´
cottton
1

Vous pouvez également utiliser in_array comme suit:

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

Bien que array_intersect soit certainement plus pratique à utiliser, il s'avère que ce n'est pas vraiment supérieur en termes de performances. J'ai aussi créé ce 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) }

Ensuite, j'ai exécuté les deux extraits respectivement sur: http://3v4l.org/WGhO7/perf#tabs et http://3v4l.org/g1Hnu/perf#tabs et vérifié les performances de chacun. La chose intéressante est que le temps CPU total, c'est-à-dire temps utilisateur + temps système est le même pour PHP5.6 et la mémoire est également la même. Le temps CPU total sous PHP5.4 est moindre pour in_array que pour array_intersect, bien que marginalement.

slevy1
la source
Les résultats sont trompeurs. L'exécuter une seule fois est trop rapide pour mesurer une différence. Si vous avez des centaines ou des milliers de demandes par seconde, ces fractions de seconde s'additionnent rapidement, donc si vous pensez que votre application doit évoluer, je m'en tiendrai à l' in_arrayimplémentation.
Frank Forte
1

Voici une façon de le faire après avoir fait des recherches pendant un moment. Je voulais créer un point de terminaison d'API Laravel qui vérifie si un champ est "en cours d'utilisation", les informations importantes sont donc: 1) quelle table DB? 2) quelle colonne DB? et 3) y a-t-il une valeur dans cette colonne qui correspond aux termes de recherche?

Sachant cela, nous pouvons construire notre tableau associatif:

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

Ensuite, nous pouvons définir nos valeurs que nous vérifierons:

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

Ensuite, nous pouvons utiliser array_key_exists()et in_array()avec eachother pour exécuter un combo en une, deux étapes, puis agir sur la truthycondition:

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

Je m'excuse pour le code PHP spécifique à Laravel, mais je vais le laisser car je pense que vous pouvez le lire sous forme de pseudo-code. La partie importante est les deux ifinstructions qui sont exécutées de manière synchrone.

array_key_exists()et in_array()sont des fonctions PHP.

la source:

La bonne chose à propos de l'algorithme que j'ai montré ci - dessus est que vous pouvez faire un point de terminaison REST tels que GET /in-use/{table}/{column}/{value}(où table, columnet valuesont des variables).

Tu aurais pu:

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

puis vous pouvez faire des requêtes GET telles que:

GET /in-use/accounts/account_name/Bob's Drywall (vous devrez peut-être encoder uri la dernière partie, mais généralement pas)

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

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

Notez également que personne ne peut faire:

GET /in-use/users/password/dogmeat1337car passwordn'est pas répertorié dans votre liste de colonnes autorisées pour user.

Bonne chance pour ton voyage.

agm1984
la source
Je n'ai aucune idée de ce que cela a à voir avec la question mais j'ai jeté un coup d'œil et: j'espère vraiment que vous n'utiliserez JAMAIS de données dynamiques $SEARCHABLE_TABLE_COLUMNS! Cela crie pour une injection - peu importe s'il y a un "générateur de requêtes de cadre ultra sécurisé" entre qui tente de masquer et de filtrer les chaînes de table et de colonne! À la fin de la table et des colonnes, les chaînes ne peuvent pas être ajoutées via un espace réservé (instructions préparées) et doivent être insérées directement comme SELECT ... FROM {$table} WHERE {$column} = :placeholder ..... Ofc dépend des adaptateurs (mysql, mongo, ...) MAIS ce n'est pas un argument à sauvegarder! Pls statique ou pas de liste =)
cottton