Comment vérifier si plusieurs clés de tableau existent


87

J'ai une variété de tableaux qui contiendront soit

story & message

ou juste

story

Comment vérifier si un tableau contient à la fois une histoire et un message? array_key_exists()recherche uniquement cette clé unique dans le tableau.

Y a-t-il un moyen de faire cela?


2
Si "histoire" sera présente dans les deux cas, il semblerait que vous ayez juste besoin de vérifier "message".
Wyzard

5
À l'aide de array_intersect_key()comparer un tableau des clés que vous souhaitez vérifier avec le tableau que vous vérifiez. Si la longueur de la sortie est la même que le tableau de clés à vérifier, elles sont toutes présentes.
Michael Berkowski

Wyzard, j'ai d'autres tableaux qui contiennent un message, mais pas une histoire, mais ceux-ci ont d'autres clés qu'un tableau contenant soit une histoire, soit une histoire et un message ne contiendrait que. Merci
Ryan

Vous confondez les clés et les valeurs ici? Le tableau ["story & message" => "value"]est-il au format ou est-il plus semblable à["story & message"]
GordonM

Réponses:


69

Si vous n'avez que 2 clés à vérifier (comme dans la question d'origine), il est probablement assez facile d'appeler array_key_exists()deux fois pour vérifier si les clés existent.

if (array_key_exists("story", $arr) && array_key_exists("message", $arr)) {
    // Both keys exist.
}

Cependant, cela ne s'adapte évidemment pas bien à de nombreuses clés. Dans cette situation, une fonction personnalisée aiderait.

function array_keys_exists(array $keys, array $arr) {
   return !array_diff_key(array_flip($keys), $arr);
}

3
Si les gens pensent que les autres solutions sont meilleures pour vérifier si un tableau a deux membres présents, ils ne doivent pas aimer le code clair ou les performances lisibles :)
alex

C'est probablement la solution la plus simple si vos clés requises sont relativement peu nombreuses. If deviendra illisible s'il s'agit de quelque chose comme 20 ou 30.
apokryfos

1
@apokryfos D'accord, mais cela répond à la question du PO.
alex

2
@alex le seul problème est que si $keyscontient un élément qui n'est pas$arr dedans et un autre qui y est, !array_diff_keyrenvoie vide => false( exemple 3v4l ) ...
CPHPython

3
Je pense que cela peut être rendu plus lisible en utilisant, !array_diff($keys, array_keys($array));car il y a un peu moins de charge cognitive impliquée dans l'élaboration de ces array_flips.
moopet

193

Voici une solution évolutive, même si vous souhaitez rechercher un grand nombre de clés:

<?php

// The values in this arrays contains the names of the indexes (keys) 
// that should exist in the data array
$required = array('key1', 'key2', 'key3');

$data = array(
    'key1' => 10,
    'key2' => 20,
    'key3' => 30,
    'key4' => 40,
);

if (count(array_intersect_key(array_flip($required), $data)) === count($required)) {
    // All required keys exist!
}

Je voudrais savoir la raison pour laquelle cela a été déclassé ... afaik c'est plus rapide car array_intersect_key est implémenté en C et vous n'aurez pas besoin d'une boucle
Erfan

Assez intelligent en fait, bien fait - bien qu'un peu difficile à lire.
Jon z

Merci :) C'est étrange que PHP n'ait pas de fonction intégrée pour faire cela - c'est assez courant. Il y a des tonnes de classes de validation d'entrée utilisateur qui font cela, mais pour la plupart des cas d'utilisation, c'est exagéré
Erfan

12
Solution astucieuse en effet mais c'est vraiment plus lent (environ 50% plus lent sur ma box) qu'un simple: `` `` $ ok = true; foreach ($ requis comme $ field) {if (! array_key_exists ($ field, $ data)) $ ok = false; }
Ozh

@Ozh à part que array_key_exists est plus lent que isset
iautomation

34

Étonnamment array_keys_existn'existe pas?! En attendant, cela laisse un peu d'espace pour trouver une expression sur une seule ligne pour cette tâche courante. Je pense à un script shell ou à un autre petit programme.

Remarque: chacune des solutions suivantes utilise la […]syntaxe de déclaration de tableau concise disponible dans php 5.4+

