Comment vérifier si PHP array est associatif ou séquentiel?

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

en gros, je veux pouvoir différencier entre ceci:

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

et ceci:

$assocArray = array('fruit1' => 'apple', 
                    'fruit2' => 'orange', 
                    'veg1' => 'tomato', 
                    'veg2' => 'carrot');
681
demandé sur Wilco 2008-10-06 11:01:13

30 réponses

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

  • tout D'abord, comment déterminer si un tableau n'a que des clés numériques
  • Deuxièmement, comment déterminer si un tableau a clés numériques séquentielles , à partir de 0

tenez compte des comportements dont vous avez réellement besoin. (Il se peut que ce soit le fera pour vous.)

le 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é à zéro 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
515
répondu Mark Amery 2018-09-30 21:13:24

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

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.

393
répondu Captain kurO 2015-12-25 16:26:58

c'est Sûrement une meilleure alternative.

<?php
$arr = array(1,2,3,4);
$isIndexed = array_values($arr) === $arr;
122
répondu Dave Marshall 2012-06-21 13:20:37

beaucoup de commentateurs dans cette question ne comprennent pas comment les tableaux fonctionnent en PHP. De la documentation du tableau :

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 (c'est-à-dire que "8" sera interprété comme 8, tandis que "08" sera interprété comme "08"). Les flotteurs dans la clé sont tronqués à l'entier. Les types de tableaux indexés et associatifs sont le même type dans PHP, qui peut à la fois contenir des indices entiers et des indices de chaîne.

en d'autres termes, il n'y a pas de clé de tableau de" 8 " parce qu'elle sera toujours (silencieusement) convertie en entier 8. Donc essayer de faire la différence entre les entiers et les chaînes numériques est inutile.

Si vous voulez le moyen le plus efficace pour vérifier un tableau pour les non-entier touches sans faire une copie de la partie du tableau (comme array_keys ()) ou en totalité (comme foreach n'):

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

cela fonctionne parce que key() retourne NULL lorsque la position courante du tableau est invalide et NULL ne peut jamais être une clé valide (si vous essayez D'utiliser NULL comme une clé du tableau, elle est convertie silencieusement en"").

73
répondu squirrel 2011-05-11 19:34:45

Comme , a 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 en premier: qu'est-ce qu'une clé dans un tableau PHP ?:

la clé peut être soit une integer ou un chaîne .

cela signifie qu'il y a 3 cas possibles:

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

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

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

Note : cette fonction retourne 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);
}

Case 2: Toutes les clés sont strings .

Note : cette fonction retourne 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. certaines touches sont chaînes , certaines touches sont numérique / entiers .

Note : cette fonction retourne true 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, ce qui signifie:

  • ses clés sont toutes numérique / entiers .
  • ses clés sont séquentielle (c.-à-d. augmentation par étape 1).
  • ses clés départ de zéro .

, Nous pouvons vérifier avec la fonction suivante.

affaire 3a. les clés sont numérique / entiers , séquentielle , et base zéro .

Note : cette fonction retourne 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, faits encore plus particuliers sur les clés de tableau en PHP)

Integer "clés de 1519670920"

les clés pour 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 en Chaîne

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 /q/how-do-i-fix-this-missing-semicolon-syntax-error-in-javascript-51507/"ま말轉转ДŁ" => "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!

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

si vous pensez que la clé dans array("13" => "b") est une chaîne de caractères , vous avez tort . De la doc ici :

Les chaînes

contenant des entiers valides seront lancées vers le type entier. Par exemple: la clé " 8 " sera en fait stockée sous 8. D'un autre côté "08" ne sera pas moulé, car il n'est pas un entier décimal valide.

