Question Comment vérifier si le tableau PHP est associatif ou séquentiel?


PHP traite tous les tableaux comme associatifs, donc il n'y a pas de fonctions intégrées. Quelqu'un peut-il recommander un moyen assez efficace de vérifier si un tableau ne contient que des clés numériques?

Fondamentalement, je veux être capable de faire la différence entre ceci:

$sequentialArray = array('apple', 'orange', 'tomato', 'carrot');

et ça:

$assocArray = array('fruit1' => 'apple', 
                    'fruit2' => 'orange', 
                    'veg1' => 'tomato', 
                    'veg2' => 'carrot');

662


origine


Réponses:


Vous avez posé deux questions qui ne sont pas tout à fait équivalentes:

  • Premièrement, comment déterminer si un tableau a seulement des clés numériques
  • Deuxièmement, comment déterminer si un tableau a séquentiel touches numériques, à partir de 0

Considérez lequel de ces comportements vous avez réellement besoin. (Peut-être que l'un ou l'autre fera pour vos fins.)

La première question (vérifier simplement que toutes les clés sont numériques) est bien répondu par le capitaine Kuro.

Pour la deuxième question (vérifier si le tableau est indexé et séquentiel), vous pouvez utiliser la fonction suivante:

function isAssoc(array $arr)
{
    if (array() === $arr) return false;
    return array_keys($arr) !== range(0, count($arr) - 1);
}

var_dump(isAssoc(array('a', 'b', 'c'))); // false
var_dump(isAssoc(array("0" => 'a', "1" => 'b', "2" => 'c'))); // false
var_dump(isAssoc(array("1" => 'a', "0" => 'b', "2" => 'c'))); // true
var_dump(isAssoc(array("a" => 'a', "b" => 'b', "c" => 'c'))); // true

497



Pour vérifier simplement si le tableau a des clés non-entières (pas si le tableau est indexé séquentiellement ou indexé par zéro):

function has_string_keys(array $array) {
  return count(array_filter(array_keys($array), 'is_string')) > 0;
}

S'il y a au moins une clé de chaîne, $array sera considéré comme un tableau associatif.


388



Sûrement c'est une meilleure alternative.

<?php
$arr = array(1,2,3,4);
$isIndexed = array_values($arr) === $arr;

122



Beaucoup de commentateurs dans cette question ne comprennent pas comment les tableaux fonctionnent en PHP. Du documentation de groupe:

Une clé peut être un entier ou une chaîne. Si une clé est la représentation standard d'un entier, elle sera interprétée comme telle (à savoir "8" sera interprété comme 8, tandis que "08" sera interprété comme "08"). Les flottants dans la clé sont tronqués à l'entier. Les types de tableaux indexés et associatifs sont du même type en PHP, qui peuvent tous deux contenir des indices de type entier et de type chaîne.

En d'autres termes, il n'existe pas de clé de tableau "8", car elle sera toujours (silencieusement) convertie en entier 8. Il est donc inutile d'essayer de différencier les entiers et les chaînes numériques.

Si vous voulez le moyen le plus efficace de vérifier un tableau pour des clés non entières sans faire une copie d'une partie du tableau (comme le fait array_keys ()) ou tout (comme foreach):

for (reset($my_array); is_int(key($my_array)); next($my_array));
$onlyIntKeys = is_null(key($my_array));