array_diff + array_keys

if (0 === count(array_diff(['story', 'message', '…'], array_keys($source)))) {
  // all keys found
} else {
  // not all
}

(pointe du chapeau à Kim Stacks )

Cette approche est la plus brève que j'ai trouvée. array_diff()renvoie un tableau d'éléments présents dans l'argument 1 non présents dans l'argument2. Par conséquent, un tableau vide indique que toutes les clés ont été trouvées. En php 5.5, vous pourriez simplifier 0 === count(…)pour être simplement empty(…).

array_reduce + unset

if (0 === count(array_reduce(array_keys($source), 
    function($in, $key){ unset($in[array_search($key, $in)]); return $in; }, 
    ['story', 'message', '…'])))
{
  // all keys found
} else {
  // not all
}

Plus difficile à lire, facile à changer. array_reduce()utilise un rappel pour parcourir un tableau pour arriver à une valeur. En alimentant les clés, nous nous intéressons à la $initialvaleur de $in, puis en supprimant les clés trouvées dans la source, nous pouvons nous attendre à terminer avec 0 élément si toutes les clés ont été trouvées.

La construction est facile à modifier car les clés qui nous intéressent s'adaptent bien à la ligne du bas.

array_filter & in_array

if (2 === count(array_filter(array_keys($source), function($key) { 
        return in_array($key, ['story', 'message']); }
    )))
{
  // all keys found
} else {
  // not all
}