par exemple, la clé pour ces tableaux sont 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 entier dépend de la plate-forme, bien qu'une valeur maximale d'environ deux milliards soit la valeur habituelle (c'est-à-dire 32 bits signés). Plates-formes 64 bits habituellement ont 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 ou ne peut pas être un entier - cela dépend de votre plate-forme.

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

encore pire, PHP tend à être buggy si l'entier est proche de la 2 31 = 2,147,483,648 limite (voir bogue 51430 , bogue 52899 ). Par exemple, sur mon environnement local (PHP 5.3.8 sur XAMPP 1.7.7 sur Windows 7), var_dump(array("2147483647" => "b")) donne

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

mais sur cette démo live sur 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 une chaîne dans un autre, même si 2147483647 est une valide signée 32 bits entier .

36
répondu Pang 2017-05-23 10:31:37

vitesse:

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

de la Mémoire-sage:

function isAssoc($array)
{
    $array = array_keys($array); return ($array !== array_keys($array));
}
32
répondu Alix Axel 2011-08-06 17:39:10
function checkAssoc($array){
    return  ctype_digit( implode('', array_keys($array) ) );
}
18
répondu dsims 2010-03-15 02:14:41

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 clés des clés (qui sera toujours être 0,1,2 etc).

18
répondu 4 revs, 3 users 67%anon 2016-03-08 03:58:00

j'ai utilisé à la fois array_keys($obj) !== range(0, count($obj) - 1) et array_values($arr) !== $arr (qui sont des duos l'un de l'Autre, bien que le second soit moins cher que le premier) mais les deux échouent pour de très grands tableaux.

c'est parce que array_keys et array_values sont tous deux des opérations très coûteuses (puisqu'ils construisent un nouvel ensemble de taille à peu près celle 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 vous souciez pas de différencier les tableaux épars des tableaux associatifs, vous pouvez simplement retourner 'assoc' des deux blocs if .

Enfin, même si cela peut sembler beaucoup moins "élégant" que beaucoup de "solutions" sur cette page, dans la pratique, il est beaucoup plus efficace. Presque n'importe quel tableau associatif sera instantanément détecté. Seuls les tableaux indexés seront vérifiés de façon exhaustive, et les méthodes décrites ci-dessus ne vérifient pas seulement les tableaux indexés de façon exhaustive, ils font double emploi avec eux.

16
répondu podperson 2016-01-02 00:13:49

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 cochent l'une ou l'autre des conditions:

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 plus si toutes les clés sont séquentielles à partir de $base, qui est par défaut à 0 et peut donc être omis si vous n'avez pas besoin de spécifier une autre valeur de base. key ($my_array) retourne null si le pointeur de lecture est déplacé au-delà de la fin du tableau, qui est ce qui termine la boucle for et fait la déclaration après le retour de boucle for true si toutes les clés étaient entières. Si non, la boucle se termine prématurément parce qu'une clé est de type string, et l'instruction après la boucle for retournera false. Cette dernière fonction ajoute en plus un à $base après chaque comparaison, pour pouvoir vérifiez si la touche suivante a la bonne valeur. La comparaison stricte permet aussi de vérifier si la clé est de type entier. La partie $base = (int) $base dans la première section de la boucle for peut être omise lorsque $base est omis ou si vous vous assurez qu'il est seulement appelé en utilisant un entier. Mais comme je ne peux pas être sûr pour tout le monde, je l'ai laissé dedans. L'instruction est exécutée qu'une seule fois, de toute façon. Je pense que ce sont les solutions les plus efficaces:

  • Mémoire de sage: Pas de copie de données ou des gammes de clés. Faire un array_values ou un 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 d'énoncés (visibles) que dans d'autres solutions, mais ce n'est pas ce qui compte, n'est-ce pas?
  • time wise: outre le fait que copier/extraire des données et/ou des clés prend également du temps, Cette solution est plus efficace que de faire un foreach. Encore un foreach peut sembler plus efficace pour certains car il est plus court notation, mais en arrière-plan foreach appelle aussi reset, touche et à côté de faire il est en boucle. Mais en plus il appelle aussi valide pour vérifier la condition de fin, qui est évitée ici en raison de la combinaison avec le contrôle 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 un entier. Ce qui fait de la vérification d'une clé entière la seule opération nécessaire en plus de compter si vous voulez le tableau séquentiel. Naturellement, si is_indexed_array retourne false, le tableau peut être vu comme associatif. Je dis "vu", parce qu'en fait, ils le sont tous.

13
répondu Niels Ockeloen 2012-07-15 21:54:50

cette fonction peut prendre en charge:

  • tableau comportant des trous dans l'index (p.ex. 1,2,4,5,8,10)
  • tableau avec touches "0x": par exemple la touche " 08 "est associative tandis que la touche" 8 " est séquentielle.

l'idée est simple: si l'une des clés n'est PAS un entier, il est un tableau associatif, sinon c'est séquentielle.

function is_asso($a){
    foreach(array_keys($a) as $key) {if (!is_int($key)) return TRUE;}
    return FALSE;
}
7
répondu LazNiko 2011-10-05 09:13:12

j'ai remarqué deux approches populaires pour cette question: l'Une utilisant array_values() et l'autre utilisant key() . Pour savoir qui est plus rapide, j'ai écrit un petit programme:

$arrays = Array(
  'Array #1' => Array(1, 2, 3, 54, 23, 212, 123, 1, 1),
  'Array #2' => Array("Stack", 1.5, 20, Array(3.4)),
  'Array #3' => Array(1 => 4, 2 => 2),
  'Array #4' => Array(3.0, "2", 3000, "Stack", 5 => "4"),
  'Array #5' => Array("3" => 4, "2" => 2),
  'Array #6' => Array("0" => "One", 1.0 => "Two", 2 => "Three"),
  'Array #7' => Array(3 => "asdf", 4 => "asdf"),
  'Array #8' => Array("apple" => 1, "orange" => 2),
);

function is_indexed_array_1(Array &$arr) {
  return $arr === array_values($arr);
}

function is_indexed_array_2(Array &$arr) {
  for (reset($arr), $i = 0; key($arr) === $i++; next($arr))
    ;
  return is_null(key($arr));
}

// Method #1
$start = microtime(true);
for ($i = 0; $i < 1000; $i++) {
  foreach ($arrays as $array) {
    $dummy = is_indexed_array_1($array);
  }
}
$end = microtime(true);
echo "Time taken with method #1 = ".round(($end-$start)*1000.0,3)."ms\n";

// Method #2
$start = microtime(true);
for ($i = 0; $i < 1000; $i++) {
  foreach ($arrays as $array) {
    $dummy = is_indexed_array_2($array);
  }
}
$end = microtime(true);
echo "Time taken with method #1 = ".round(($end-$start)*1000.0,3)."ms\n";

sortie pour le programme sur PHP 5.2 sur CentOS est comme suit:

temps pris avec la méthode no 1 = 10,745 ms

Temps pris avec la méthode #2 = 18.239 ms

Sortie

sur PHP 5.3 des résultats similaires. Évidemment, utiliser array_values() est beaucoup plus rapide.

7
répondu Manu M. 2013-02-20 10:32:10

il y a déjà beaucoup de réponses, mais voici la méthode que Laravel utilise dans sa classe Arr:

/**
 * Determines if an array is associative.
 *
 * An array is "associative" if it doesn't have sequential numerical keys beginning with zero.
 *
 * @param  array  $array
 * @return bool
 */
public static function isAssoc(array $array)
{
    $keys = array_keys($array);

    return array_keys($keys) !== $keys;
}

Source: https://github.com/laravel/framework/blob/5.4/src/Illuminate/Support/Arr.php

6
répondu Ben 2018-09-30 21:23:10

une façon d'aborder ceci est de piggyback sur json_encode , qui a déjà sa propre méthode interne pour différencier entre un tableau associatif et un tableau indexé afin de produire le JSON correct.

Vous pouvez le faire en vérifiant si le premier caractère est retourné après l'encodage est un { (tableau associatif) ou un [ (tableau indexé).

// Too short :)
function is_assoc($arr) {
    ksort($arr);
    return json_encode($arr)[0] === '{';
}
5
répondu MAChitgarha 2018-09-10 08:31:43
function array_is_assoc(array $a) {
    $i = 0;
    foreach ($a as $k => $v) {
        if ($k !== $i++) {
            return true;
        }
    }
    return false;
}

rapide, concis, et efficace de mémoire. Pas de comparaison coûteuse, d'appel de fonction ou de copie de tableau.

4
répondu Jesse 2016-03-08 04:02:54

en utilisant xarray PHP extension

vous pouvez faire cela très rapidement (environ 30+ fois plus rapide en PHP 5.6):

if (array_is_indexed($array)) {  }

ou:

if (array_is_assoc($array)) {  }
3
répondu c9s 2015-10-16 08:05:15

ma solution:

function isAssociative(array $array)
{
    return array_keys(array_merge($array)) !== range(0, count($array) - 1);
}

array_merge sur un seul tableau réindexera toutes les clés integer , mais pas les autres. Par exemple:

array_merge([1 => 'One', 3 => 'Three', 'two' => 'Two', 6 => 'Six']);

// This will returns [0 => 'One', 1 => 'Three', 'two' => 'Two', 2 => 'Six']

donc si une liste (un tableau non-associatif) est créée ['a', 'b', 'c'] alors une valeur est supprimée unset($a[1]) puis array_merge est appelé, la liste est reindexée à partir de 0.

3
répondu ByScripts 2016-01-02 16:55:37

Voici la méthode que j'utilise:

function is_associative ( $a )
{
    return in_array(false, array_map('is_numeric', array_keys($a)));
}

assert( true === is_associative(array(1, 2, 3, 4)) );

assert( false === is_associative(array('foo' => 'bar', 'bar' => 'baz')) );

assert( false === is_associative(array(1, 2, 3, 'foo' => 'bar')) );

notez que cela ne tient pas compte des cas spéciaux comme:

$a = array( 1, 2, 3, 4 );

unset($a[1]);

assert( true === is_associative($a) );

Désolé, Je ne peux pas vous aider avec ça. Il est aussi un peu performant pour les tableaux de taille décente, car il ne fait pas de copies inutiles. Ce sont ces petites choses qui rendent Python et Ruby tellement plus agréables à écrire... : P

2
répondu AL the X 2011-07-22 19:29:46
<?php

function is_list($array) {
    return array_keys($array) === range(0, count($array) - 1);
}

function is_assoc($array) {
    return count(array_filter(array_keys($array), 'is_string')) == count($array);
}

?>

ces deux exemples, qui ont marqué le plus de points ne fonctionnent pas correctement avec des tableaux comme $array = array('foo' => 'bar', 1)

2
répondu KillEveryBody 2012-05-16 11:28:27

Ce serait trop de travail ( démo ):

function array_has_numeric_keys_only(array $array)
{
    try {
        SplFixedArray::fromArray($array, true);
    } catch (InvalidArgumentException $e) {
        return false;
    }
    return true;
}

veuillez noter que le point principal de cette réponse est de vous informer de l'existence de SplFixedArray et de ne pas vous encourager à utiliser des Exceptions pour ce genre de tests.

2
répondu Gordon 2012-06-21 11:14:58

je pense que la définition d'un tableau scalaire variera selon l'application. C'est-à-dire que certaines applications exigeront un sens plus strict de ce qui se qualifie comme un tableau scalaire, et certaines applications exigeront un sens plus lâche.

ci-dessous je présente 3 méthodes de rigueur variable.

<?php
/**
 * Since PHP stores all arrays as associative internally, there is no proper
 * definition of a scalar array.
 * 
 * As such, developers are likely to have varying definitions of scalar array,
 * based on their application needs.
 * 
 * In this file, I present 3 increasingly strict methods of determining if an
 * array is scalar.
 * 
 * @author David Farrell <DavidPFarrell@gmail.com>
 */

/**
 * isArrayWithOnlyIntKeys defines a scalar array as containing
 * only integer keys.
 * 
 * If you are explicitly setting integer keys on an array, you
 * may need this function to determine scalar-ness.
 * 
 * @param array $a
 * @return boolean
 */ 
function isArrayWithOnlyIntKeys(array $a)
{
    if (!is_array($a))
        return false;
    foreach ($a as $k => $v)
        if (!is_int($k))
            return false;
    return true;
}

/**
 * isArrayWithOnlyAscendingIntKeys defines a scalar array as
 * containing only integer keys in ascending (but not necessarily
 * sequential) order.
 * 
 * If you are performing pushes, pops, and unsets on your array,
 * you may need this function to determine scalar-ness.
 * 
 * @param array $a
 * @return boolean
 */ 
function isArrayWithOnlyAscendingIntKeys(array $a)
{
    if (!is_array($a))
        return false;
    $prev = null;
    foreach ($a as $k => $v)
    {
        if (!is_int($k) || (null !== $prev && $k <= $prev))
            return false;
        $prev = $k;
    }
    return true;
}

/**
 * isArrayWithOnlyZeroBasedSequentialIntKeys defines a scalar array
 * as containing only integer keys in sequential, ascending order,
 * starting from 0.
 * 
 * If you are only performing operations on your array that are
 * guaranteed to either maintain consistent key values, or that
 * re-base the keys for consistency, then you can use this function.
 * 
 * @param array $a
 * @return boolean
 */
function isArrayWithOnlyZeroBasedSequentialIntKeys(array $a)
{
    if (!is_array($a))
        return false;
    $i = 0;
    foreach ($a as $k => $v)
        if ($i++ !== $k)
            return false;
    return true;
}
2
répondu David Farrell 2012-12-25 00:46:57

est-ce la solution?

  public static function isArrayAssociative(array $array) {
      reset($array);
      return !is_int(key($array));
  }

la mise en garde est évidemment que le curseur du tableau est réinitialisé mais je dirais probablement que la fonction est utilisée avant même que le tableau soit traversé ou utilisé.

2
répondu Kat Lim Ruiz 2015-01-25 18:53:52

à moins que PHP n'ait un builtin pour cela, vous ne pourrez pas le faire en moins de O(n) - en énumérant toutes les clés et en vérifiant le type entier. En fait, vous voulez aussi s'assurer qu'il n'y a pas de trous, donc votre algorithme pourrait ressembler à:

for i in 0 to len(your_array):
    if not defined(your-array[i]):
        # this is not an array array, it's an associative array :)

mais à quoi bon? Supposons que le tableau est du type que vous attendez. Si ce n'est pas le cas, il va tout simplement exploser dans votre visage - c'est la programmation dynamique pour vous! Tester votre code et tout ira bien...

1
répondu Daren Thomas 2008-10-06 07:06:15

je sais que c'est un peu inutile d'ajouter une réponse à cette énorme file d'attente, mais voici une solution lisible O(n) qui ne nécessite pas de duplication des valeurs:

function isNumericArray($array) {
    $count = count($array);
    for ($i = 0; $i < $count; $i++) {
        if (!isset($array[$i])) {
            return FALSE;
        }
    }
    return TRUE;
}

plutôt que de vérifier les clés pour voir si elles sont toutes numériques, vous itérez sur les clés que serait être là pour un tableau numérique et s'assurer qu'ils existent.

1
répondu cloudfeet 2013-07-10 23:29:21

Un de plus rapide à partir de source . Code d'ajustement de json_encode (et bson_encode ). Ainsi que la conformité du tableau javascript.

function isSequential($value){
    if(is_array($value) || ($value instanceof \Countable && $value instanceof \ArrayAccess)){
        for ($i = count($value) - 1; $i >= 0; $i--) {
            if (!isset($value[$i]) && !array_key_exists($i, $value)) {
                return false;
            }
        }
        return true;
    } else {
        throw new \InvalidArgumentException(
            sprintf('Data type "%s" is not supported by method %s', gettype($value), __METHOD__)
        );
    }
}
1
répondu lazycommit 2014-08-09 05:43:45

les réponses sont déjà données mais il y a trop de désinformation sur la performance. J'ai écrit ce petit script de benchmark qui montre que la méthode foreach est la plus rapide.

avertissement: les méthodes suivantes ont été copiées-collées à partir des autres réponses

<?php

function method_1(Array &$arr) {
    return $arr === array_values($arr);
}

function method_2(Array &$arr) {
    for (reset($arr), $i = 0; key($arr) !== $i++; next($arr));
    return is_null(key($arr));
}

function method_3(Array &$arr) {
    return array_keys($arr) === range(0, count($arr) - 1);
}

function method_4(Array &$arr) {
    $idx = 0;
    foreach( $arr as $key => $val ){
        if( $key !== $idx )
            return FALSE;
        $idx++;
    }
    return TRUE;
}




function benchmark(Array $methods, Array &$target){    
    foreach($methods as $method){
        $start = microtime(true);
        for ($i = 0; $i < 1000; $i++)
            $dummy = call_user_func($method, $target);

        $end = microtime(true);
        echo "Time taken with $method = ".round(($end-$start)*1000.0,3)."ms\n";
    }
}



$targets = [
    'Huge array' => range(0, 30000),
    'Small array' => range(0, 1000),
];
$methods = [
    'method_1',
    'method_2',
    'method_3',
    'method_4',
];
foreach($targets as $targetName => $target){
    echo "==== Benchmark using $targetName ====\n";
    benchmark($methods, $target);
    echo "\n";
}

résultats:

==== Benchmark using Huge array ====
Time taken with method_1 = 5504.632ms
Time taken with method_2 = 4509.445ms
Time taken with method_3 = 8614.883ms
Time taken with method_4 = 2720.934ms

==== Benchmark using Small array ====
Time taken with method_1 = 77.159ms
Time taken with method_2 = 130.03ms
Time taken with method_3 = 160.866ms
Time taken with method_4 = 69.946ms
1
répondu nonsensei 2016-07-06 07:17:09

après quelques benchmarking, debugging, compiler probing, profiling, et abusing 3v4l.org pour comparer à travers plus de versions (Oui, j'ai reçu un avertissement d'arrêter) et comparer avec toutes les variations que j'ai pu trouver...

je vous donne un dérivé organique meilleure moyenne-pire scénario scénario fonction de test de réseau associatif qui est à pire à peu près aussi bonne ou meilleure que tous les autres scénarios de moyenne-cas.

que Dieu ait pitié de nos âmes.

/**
 * Tests if an array is an associative array.
 *
 * @param array $array An array to test.
 * @return boolean True if the array is associative, otherwise false.
 */
function is_assoc(array &$arr) {
    // don't try to check non-arrays or empty arrays
    if (FALSE === is_array($arr) || 0 === ($l = count($arr))) {
        return false;
    }

    // shortcut by guessing at the beginning
    reset($arr);
    if (key($arr) !== 0) {
        return true;
    }

    // shortcut by guessing at the end
    end($arr);
    if (key($arr) !== $l-1) {
        return true;
    }

    // rely on php to optimize test by reference or fast compare
    return array_values($arr) !== $arr;
}

de https://3v4l.org/rkieX :

<?php

// array_values
function method_1(Array &$arr) {
    return $arr === array_values($arr);
}

// method_2 was DQ; did not actually work

// array_keys
function method_3(Array &$arr) {
    return array_keys($arr) === range(0, count($arr) - 1);
}

// foreach
function method_4(Array &$arr) {
    $idx = 0;
    foreach( $arr as $key => $val ){
        if( $key !== $idx )
            return FALSE;
        ++$idx;
    }
    return TRUE;
}

// guessing
function method_5(Array &$arr) {
    global $METHOD_5_KEY;
    $i = 0;
    $l = count($arr)-1;

    end($arr);
    if ( key($arr) !== $l )
        return FALSE;

    reset($arr);
    do {
        if ( $i !== key($arr) )
            return FALSE;
        ++$i;
        next($arr);
    } while ($i < $l);
    return TRUE;
}

// naieve
function method_6(Array &$arr) {
    $i = 0;
    $l = count($arr);
    do {
        if ( NULL === @$arr[$i] )
            return FALSE;
        ++$i;
    } while ($i < $l);
    return TRUE;
}

// deep reference reliance
function method_7(Array &$arr) {
    return array_keys(array_values($arr)) === array_keys($arr);
}


// organic (guessing + array_values)
function method_8(Array &$arr) {
    reset($arr);
    if ( key($arr) !== 0 )
        return FALSE;

    end($arr);
    if ( key($arr) !== count($arr)-1 )
        return FALSE;

    return array_values($arr) === $arr;
}

function benchmark(Array &$methods, Array &$target, $expected){    
    foreach($methods as $method){
        $start = microtime(true);
        for ($i = 0; $i < 2000; ++$i) {
            //$dummy = call_user_func($method, $target);
            if ( $method($target) !== $expected ) {
                echo "Method $method is disqualified for returning an incorrect result.\n";
                unset($methods[array_search($method,$methods,true)]);
                $i = 0;
                break;
            }
        }
        if ( $i != 0 ) {
            $end = microtime(true);
            echo "Time taken with $method = ".round(($end-$start)*1000.0,3)."ms\n";
        }
    }
}



$true_targets = [
    'Giant array' => range(0, 500),
    'Tiny array' => range(0, 20),
];


$g = range(0,10);
unset($g[0]);

$false_targets = [
    'Large array 1' => range(0, 100) + ['a'=>'a'] + range(101, 200),
    'Large array 2' => ['a'=>'a'] + range(0, 200),
    'Tiny array' => range(0, 10) + ['a'=>'a'] + range(11, 20),
    'Gotcha array' => $g,
];

$methods = [
    'method_1',
    'method_3',
    'method_4',
    'method_5',
    'method_6',
    'method_7',
    'method_8'
];


foreach($false_targets as $targetName => $target){
    echo "==== Benchmark using $targetName expecing FALSE ====\n";
    benchmark($methods, $target, false);
    echo "\n";
}
foreach($true_targets as $targetName => $target){
    echo "==== Benchmark using $targetName expecting TRUE ====\n";
    benchmark($methods, $target, true);
    echo "\n";
}
1
répondu TylerY86 2018-01-15 07:44:02

je compare la différence entre les clés du tableau et les clés du résultat de array_values() du tableau, qui sera toujours un tableau avec des indices entiers. Si les touches sont les mêmes, ce n'est pas un tableau associatif.

function isHash($array) {
    if (!is_array($array)) return false;
    $diff = array_diff_assoc($array, array_values($array));
    return (empty($diff)) ? false : true;
}
0
répondu philroy 2009-05-15 15:04:49

Modification sur la réponse la plus populaire.

Cela demande un peu plus de traitement, mais est plus précis.

<?php
//$a is a subset of $b
function isSubset($a, $b)
{
    foreach($a =>$v)
        if(array_search($v, $b) === false)
            return false;

    return true;

    //less effecient, clearer implementation. (uses === for comparison)
    //return array_intersect($a, $b) === $a;
}

function isAssoc($arr)
{
    return !isSubset(array_keys($arr), range(0, count($arr) - 1));
}

var_dump(isAssoc(array('a', 'b', 'c'))); // false
var_dump(isAssoc(array(1 => 'a', 0 => 'b', 2 => 'c'))); // false
var_dump(isAssoc(array("0" => 'a', "1" => 'b', "2" => 'c'))); // false 
//(use === in isSubset to get 'true' for above statement)
var_dump(isAssoc(array("a" => 'a', "b" => 'b', "c" => 'c'))); // true
?>
0
répondu Jason McCarrell 2011-04-19 19:02:07

à mon avis, Un tableau devrait être accepté comme associatif si l'une de ses clés n'est pas un nombre entier, par exemple des nombres flottants et une chaîne vide".

aussi les entiers non-séquencés doivent être considérés comme associatifs comme (0,2,4,6) parce que ces types de tableaux ne peuvent pas être utilisés pour des boucles de cette façon:

$n =count($arr);
for($i=0,$i<$n;$i++) 

la deuxième partie de la fonction ci-dessous vérifie si les touches sont indexées ou non.Il fonctionne également pour les clés avec des valeurs négatives. Exemple (-1.0.1.2.3.4.5)

count() = 7 , max = 5, min=-1



if( 7 == (5-(-1)+1 ) // true
    return false; // array not associative


/** 
 * isAssoc Checks if an array is associative
 * @param $arr reference to the array to be checked
 * @return bool 
 */     
function IsAssoc(&$arr){
    $keys= array_keys($arr);
    foreach($keys as $key){
        if (!is_integer($key))
            return true;
    }
    // if all keys are integer then check if they are indexed
    if(count($arr) == (max($keys)-min($keys)+1))
        return false;
    else
        return true;
}
0
répondu Selim Acar 2012-06-19 13:38:10