Cela fonctionne car key () renvoie NULL lorsque la position actuelle du tableau est invalide et NULL ne peut jamais être une clé valide (si vous essayez d'utiliser NULL comme une clé de tableau, il est converti en "").


73



Comme déclaré par l'OP:

PHP traite tous les tableaux comme associatifs

il n'est pas tout à fait raisonnable (IMHO) d'écrire une fonction qui vérifie si un tableau est associatif. Donc, première chose d'abord: qu'est-ce qu'une clé dans un tableau PHP?:

le clé peut être soit un entier ou un chaîne.

Cela signifie qu'il y a 3 cas possibles:

  • Cas 1. toutes les clés sont numérique / entiers.
  • Cas 2. toutes les clés sont cordes.
  • Cas 3. quelques clés sont cordes, certaines touches sont numérique / entiers.

Nous pouvons vérifier chaque cas avec les fonctions suivantes.

Cas 1: toutes les clés sont numérique / entiers.

Remarque: Cette fonction renvoie vrai pour les tableaux vides aussi.

//! Check whether the input is an array whose keys are all integers.
/*!
    \param[in] $InputArray          (array) Input array.
    \return                         (bool) \b true iff the input is an array whose keys are all integers.
*/
function IsArrayAllKeyInt($InputArray)
{
    if(!is_array($InputArray))
    {
        return false;
    }

    if(count($InputArray) <= 0)
    {
        return true;
    }

    return array_unique(array_map("is_int", array_keys($InputArray))) === array(true);
}

Cas 2: toutes les clés sont cordes.

Remarque: Cette fonction renvoie vrai pour les tableaux vides aussi.

//! Check whether the input is an array whose keys are all strings.
/*!
    \param[in] $InputArray          (array) Input array.
    \return                         (bool) \b true iff the input is an array whose keys are all strings.
*/
function IsArrayAllKeyString($InputArray)
{
    if(!is_array($InputArray))
    {
        return false;
    }

    if(count($InputArray) <= 0)
    {
        return true;
    }

    return array_unique(array_map("is_string", array_keys($InputArray))) === array(true);
}

Cas 3. quelques clés sont cordes, certaines touches sont numérique / entiers.

Remarque: Cette fonction renvoie vrai pour les tableaux vides aussi.

//! Check whether the input is an array with at least one key being an integer and at least one key being a string.
/*!
    \param[in] $InputArray          (array) Input array.
    \return                         (bool) \b true iff the input is an array with at least one key being an integer and at least one key being a string.
*/
function IsArraySomeKeyIntAndSomeKeyString($InputArray)
{
    if(!is_array($InputArray))
    {
        return false;
    }

    if(count($InputArray) <= 0)
    {
        return true;
    }

    return count(array_unique(array_map("is_string", array_keys($InputArray)))) >= 2;
}

Il s'ensuit que:


Maintenant, pour qu'un tableau soit un "véritable" tableau auquel nous sommes tous habitués, c'est-à-dire:

  • Ses clés sont toutes numérique / entiers.
  • Ses clés sont séquentiel (c'est-à-dire en augmentant par l'étape 1).
  • Ses clés commencer à partir de zéro.

Nous pouvons vérifier avec la fonction suivante.

Cas 3a. les clés sont numérique / entiers, séquentiel, et à base zéro.

Remarque: Cette fonction renvoie vrai pour les tableaux vides aussi.

//! Check whether the input is an array whose keys are numeric, sequential, and zero-based.
/*!
    \param[in] $InputArray          (array) Input array.
    \return                         (bool) \b true iff the input is an array whose keys are numeric, sequential, and zero-based.
*/
function IsArrayKeyNumericSequentialZeroBased($InputArray)
{
    if(!is_array($InputArray))
    {
        return false;
    }

    if(count($InputArray) <= 0)
    {
        return true;
    }

    return array_keys($InputArray) === range(0, count($InputArray) - 1);
}

Mises en garde / pièges (ou, encore plus de faits particuliers sur les clés de tableau en PHP)

Clés entières

Les clés de ces tableaux sont entiers:

array(0 => "b");
array(13 => "b");
array(-13 => "b");          // Negative integers are also integers.
array(0x1A => "b");         // Hexadecimal notation.

Clés à corde

Les clés de ces tableaux sont cordes:

array("fish and chips" => "b");
array("" => "b");                                   // An empty string is also a string.
array("stackoverflow_email@example.com" => "b");    // Strings may contain non-alphanumeric characters.
array("stack\t\"over\"\r\nflow's cool" => "b");     // Strings may contain special characters.
array('$tα€køv∈rflöw' => "b");                    // Strings may contain all kinds of symbols.
array("functіon" => "b");                           // You think this looks fine? Think again! (see https://stackoverflow.com/q/9246051/1402846)
array("ま말轉转ДŁ" => "b");                         // How about Japanese/Korean/Chinese/Russian/Polish?
array("fi\x0sh" => "b");                            // Strings may contain null characters.
array(file_get_contents("https://www.google.com/images/nav_logo114.png") => "b");   // Strings may even be binary!

Les clés entières qui ressemblent à des chaînes

Si vous pensez que la clé array("13" => "b") est un chaîne, Vous avez tort. De la doc ici:

Les chaînes contenant des entiers valides seront converties en type entier. Par exemple. la clé "8" sera en réalité stockée sous 8. D'autre part "08" ne sera pas converti, car il ne s'agit pas d'un nombre décimal entier valide.

Par exemple, la clé de ces tableaux est entiers:

array("13" => "b");
array("-13" => "b");                        // Negative, ok.

Mais la clé pour ces tableaux sont cordes:

array("13." => "b");
array("+13" => "b");                        // Positive, not ok.
array("-013" => "b");
array("0x1A" => "b");                       // Not converted to integers even though it's a valid hexadecimal number.
array("013" => "b");                        // Not converted to integers even though it's a valid octal number.
array("18446744073709551616" => "b");       // Not converted to integers as it can't fit into a 64-bit integer.

De plus, selon le doc,

La taille d'un nombre entier dépend de la plateforme, bien qu'une valeur maximale d'environ deux milliards soit la valeur habituelle (c'est-à-dire 32 bits signés). Les plates-formes 64 bits ont généralement une valeur maximale d'environ 9E18, sauf pour Windows, qui est toujours de 32 bits. PHP ne supporte pas les entiers non signés.