Plus simple à écrire que la array_reducesolution mais légèrement plus compliqué à modifier. array_filterest également un rappel itératif qui vous permet de créer un tableau filtré en retournant true (copier l'élément dans un nouveau tableau) ou false (ne pas copier) dans le rappel. Le gotchya est que vous devez changer 2le nombre d'articles que vous attendez.

Cela peut être rendu plus durable mais à la limite de la lisibilité absurde:

$find = ['story', 'message'];
if (count($find) === count(array_filter(array_keys($source), function($key) use ($find) { return in_array($key, $find); })))
{
  // all keys found
} else {
  // not all
}

3
la différence sera négligeable pour les petits ensembles. si vous écrivez une bibliothèque / structure qui gère de grands ensembles de données, vous devriez probablement tester les performances de chaque unité pour trouver les goulots d'étranglement plutôt que d'optimiser prématurément.
Mark Fox

16

Il me semble que la méthode la plus simple serait de loin la suivante:

$required = array('a','b','c','d');

$values = array(
    'a' => '1',
    'b' => '2'
);

$missing = array_diff_key(array_flip($required), $values);

Impressions:

Array(
    [c] => 2
    [d] => 3
)

Cela permet également de vérifier quelles clés manquent exactement. Cela peut être utile pour la gestion des erreurs.


C'est pour ça que je suis venu ici!
eNeMetcH

8

Une autre solution possible:

if (!array_diff(['story', 'message'], array_keys($array))) {
    // OK: all the keys are in $array
} else {
   // FAIL: some keys are not
}

7

Les solutions ci-dessus sont intelligentes, mais très lentes. Une simple boucle foreach avec isset est plus de deux fois plus rapide que la array_intersect_keysolution.

function array_keys_exist($keys, $array){
    foreach($keys as $key){
        if(!array_key_exists($key, $array))return false;
    }
    return true;
}

(344 ms contre 768 ms pour 1000000 itérations)


isset renverra false si ['key' => null] et parfois vous avez des tableaux avec des valeurs nulles. Vous devriez utiliser array_key_exists à la place isset
j4r3k

J'ai dû utiliser le contraire ici à cause du retour prématuré avec false( falseremplace truedans ce cas). Donc, ce qui fonctionne pour mes besoins, c'est que foreach ($keys as $key) { if (array_key_exists($key, $array)) { return true; }} return false;mes besoins étaient si la anyclé d'un tableau existe dans un autre tableau ...
Geoff

1
Je n'appellerais pas +/- 400ms sur un million de touches "très lentes", mais je ne suis qu'humain!
colonelclick le

3

Si vous avez quelque chose comme ça:

$stuff = array();
$stuff[0] = array('story' => 'A story', 'message' => 'in a bottle');
$stuff[1] = array('story' => 'Foo');

Vous pouvez simplement count():

foreach ($stuff as $value) {
  if (count($value) == 2) {
    // story and message
  } else {
    // only story
  }
}

Cela ne fonctionne que si vous savez avec certitude que vous avez UNIQUEMENT ces clés de tableau, et rien d'autre.

L'utilisation de array_key_exists () ne prend en charge que la vérification d'une clé à la fois, vous devrez donc vérifier les deux séparément:

foreach ($stuff as $value) {
  if (array_key_exists('story', $value) && array_key_exists('message', $value) {
    // story and message
  } else {
    // either one or both keys missing
  }
}

array_key_exists()renvoie true si la clé est présente dans le tableau, mais c'est une fonction réelle et beaucoup à taper. La construction du langage isset()fera presque la même chose, sauf si la valeur testée est NULL:

foreach ($stuff as $value) {
  if (isset($value['story']) && isset($value['message']) {
    // story and message
  } else {
    // either one or both keys missing
  }
}

De plus, isset permet de vérifier plusieurs variables à la fois:

foreach ($stuff as $value) {
  if (isset($value['story'], $value['message']) {
    // story and message
  } else {
    // either one or both keys missing
  }
}

Maintenant, pour optimiser le test des éléments définis, vous feriez mieux d'utiliser ce "si":

foreach ($stuff as $value) {
  if (isset($value['story']) {
    if (isset($value['message']) {
      // story and message
    } else {
      // only story
    }
  } else {
    // No story - but message not checked
  }
}

3

Et ça:

isset($arr['key1'], $arr['key2']) 

ne renvoie true que si les deux ne sont pas nuls

si est nul, la clé n'est pas dans le tableau


1
si la valeur de $arr['key1']ou $arr['key2']est null, le code sera, la clé existe toujours.
Xorifelse le

J'ai écrit un test s'il vous plaît regardez le test @Xorifelse et corrigez-moi si je me trompe. FYI: cette fois, je ne connaissais que la version PHP 5.6. * Donc je ne l'ai fait que pour cela.
David Dutkovsky

Qu'est-ce que ce code essaie d'accomplir? Pourquoi n'utilisez-vous pas simplement une foreachboucle?
Xorifelse

Je voulais ajouter une preuve que la issetfonction fonctionne comme je l'entendais, mais maintenant je réalise que vous aviez raison, les clés restent toujours dans un tableau et donc ma réponse n'est pas correcte, merci pour vos commentaires. Oui, je pourrais l'utiliser foreach.
David Dutkovsky

3

J'utilise quelque chose comme ça assez souvent

$wantedKeys = ['story', 'message'];
$hasWantedKeys = count(array_intersect(array_keys($source), $wantedKeys)) > 0

ou pour trouver les valeurs des clés recherchées

$wantedValues = array_intersect_key($source, array_fill_keys($wantedKeys, 1))

2

essaye ça

$required=['a','b'];$data=['a'=>1,'b'=>2];
if(count(array_intersect($required,array_keys($data))>0){
    //a key or all keys in required exist in data
 }else{
    //no keys found
  }

1

C'est la fonction que j'ai écrite pour moi-même à utiliser dans une classe.

<?php
/**
 * Check the keys of an array against a list of values. Returns true if all values in the list
 is not in the array as a key. Returns false otherwise.
 *
 * @param $array Associative array with keys and values
 * @param $mustHaveKeys Array whose values contain the keys that MUST exist in $array
 * @param &$missingKeys Array. Pass by reference. An array of the missing keys in $array as string values.
 * @return Boolean. Return true only if all the values in $mustHaveKeys appear in $array as keys.
 */
    function checkIfKeysExist($array, $mustHaveKeys, &$missingKeys = array()) {
        // extract the keys of $array as an array
        $keys = array_keys($array);
        // ensure the keys we look for are unique
        $mustHaveKeys = array_unique($mustHaveKeys);
        // $missingKeys = $mustHaveKeys - $keys
        // we expect $missingKeys to be empty if all goes well
        $missingKeys = array_diff($mustHaveKeys, $keys);
        return empty($missingKeys);
    }


$arrayHasStoryAsKey = array('story' => 'some value', 'some other key' => 'some other value');
$arrayHasMessageAsKey = array('message' => 'some value', 'some other key' => 'some other value');
$arrayHasStoryMessageAsKey = array('story' => 'some value', 'message' => 'some value','some other key' => 'some other value');
$arrayHasNone = array('xxx' => 'some value', 'some other key' => 'some other value');

$keys = array('story', 'message');
if (checkIfKeysExist($arrayHasStoryAsKey, $keys)) { // return false
    echo "arrayHasStoryAsKey has all the keys<br />";
} else {
    echo "arrayHasStoryAsKey does NOT have all the keys<br />";
}

if (checkIfKeysExist($arrayHasMessageAsKey, $keys)) { // return false
    echo "arrayHasMessageAsKey has all the keys<br />";
} else {
    echo "arrayHasMessageAsKey does NOT have all the keys<br />";
}

if (checkIfKeysExist($arrayHasStoryMessageAsKey, $keys)) { // return false
    echo "arrayHasStoryMessageAsKey has all the keys<br />";
} else {
    echo "arrayHasStoryMessageAsKey does NOT have all the keys<br />";
}

if (checkIfKeysExist($arrayHasNone, $keys)) { // return false
    echo "arrayHasNone has all the keys<br />";
} else {
    echo "arrayHasNone does NOT have all the keys<br />";
}

Je suppose que vous devez vérifier plusieurs clés TOUTES EXISTENT dans un tableau. Si vous recherchez une correspondance d'au moins une clé, faites-le moi savoir afin que je puisse fournir une autre fonction.

Codepad ici http://codepad.viper-7.com/AKVPCH


1
La solution est bonne mais il y a une belle gemme d'une ligne enterrée:if (0 === count(array_diff(['key1','key2','key3'], array_keys($lookIn)))) { // all keys exist } else { // nope }
Mark Fox

Ce que vous écrivez est vrai. Je trouve ma fonction plus lisible mais verbeuse. Bien sûr, je peux me tromper. Merci d'avoir commenté ma réponse. J'apprends quelque chose de nouveau.
Kim Stacks

1

J'espère que cela t'aides:

function array_keys_exist($searchForKeys = array(), $inArray = array()) {
    $inArrayKeys = array_keys($inArray);
    return count(array_intersect($searchForKeys, $inArrayKeys)) == count($searchForKeys); 
}

1

Ceci est vieux et sera probablement enterré, mais c'est ma tentative.

J'ai eu un problème similaire à @Ryan. Dans certains cas, je devais vérifier uniquement si au moins 1 clé était dans un tableau, et dans certains cas, tout était nécessaire être présentes.

J'ai donc écrit cette fonction:

/**
 * A key check of an array of keys
 * @param array $keys_to_check An array of keys to check
 * @param array $array_to_check The array to check against
 * @param bool $strict Checks that all $keys_to_check are in $array_to_check | Default: false
 * @return bool
 */
function array_keys_exist(array $keys_to_check, array $array_to_check, $strict = false) {
    // Results to pass back //
    $results = false;

    // If all keys are expected //
    if ($strict) {
        // Strict check //

        // Keys to check count //
        $ktc = count($keys_to_check);
        // Array to check count //
        $atc = count(array_intersect($keys_to_check, array_keys($array_to_check)));

        // Compare all //
        if ($ktc === $atc) {
            $results = true;
        }
    } else {
        // Loose check - to see if some keys exist //

        // Loop through all keys to check //
        foreach ($keys_to_check as $ktc) {
            // Check if key exists in array to check //
            if (array_key_exists($ktc, $array_to_check)) {
                $results = true;
                // We found at least one, break loop //
                break;
            }
        }
    }

    return $results;
}

C'était beaucoup plus facile que d'avoir à écrire plusieurs blocs ||et &&.


0

Cela ne marche pas?

array_key_exists('story', $myarray) && array_key_exists('message', $myarray)

2
Les constantes ne peuvent pas être des tableaux ... :)
Sven

J'oublie toujours le $ quand je n'écris pas dans mon super code vérifiant l'IDE de saisie semi-automatique. =)
Kiwi

0
<?php

function check_keys_exists($keys_str = "", $arr = array()){
    $return = false;
    if($keys_str != "" and !empty($arr)){
        $keys = explode(',', $keys_str);
        if(!empty($keys)){
            foreach($keys as $key){
                $return = array_key_exists($key, $arr);
                if($return == false){
                    break;
                }
            }
        }
    }
    return $return;
}

// lancer la démo

$key = 'a,b,c';
$array = array('a'=>'aaaa','b'=>'ccc','c'=>'eeeee');

var_dump( check_keys_exists($key, $array));

0

Je ne suis pas sûr, si c'est une mauvaise idée, mais j'utilise une boucle foreach très simple pour vérifier plusieurs clés de tableau.

// get post attachment source url
$image     = wp_get_attachment_image_src(get_post_thumbnail_id($post_id), 'single-post-thumbnail');
// read exif data
$tech_info = exif_read_data($image[0]);

// set require keys
$keys = array('Make', 'Model');

// run loop to add post metas foreach key
foreach ($keys as $key => $value)
{
    if (array_key_exists($value, $tech_info))
    {
        // add/update post meta
        update_post_meta($post_id, MPC_PREFIX . $value, $tech_info[$value]);
    }
} 

0
// sample data
$requiredKeys = ['key1', 'key2', 'key3'];
$arrayToValidate = ['key1' => 1, 'key2' => 2, 'key3' => 3];

function keysExist(array $requiredKeys, array $arrayToValidate) {
    if ($requiredKeys === array_keys($arrayToValidate)) {
        return true;
    }

    return false;
}

0
$myArray = array('key1' => '', 'key2' => '');
$keys = array('key1', 'key2', 'key3');
$keyExists = count(array_intersect($keys, array_keys($myArray)));

Renvoie true, car il y a des clés du tableau $ keys dans $ myArray


0

Quelque chose comme cela pourrait être utilisé

//Say given this array
$array_in_use2 = ['hay' => 'come', 'message' => 'no', 'story' => 'yes'];
//This gives either true or false if story and message is there
count(array_intersect(['story', 'message'], array_keys($array_in_use2))) === 2;

Notez la vérification contre 2, si les valeurs que vous souhaitez rechercher sont différentes, vous pouvez les modifier.

Cette solution n'est peut-être pas efficace, mais elle fonctionne!

Mises à jour

Dans une fonction de graisse :

 /**
 * Like php array_key_exists, this instead search if (one or more) keys exists in the array
 * @param array $needles - keys to look for in the array
 * @param array $haystack - the <b>Associative</b> array to search
 * @param bool $all - [Optional] if false then checks if some keys are found
 * @return bool true if the needles are found else false. <br>
 * Note: if hastack is multidimentional only the first layer is checked<br>,
 * the needles should <b>not be<b> an associative array else it returns false<br>
 * The array to search must be associative array too else false may be returned
 */
function array_keys_exists($needles, $haystack, $all = true)
{
    $size = count($needles);
    if($all) return count(array_intersect($needles, array_keys($haystack))) === $size;
    return !empty(array_intersect($needles, array_keys($haystack)));

}

Donc par exemple avec ceci:

$array_in_use2 = ['hay' => 'come', 'message' => 'no', 'story' => 'yes'];
//One of them exists --> true
$one_or_more_exists = array_keys_exists(['story', 'message'], $array_in_use2, false);
//all of them exists --> true
$all_exists = array_keys_exists(['story', 'message'], $array_in_use2);

J'espère que cela t'aides :)


0

J'utilise généralement une fonction pour valider mon message et c'est aussi une réponse à cette question, alors laissez-moi le poster.

pour appeler ma fonction, j'utiliserai le tableau 2 comme ceci

validatePost(['username', 'password', 'any other field'], $_POST))

alors ma fonction ressemblera à ceci

 function validatePost($requiredFields, $post)
    {
        $validation = [];

        foreach($requiredFields as $required => $key)
        {
            if(!array_key_exists($key, $post))
            {
                $validation['required'][] = $key;
            }
        }

        return $validation;
    }

cela affichera ceci

"obligatoire": ["nom d'utilisateur", "mot de passe", "tout autre champ"]

donc ce que fait cette fonction est de valider et de renvoyer tous les champs manquants de la demande de publication.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.