Donc, la clé pour ce tableau peut-être ou peut-être pas haricot entier - Cela dépend de votre plateforme.

array("60000000000" => "b");                // Array key could be integer or string, it can fit into a 64-bit (but not 32-bit) integer.

Pire encore, PHP tend à être buggy si l'entier est proche du 231 = 2 147 483 648 frontière (voir bug 51430, bug 52899). Par exemple, sur mon environnement local (PHP 5.3.8 sur XAMPP 1.7.7 sous Windows 7), var_dump(array("2147483647" => "b")) donne

array(1) {
    [2147483647]=>
    string(1) "b"
}   

mais cette démo en direct sur le codepad (PHP 5.2.5), la même expression donne

array(1) {
    ["2147483647"]=>
    string(1) "b"
}

Donc la clé est un entier dans un environnement, mais un chaîne dans un autre, même si 2147483647 est un 32 bits signé valide entier.


36



Vitesse-sage:

function isAssoc($array)
{
    return ($array !== array_values($array));
}

Mémoire-sage:

function isAssoc($array)
{
    $array = array_keys($array); return ($array !== array_keys($array));
}

31



function checkAssoc($array){
    return  ctype_digit( implode('', array_keys($array) ) );
}

18



En fait, le moyen le plus efficace est donc:

function is_assoc($array){
   $keys = array_keys($array);
   return $keys !== array_keys($keys);
}

Cela fonctionne parce qu'il compare les clés (qui pour un tableau séquentiel sont toujours 0,1,2 etc) aux touches des touches (ce qui toujours être 0,1,2 etc.).


18



J'ai utilisé les deux array_keys($obj) !== range(0, count($obj) - 1) et array_values($arr) !== $arr (qui sont des duals les uns des autres, bien que le second soit moins cher que le premier) mais tous les deux échouent pour de très grands réseaux.

Ceci est dû au fait array_keys et array_values sont deux opérations très coûteuses (puisqu'elles construisent un tout nouveau tableau de taille à peu près celui de l'original).

La fonction suivante est plus robuste que les méthodes fournies ci-dessus:

function array_type( $obj ){
    $last_key = -1;
    $type = 'index';
    foreach( $obj as $key => $val ){
        if( !is_int( $key ) || $key < 0 ){
            return 'assoc';
        }
        if( $key !== $last_key + 1 ){
            $type = 'sparse';
        }
        $last_key = $key;
    }
    return $type;
}

Notez également que si vous ne voulez pas différencier les baies clairsemées des baies associatives, vous pouvez simplement retourner 'assoc' à la fois if des blocs.

Enfin, bien que cela puisse sembler beaucoup moins «élégant» que beaucoup de «solutions» sur cette page, dans la pratique, il est beaucoup plus efficace. Presque tout tableau associatif sera détecté instantanément. Seuls les tableaux indexés seront vérifiés de manière exhaustive, et les méthodes décrites ci-dessus non seulement vérifient exhaustivement les tableaux indexés, ils les dupliquent.


16



Je pense que les deux fonctions suivantes sont la meilleure façon de vérifier si un tableau est associatif ou numérique. Puisque «numérique» pourrait signifier seulement des clés numériques ou seulement des clés numériques séquentielles, deux fonctions sont énumérées ci-dessous qui vérifient l'une ou l'autre condition:

function is_indexed_array(&$arr) {
  for (reset($arr); is_int(key($arr)); next($arr));
  return is_null(key($arr));
}

function is_sequential_array(&$arr, $base = 0) {
  for (reset($arr), $base = (int) $base; key($arr) === $base++; next($arr));
  return is_null(key($arr));
}

La première fonction vérifie si chaque clé est une valeur entière. La deuxième fonction vérifie si chaque clé est une valeur entière et vérifie en outre si toutes les clés sont séquentielles à partir de $ base, par défaut à 0 et peuvent donc être omises si vous n'avez pas besoin de spécifier une autre valeur de base. key ($ my_array) renvoie null si le pointeur de lecture est déplacé après la fin du tableau, ce qui termine la boucle for et rend l'instruction après le retour de la boucle for vrai si toutes les clés étaient entières. Sinon, la boucle se termine prématurément car une clé est de type string, et l'instruction après la boucle for retournera false. La dernière fonction ajoute en plus une à $ base après chaque comparaison, pour pouvoir vérifier si la clé suivante est de la bonne valeur. La comparaison stricte permet également de vérifier si la clé est de type entier. La partie de base $ base = (int) $ dans la première section de la boucle for peut être omise lorsque $ base est omise ou si vous vous assurez qu'elle est seulement appelée en utilisant un entier. Mais comme je ne peux pas être sûr pour tout le monde, je l'ai laissé. La déclaration n'est exécutée qu'une seule fois, de toute façon. Je pense que ce sont les solutions les plus efficaces:

  • Mémoire: aucune copie des données ou des plages de touches. Faire une array_values ​​ou une array_keys peut sembler plus court (moins de code) mais gardez à l'esprit ce qui se passe en arrière-plan une fois que vous faites cet appel. Oui, il y a plus de déclarations (visibles) que dans d'autres solutions, mais ce n'est pas ce qui compte, n'est-ce pas?
  • Dans le temps: Outre le fait que copier / extraire des données et / ou des clés prend aussi du temps, cette solution est plus efficace que de faire une foreach. Encore une foreach peut sembler plus efficace à certains parce qu'elle est plus courte dans la notation, mais en arrière-plan foreach aussi les appels reset, key et à côté faire le looping. Mais en plus il appelle aussi valide pour vérifier la condition de fin, ce qui est évité ici en raison de la combinaison avec le contrôle d'entier.

Rappelez-vous qu'une clé de tableau ne peut être qu'un entier ou une chaîne, et qu'une chaîne strictement numérique telle que "1" (mais pas "01") sera traduite en entier. Ce qui rend la vérification d'une clé entière la seule opération nécessaire en plus de compter si vous voulez que le tableau soit séquentiel. Naturellement, si is_indexed_array renvoie false, le tableau peut être vu comme associatif. Je dis 'vu', parce qu'en fait ils le sont tous.


13