¿Cómo verificar si la matriz de PHP es asociativa o secuencial?

PHP trata todas las matrices como asociativas, por lo que no hay funciones integradas. ¿Alguien puede recomendar una forma bastante eficiente de verificar si una matriz contiene solo teclas numéricas?

Básicamente, quiero poder diferenciar entre esto:

$sequentialArray = [
    'apple', 'orange', 'tomato', 'carrot'
];

y esto:

$assocArray = [
    'fruit1' => 'apple',
    'fruit2' => 'orange',
    'veg1' => 'tomato',
    'veg2' => 'carrot'
];

preguntado Oct 06 '08, 05:10

Hay un error en tu código: el tomate es una fruta. -

Este método tiene salvedades, pero a menudo solo hago if (isset($array[0])), que es simple y rápido. Por supuesto, primero debe asegurarse de que la matriz no esté vacía, y debe tener algún conocimiento sobre los posibles contenidos de la matriz para que el método no pueda fallar (como numérico / asociativo mixto o no secuencial). -

@ OlleHärstedt No según Tribunal Superior de Estados Unidos. ;-) -

30 Respuestas

Ha hecho dos preguntas que no son del todo equivalentes:

  • En primer lugar, cómo determinar si una matriz tiene solo teclas numéricas
  • En segundo lugar, cómo determinar si una matriz tiene secuencial teclas numéricas, comenzando desde 0

Considere cuál de estos comportamientos realmente necesita. (Puede ser que cualquiera de los dos sirva para sus propósitos).

La primera pregunta (simplemente comprobar que todas las teclas son numéricas) es bien respondido por el Capitán KurO.

Para la segunda pregunta (verificar si la matriz tiene un índice cero y es secuencial), puede usar la siguiente función:

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

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

Respondido 09 Oct 19, 13:10

Solución muy elegante. Tenga en cuenta que devuelve VERDADERO en el caso (ambiguo) de una matriz vacía. - jonathan lidbeck

Creo que es más útil pensar en matrices secuenciales como un caso especial de matrices asociativas. Entonces, cada matriz es asociativa, pero solo algunas son secuenciales. Por tanto, una función isSequential() tendría más sentido que isAssoc(). En tal función, la matriz vacía debemos ser visto como secuencial. La fórmula podría ser array() === $arr || !isAssoc($arr). - Don Quijote

Creo que esto evitaría una gran cantidad de tiempo de CPU y memoria potencial si se verificara si isset ($ arr [0]) es falso antes de extraer todas las claves, ya que es claramente asociativo si la matriz no está vacía pero no tiene ningún elemento en 0 posición. Como "la mayoría" de las matrices asociativas reales tienen cadenas como claves, esto debería ser una buena optimización para el caso general de dicha función. - OderWat

@OderWat: su optimización debe usar array_key_exists en lugar de isset porque si el elemento cero es un valor nulo, isset devolverá falso incorrectamente. Normalmente, un valor nulo debería ser un valor legítimo en dicha matriz. - DevOC

@MAChitgarha, su edición cambió el comportamiento de la función sin ninguna explicación de por qué, y la hizo contradecir la descripción en la prosa anterior de lo que realmente se supone que debe hacer. Lo he revertido. - marca amery

Para simplemente verificar si la matriz tiene claves no enteras (no si la matriz está indexada secuencialmente o indexada a cero):

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

Si hay al menos una clave de cadena, $array se considerará una matriz asociativa.

Respondido el 25 de diciembre de 15 a las 16:12

Este método es mucho mejor de lo que parece. Si cuenta (matriz_filtrada) == cuenta (matriz_original), entonces es una matriz asociada. Si cuenta (matriz_filtrada) == 0, entonces es una matriz indexada. Si cuenta (matriz_filtrada) <cuenta (matriz_original), entonces la matriz tiene claves numéricas y de cadena. - Jamol

@MikePretzlaw de curso itera; (obviamente) no hay forma posible de determinar si todas las claves de la matriz son enteras sin mirar todas las claves de la matriz. Supongo que las alternativas no iterativas que se supone que veremos a continuación son como $isIndexed = array_values($arr) === $arr;? A lo que pregunto: como piensas array_values() ¿obras? Cómo crees que === aplicado a matrices funciona? La respuesta es, por supuesto, que Además, iterar sobre la matriz. - marca amery

@ARW "PHP parece convertir todo en un int en una definición de matriz si puede". - Sí, eso es exactamente lo que sucede. La mayor WTF es que incluso hace esto con flotadores; si intentas var_dump([1.2 => 'foo', 1.5 => 'bar']); descubrirás que obtienes la matriz [1 => 'bar']. No hay forma alguna de averiguar el tipo original de una clave. Sí, todo esto es espantoso; Las matrices de PHP son, con mucho, la peor parte del lenguaje, y la mayor parte del daño es irreparable y se debe a que la idea de usar una sola construcción para matrices tradicionales y hashmaps tradicionales es terrible desde el principio. - marca amery

@MarkAmery Lo anterior, aunque simple, garantiza un recorrido del 100% de la matriz. Sería más eficiente, especialmente si está tratando con matrices grandes, si estuviera buscando una cadena o int y rompiera en el primero que encontró. Por ejemplo: function isAssociative($arr) { foreach ($arr as $key => $value) { if (is_string($key)) return true; } return false; } - Thought

@Pensé que su código funciona muy rápido pero no puede detectar matriz secuencial. Ejemplo array(1 => 'a', 0 => 'b', 2 => 'c') se convertirá false (matriz secuencial) mientras que debería ser true (matriz asociativa). toolsqa.com/data-structures/array-in-programming No estoy seguro de si la clave debe ser el orden ascendente. (0, 1, ...) - uve

Seguramente esta es una mejor alternativa.

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

Respondido el 21 de junio de 12 a las 14:06

Esto duplicará los valores de la matriz, lo que es potencialmente muy caro. Es mucho mejor examinar las claves de la matriz. - meagar ♦

Acabo de usar ==; No creo que haya necesidad de === aquí. Pero para responder al "desarmado y no funciona": una vez que desarmado el primer elemento, ya no es una matriz indexada con números enteros que comienza en 0. Entonces, en mi opinión, sí funciona. - Grantwparks

De acuerdo con @grantwparks: una matriz dispersa no está indexada. Curiosamente, debido a que no hay forma de eliminar un elemento del medio de una matriz indexada, PHP básicamente declara todas las matrices como asociativas y numéricas es solo una versión de 'inventar la clave para mí'. - RickMeasham

El único problema que tengo con esto es que el === Perderemos el tiempo comprobando si los valores son iguales, aunque solo nos interesen las claves. Por eso prefiero el $k = array_keys( $arr ); return $k === array_keys( $k ); versión. - Jesse

Una nota adicional, esto falla en matrices especificadas con teclas numéricas que están fuera de servicio. por ejemplo, $ myArr = array (0 => 'a', 3 => 'b', 4 => 1, 2 => 2, 1 => '3'); Una posible solución es ejecutar ksort ($ arr) antes de realizar la prueba: Scott

Muchos comentaristas en esta pregunta no entienden cómo funcionan las matrices en PHP. Desde el documentación de la matriz:

Una clave puede ser un número entero o una cadena. Si una clave es la representación estándar de un número entero, se interpretará como tal (es decir, "8" se interpretará como 8, mientras que "08" se interpretará como "08"). Los flotantes en clave se truncan a un número entero. Los tipos de matrices indexadas y asociativas son del mismo tipo en PHP, que pueden contener índices enteros y de cadena.

En otras palabras, no existe una clave de matriz de "8" porque siempre se convertirá (silenciosamente) al entero 8. Por lo tanto, tratar de diferenciar entre enteros y cadenas numéricas es innecesario.

Si desea la forma más eficiente de verificar una matriz en busca de claves no enteras sin hacer una copia de parte de la matriz (como hace array_keys ()) o toda (como hace foreach):

function keyedNext( &$arr, &$k){
    $k = key($arr);
    return next($arr);
}

for ($k = key(reset($my_array)); is_int($k); keyedNext($my_array,$k))
    $onlyIntKeys = is_null($k);

Esto funciona porque key () devuelve NULL cuando la posición actual de la matriz no es válida y NULL nunca puede ser una clave válida (si intenta utilizar NULL como clave de matriz, se convierte silenciosamente a "").

Respondido el 02 de enero de 19 a las 19:01

Esto no funciona para claves enteras no secuenciales. Pruébelo con [2 => 'a', 4 => 'b']. - davidj

@DavidJ, ¿Qué quieres decir con "no funciona"? Determina con éxito que todas las claves son números enteros. ¿Está afirmando que una matriz como la que publicó no debería considerarse una "matriz numérica"? - error de volcado de núcleo

Una matriz no asociativa debe tener claves que van desde 0 a count($array)-1, en este estricto orden. Un control preliminar con is_array() puede ayudar. Agregue una variable creciente para verificar la secuencia de teclas: for ($k = 0, reset($array) ; $k === key($array) ; next($array)) ++$k; Eso resuelve el trato. - ofavre

Usar foreach en lugar de la iteración explícita es aproximadamente dos veces más rápida. - ofavre

Si desea convertir esto en una función: function isAssocStr($array) { for (reset($array); is_int(key($array)); next($array)) { if (is_null(key($array))) return false; } return true; } - Katrina

As declarado por el OP:

PHP trata todas las matrices como asociativas

no es muy sensato (en mi humilde opinión) escribir una función que compruebe si una matriz es de asociación. Así que lo primero es lo primero: ¿Qué es una clave en una matriz PHP??:

La clave puede ser un entero o con una cadena.

Eso significa que hay 3 casos posibles:

  • Caso 1. todas las claves son numérico / enteros.
  • Caso 2. todas las claves son instrumentos de cuerda.
  • Caso 3. algunas claves son instrumentos de cuerda, algunas claves son numérico / enteros.

Podemos comprobar cada caso con las siguientes funciones.

Caso 1: todas las claves son numérico / enteros.

Nota:: Esta función devuelve verdadero para matrices vacías también.

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

Caso 2: todas las claves son instrumentos de cuerda.

Nota:: Esta función devuelve verdadero para matrices vacías también.

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

Caso 3. algunas claves son instrumentos de cuerda, algunas claves son numérico / enteros.

Nota:: Esta función devuelve verdadero para matrices vacías también.

//! 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;
}

Resulta que:


Ahora, para que una matriz sea un matriz "genuina" que todos estamos acostumbrados, es decir:

  • Sus claves son todas numérico / enteros.
  • Sus claves son secuencial (es decir, aumentando en el paso 1).
  • Sus claves empezar desde cero.

Podemos comprobar con la siguiente función.

Caso 3a. las llaves son numérico / enteros, secuencialy basado en cero.

Nota:: Esta función devuelve verdadero para matrices vacías también.

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

Advertencias / trampas (o, incluso hechos más peculiares sobre las claves de matriz en PHP)

Claves enteras

Las claves para estas matrices son enteros:

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

Claves de cadena

Las claves para estas matrices son instrumentos de cuerda:

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!

Teclas enteras que parecen cadenas

Si crees que la clave en array("13" => "b") es un cadena, Está usted equivocado. Del doc aquí:

Las cadenas que contienen enteros válidos se convertirán al tipo de entero. Por ejemplo, la clave "8" se almacenará en realidad en 8. Por otro lado, "08" no se convertirá, ya que no es un entero decimal válido.

Por ejemplo, la clave para estas matrices es enteros:

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

Pero la clave de estas matrices es instrumentos de cuerda:

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.

Además, según el doctor,

El tamaño de un número entero depende de la plataforma, aunque un valor máximo de aproximadamente dos mil millones es el valor habitual (es decir, 32 bits con signo). Las plataformas de 64 bits suelen tener un valor máximo de aproximadamente 9E18, excepto para Windows, que siempre es de 32 bits. PHP no admite enteros sin firmar.

Entonces la clave para esta matriz puede o puede que no frijol entero - depende de tu plataforma.

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

Peor aún, PHP tiende a ser calesa si el entero está cerca del 231 = 2,147,483,648 límite (ver error 51430, error 52899). Por ejemplo, en mi entorno local (PHP 5.3.8 en XAMPP 1.7.7 en Windows 7), var_dump(array("2147483647" => "b")) da

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

pero en esta demostración en vivo en el teclado (PHP 5.2.5), la misma expresión da

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

Entonces la clave es un entero en un entorno pero un cadena en otro, aunque 2147483647 es un 32 bits con signo válido entero.

contestado el 23 de mayo de 17 a las 11:05

Excepto que, como menciono a continuación, implica la creación de una matriz duplicada de la que se está verificando, lo que lo hace muy costoso para matrices grandes y una fuente potencial de fallas de memoria en hosts compartidos. - vaina

Me sorprende que puedas convertir "-13" en una cadena, pero no "+13". Supongo que nadie escribiría "+13" en lugar de "13", pero todavía está un poco cansado. Tobías

En cuanto a velocidad:

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

En cuanto a la memoria:

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

Respondido 06 ago 11, 18:08

la siguiente matriz: matriz (02 => 11,1,2,456); se muestra como sin teclas numéricas usando el algoritmo anterior, incluso si 02 === 2 - Galileo Galilei

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

respondido 15 mar '10, 02:03

Este es el , solamente respuesta (en el momento de mi comentario) que puede lidiar con lo siguiente: $ array = array (0 => 'blah', 2 => 'yep', 3 => 'wahey') - bata gastada

pero array('1'=>'asdf', '2'=>'too') se considerará una matriz asociativa mientras que en realidad no lo es (las claves son en realidad una cadena) - Capitán KurO

@CaptainkurO Quieres decir numérico. Es una matriz asociativa. - devios1

Esta función devuelve true si las claves son: cero, números enteros (solo positivos), una cadena vacía o cualquier combinación de las anteriores, como la cadena "09". Esta función no tiene en cuenta el orden de las teclas. Entonces array(0=>'blah', 2=>'yep', 3=>'wahey'), array(0=>'blah', 2=>'yep', 1=>'wahey') y array('blah', 'yep', 'wahey') son todos asociativos de acuerdo con esta función, mientras que array('a'=>'blah', 'b'=>'yep', 'c'=>'wahey') no es. - Pang

@CaptainkurO es incorrecto. '1' y '2' se almacenarán como números enteros. Lea la parte citada de la respuesta de la ardilla del 11 de mayo de 2011 a las 19:34. PHP no almacena claves de cadena que se vean exactamente como números enteros. Los convierte en números enteros. - mayordomo butkus

En realidad, la forma más eficiente es:

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

Esto funciona porque compara las claves (que para una matriz secuencial son siempre 0,1,2, etc.) con las claves de las claves (que siempre ser 0,1,2, XNUMX, XNUMX, etc.).

respondido 08 mar '16, 03:03

Inteligente, pero no bueno. ¿Por qué es esto "más eficiente"? Sería mucho más legible simplemente comparar array_keys ($ a) con range (0, count ($ a)). La solución más inteligente rara vez es la mejor en mi experiencia. Especialmente cuando ser inteligente no aporta literalmente ningún valor a la alternativa obvia y limpia. - Shane H.

Esta función devuelve true para array(1=>"a") pero false para array("a"=>"a"). Sería más significativo si != es reemplazado por !==. - Pang

@Pang tienes razón. Pensé que tu comentario seguramente debía estar equivocado al principio, pero, para mi sorpresa, [0] == ['a'] en PHP (desde 0 == 'a', y de hecho, 0 == 'banana'). PHP == El operador está loco. - marca amery

No es eficiente en la medida en que implica llamar a array_keys versus simplemente verificar hasta que encuentre un índice entero no secuencial. Debajo del capó estás haciendo eso de todos modos, pero ya ha duplicado una gran matriz. - vaina

He usado ambos array_keys($obj) !== range(0, count($obj) - 1) y array_values($arr) !== $arr (que son duales entre sí, aunque el segundo es más barato que el primero) pero ambos fallan para matrices muy grandes.

Esto es porque array_keys y array_values Ambas son operaciones muy costosas (ya que construyen una matriz completamente nueva de tamaño más o menos el original).

La siguiente función es más robusta que los métodos proporcionados anteriormente:

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;
}

También tenga en cuenta que si no le importa diferenciar matrices dispersas de matrices asociativas, simplemente puede devolver 'assoc' de ambos if Bloques

Finalmente, si bien esto puede parecer mucho menos "elegante" que muchas "soluciones" en esta página, en la práctica es mucho más eficiente. Casi cualquier matriz asociativa se detectará instantáneamente. Solo las matrices indexadas se verificarán exhaustivamente, y los métodos descritos anteriormente no solo verifican las matrices indexadas de manera exhaustiva, sino que las duplican.

Respondido el 02 de enero de 16 a las 00:01

Creo que las siguientes dos funciones son la mejor manera de verificar 'si una matriz es asociativa o numérica'. Dado que 'numérico' podría significar solo teclas numéricas o solo teclas numéricas secuenciales, a continuación se enumeran dos funciones que verifican cualquiera de las condiciones:

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 primera función comprueba si cada tecla es un valor entero. La segunda función verifica si cada clave es un valor entero y además verifica si todas las claves son secuenciales comenzando en $ base, que por defecto es 0 y por lo tanto puede omitirse si no necesita especificar otro valor base. key ($ my_array) devuelve nulo si el puntero de lectura se mueve más allá del final de la matriz, que es lo que finaliza el ciclo for y hace que la declaración después del ciclo for devuelva verdadera si todas las claves fueran enteras. De lo contrario, el bucle termina prematuramente porque una clave es de tipo cadena y la declaración después del bucle for devolverá falso. La última función además agrega uno a $ base después de cada comparación, para poder verificar si la siguiente clave tiene el valor correcto. La comparación estricta hace que también verifique si la clave es de tipo entero. La parte $ base = (int) $ base en la primera sección del ciclo for se puede dejar fuera cuando se omite $ base o si se asegura de que solo se llame usando un número entero. Pero como no puedo estar seguro para todos, lo dejé. De todos modos, la declaración se ejecuta solo una vez. Creo que estas son las soluciones más eficientes:

  • En cuanto a la memoria: no se permiten copias de datos o rangos de claves. Hacer un array_values ​​o array_keys puede parecer más corto (menos código) pero tenga en cuenta lo que sucede en segundo plano una vez que realiza esa llamada. Sí, hay más declaraciones (visibles) que en algunas otras soluciones, pero eso no es lo que cuenta, ¿verdad?
  • En cuanto al tiempo: además del hecho de que copiar / extraer datos y / o claves también lleva tiempo, esta solución es más eficiente que hacer un foreach. Una vez más, un foreach puede parecer más eficiente para algunos porque es más corto en notación, pero en el fondo foreach también llama a reset, key y el siguiente en hacer es bucle. Pero además también llama a valid para verificar la condición final, que aquí se evita debido a la combinación con la verificación de enteros.

Recuerde que una clave de matriz solo puede ser un número entero o una cadena, y una cadena estrictamente numérica como "1" (pero no "01") se traducirá a un entero. Que es lo que hace que la verificación de una clave entera sea la única operación necesaria además de contar si desea que la matriz sea secuencial. Naturalmente, si is_indexed_array devuelve falso, la matriz puede verse como asociativa. Digo 'visto', porque de hecho todos lo son.

Respondido 15 Jul 12, 22:07

Esta es la mejor respuesta. La definición de matriz "asociativa" o "numérica" ​​depende de la situación específica. - Pato

Si foreach es menos eficiente que el método utilizado aquí, aparte del inconveniente de usar dos funciones diferentes, el rendimiento de esta solución es mejor que el mío (el anterior). Sospecho que no lo es, ya que se recomienda foreach como la forma más rápida de pasar por una matriz. - vaina

Una forma de abordar esto es a cuestas json_encode, que ya tiene su propio método interno para diferenciar entre una matriz asociativa y una matriz indexada para generar el JSON correcto.

Puede hacer esto comprobando si el primer carácter devuelto después de la codificación es un { (matriz asociativa) o un [ (matriz indexada).

// Too short :)
function is_assoc($arr) {
    ksort($arr);
    return json_encode($arr)[0] === '{';
}

Respondido el 10 de Septiembre de 18 a las 09:09

El ksort () no es necesario en mi opinión. Esta solución está funcionando, pero tiene que probar si $ arr es nulo y si json_encode falla, así que intente / capture. + no es realmente óptimo si $ arr es grande. - lubonnin

Esta función puede manejar:

  • matriz con agujeros en el índice (por ejemplo, 1,2,4,5,8,10, XNUMX, XNUMX, XNUMX, XNUMX, XNUMX)
  • matriz con claves "0x": por ejemplo, la clave '08' es asociativa mientras que la clave '8' es secuencial.

la idea es simple: si una de las claves NO es un número entero, es una matriz asociativa, de lo contrario es secuencial.

function is_asso($a){
    foreach(array_keys($a) as $key) {if (!is_int($key)) return TRUE;}
    return FALSE;
}

Respondido 05 Oct 11, 10:10

"si una de las claves NO es un número entero, es una matriz asociativa; de lo contrario, es secuencial" - ¿eh? No, esto es simplemente incorrecto. Hay espacio para discutir sobre lo que constituye una matriz "asociativa", pero el significado de "secuencial" es bastante inequívoco, y no es lo mismo que todas las claves sean números. - marca amery

Si una de las claves NO es un número entero, ES asociativa por naturaleza, sin embargo, solo es secuencial si las claves van de 0 - longitud (matriz) - 1. Sin embargo, es NUMÉRICA, si todas las claves están numeradas, o puede que no funcione con muchas funciones de matriz que requieren una matriz secuencial. Si convierte la matriz numérica con agujeros en secuencial ejecutando array_values ​​(matriz) en ella, entonces se convertiría en secuencial. - geilt

Noté dos enfoques populares para esta pregunta: uno que usa array_values() y otro uso key(). Para saber cuál es más rápido, escribí un pequeño programa:

$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";

La salida del programa en PHP 5.2 en CentOS es la siguiente:

Tiempo empleado con el método n. ° 1 = 10.745 ms
Tiempo empleado con el método n. ° 2 = 18.239 ms

La salida en PHP 5.3 arrojó resultados similares. Obviamente usando array_values() es mucho más rápido.

Respondido 20 Feb 13, 10:02

mal punto de referencia. No probó para matrices grandes. En mi computadora, a partir de 10K + elementos, el método n. ° 2 es más rápido. Prueba con $arrays = Array( 'Array #1' => range(0, 50000), ); - tonterías

Ya hay muchas respuestas, pero aquí está el método en el que se basa Laravel dentro de su clase 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;
}

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

Respondido el 30 de Septiembre de 18 a las 22:09

@Casey array_keys($keys) devolverá una matriz secuencial de números (0 ... X) que tiene la misma longitud que la matriz original. Por ejemplo array_keys(["a", "b", "c"]) = [0, 1, 2]; array_keys([0, 1, 2]) = [0, 1, 2] (es una matriz secuencial porque [0, 1, 2] !== [0, 1, 2]). Otro ejemplo: array_keys(["a" => 5, "b" => 7, "c" => 10]) = ["a", "b", "c"]; array_keys(["a", "b", "c"]) = [0, 1, 2] (es una matriz asociativa porque ["a", "b", "c"] !== [0, 1, 2]). Espero que esté claro (es difícil de explicar ampliamente en un comentario, al menos para mí): Valepu

Este algoritmo es una locura, fácil y comprensible. - Benyí

Esto no funcionará si tiene una matriz secuencial de filas asociativas. - lubonnin

function array_is_assoc(array $a) {
    $i = 0;
    foreach ($a as $k => $v) {
        if ($k !== $i++) {
            return true;
        }
    }
    return false;
}

Rápido, conciso y eficiente en memoria. Sin costosas comparaciones, llamadas a funciones o copia de matrices.

respondido 08 mar '16, 04:03

Aquí hay otra lógica simple pero poderosa (que también es utilizada por el gran marco de Laravel en su mecanismo interno)

/**
 * Determines if an array is associative.
 * @param  array  $array
 * @return bool
 */
function isAssoc(array $array)
{
    $keys = array_keys($array);

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

Respondido el 08 de Septiembre de 20 a las 05:09

Mediante el uso radiografía Extensión PHP

Puede hacer esto muy rápido (aproximadamente 30 veces más rápido en PHP 5.6):

if (array_is_indexed($array)) {  }

o:

if (array_is_assoc($array)) {  }

Respondido 16 Oct 15, 09:10

Sé que es un poco inútil agregar una respuesta a esta enorme cola, pero aquí hay una solución O (n) legible que no requiere duplicar ningún valor:

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

En lugar de comprobar las claves para ver si son todas numéricas, itera sobre las claves que se Esté allí para una matriz numérica y asegúrese de que existan.

Respondido 11 Jul 13, 00:07

un punto más. matriz en forma [1,2,null,4] fallará, pero es la matriz correcta. así que agregué algunas mejoras en stackoverflow.com/a/25206156/501831 con adición array_key_exists cheque) - perezoso

-1; isset() es la herramienta incorrecta aquí porque devolverá falso si el valor está establecido pero es null, como lo señaló @lazycommit. - marca amery

Uno más rápido de fuente. Ajustar codificación de json_encode (y bson_encode). Por lo tanto, cumple con la matriz de 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__)
        );
    }
}

Respondido 09 ago 14, 06:08

Por qué isset y array_key_exists? ¿No sería suficiente esto último? - mcfedr

@mcfedr sí, lo haría - el isset() comprobar aquí es completamente redundante. - marca amery

@mcfedr, @ mark-amery por motivos de rendimiento. isset() es más rápido que array_key_exists(). ver ilia.ws/archives/… - perezoso

@lazycommit Dependerá de su matriz, luego de si es mejor con o sin, no es probable que tenga una matriz con muchos nulls, pero tampoco es tan probable que tenga una matriz lo suficientemente grande como para que haya una diferencia de rendimiento notable al usar ambas comprobaciones: mcfedr

si necesita comprobar si encajaría json_encode, simplemente puede verificar el primer símbolo de la cadena, devuelto por json_encode($your_arr) -- ya sea [ or { ;-)- pilat

Mi solución:

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

array_merge en una sola matriz se reindexarán todos integer claves, pero no otras. Por ejemplo:

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

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

Entonces, si se crea una lista (una matriz no asociativa) ['a', 'b', 'c'] luego se elimina un valor unset($a[1]) luego array_merge se llama, la lista se vuelve a indexar a partir de 0.

Respondido el 02 de enero de 16 a las 16:01

-1; esto es O(n) en memoria adicional utilizada (ya que creó múltiples matrices nuevas con tantos elementos como $array), la respuesta no aborda la ambigüedad de la pregunta que se hizo ni explica exactamente cómo está definiendo una lista / matriz no asociativa, e incluso si ninguno de estos puntos fuera cierto, no está claro que esto agregue algún valor en comparación con otras respuestas ya publicado. - marca amery

Después de algunas evaluaciones comparativas locales, depuración, sondeo del compilador, elaboración de perfiles y abuso de 3v4l.org para comparar más versiones (sí, recibí una advertencia para detenerme) y comparar con todas las variaciones que pude encontrar ...

Te doy un derivado orgánicamente mejor-promedio-peor escenario función de prueba de matriz asociativa que está en peor aproximadamente tan bueno o mejor que todos los demás escenarios de casos promedio.

/**
 * 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;
}

Desde 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";
}

respondido 24 nov., 18:17

Muchas de las soluciones aquí son elegantes y bonitas, pero no escalan bien y consumen mucha memoria o CPU. La mayoría está creando 2 nuevos puntos de datos en la memoria con esta solución desde ambos lados de la comparación. Cuanto mayor sea la matriz, más duro y más largo será el proceso y la memoria utilizados, y perderá el beneficio de la evaluación de cortocircuitos. Hice algunas pruebas con algunas ideas diferentes. Intentar evitar array_key_exists ya que es costoso y también evitar la creación de nuevos conjuntos de datos grandes para comparar. Creo que esta es una forma sencilla de saber si una matriz es secuencial.

public function is_sequential( $arr = [] ){
    if( !is_array( $arr ) || empty( $arr ) ) return false;

    $i = 0;

    $total = count( $arr );

    foreach( $arr as $key => $value ) if( $key !== $i++ ) return false;

    return true;
}

Ejecuta un solo recuento en la matriz principal y almacena un solo entero. Luego recorre la matriz y verifica una coincidencia exacta mientras itera el contador. Debe tener de 1 para contar. Si falla, se producirá un cortocircuito, lo que aumentará el rendimiento cuando sea falso.

Originalmente hice esto con un bucle for y verificando isset ($ arr [$ i]) pero esto no detectará claves nulas que requieren array_key_exists, y como sabemos, esa es la peor función para usar para la velocidad.

Actualizando constantemente las variables a través de foreach para verificar junto con el iterador que nunca pasa de su tamaño entero, usemos PHP, la optimización de memoria integrada, el almacenamiento en caché y la recolección de basura para mantenerlo con un uso muy bajo de recursos.

Además, argumentaré que usar array_keys en un foreach es una tontería cuando simplemente puede ejecutar $ key => $ value y verificar la clave. ¿Por qué crear el nuevo punto de datos? Una vez que abstrae las claves de la matriz, ha consumido más memoria inmediatamente.

contestado el 10 de mayo de 19 a las 04:05

Este es el método que utilizo:

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

Tenga en cuenta que esto no tiene en cuenta casos especiales como:

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

unset($a[1]);

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

Lo siento, no puedo ayudarte con eso. También es algo eficaz para matrices de tamaño decente, ya que no hace copias innecesarias. Son estas pequeñas cosas las que hacen que Python y Ruby sean mucho más agradables para escribir ...: P

Respondido 22 Jul 11, 20:07

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

?>

Ambos ejemplos, que obtuvieron la mayor cantidad de puntos, no funcionan correctamente con matrices como $array = array('foo' => 'bar', 1)

contestado el 16 de mayo de 12 a las 12:05

+1 tu is_list () es OMI la mejor respuesta. Algunas personas no tienen ni idea sobre la complejidad del tiempo y el espacio, y la función nativa frente a la función de script PHP ... - e2-e4

Esto también funcionaría (manifestación):

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

Tenga en cuenta que el punto principal de esta respuesta es informarle sobre la existencia de SplFixedArray y no para animarle a utilizar Excepciones para este tipo de pruebas.

Respondido el 21 de junio de 12 a las 12:06

Creo que la definición de una matriz escalar variará según la aplicación. Es decir, algunas aplicaciones requerirán un sentido más estricto de lo que califica como una matriz escalar, y algunas aplicaciones requerirán un sentido más flexible.

A continuación presento 3 métodos de diferente rigor.

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

Respondido el 25 de diciembre de 12 a las 00:12

¿Podría ser esta la solución?

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

La advertencia es obviamente que el cursor de la matriz se restablece, pero yo diría que probablemente la función se usa antes de que la matriz se atraviese o se use.

Respondido el 25 de enero de 15 a las 18:01

Esta función devuelve false por tanto array("a", "b") y array("a", "b" => "B") ya que solo verifica la primera clave. POR CIERTO, is_long es solo un alias de is_int. - Pang

Sinceramente, creo que esto sería muy eficaz en la gran mayoría de los casos y es mucho más eficaz que las alternativas. Si comprende las consecuencias de este método y se da cuenta de que le funcionará, probablemente sea la mejor opción. - Gershy

Esto es simplemente incorrecto; solo mira la primera clave. - marca amery

@MarkAmery la pregunta hizo cómo diferenciar puramente matrices secuenciales de puramente matrices asociativas. Esta respuesta hace exactamente eso y es la más eficiente de todas. Tener un comportamiento indefinido para mezclado arrrays está perfectamente bien en el contexto de la pregunta. +1 - Tobia

@Tobia No creo que la mayoría de la gente esté de acuerdo con que clasifiques, digamos, [7 => 'foo', 2 => 'bar'] como una matriz "mixta" que es en parte pero no "puramente" secuencial. Eso me parece un uso de palabras claramente incorrecto. - marca amery

ya se han dado las respuestas, pero hay demasiada desinformación sobre el rendimiento. Escribí este pequeño script de referencia que muestra que el método foreach es el más rápido.

Descargo de responsabilidad: los siguientes métodos se copiaron y pegaron de las otras respuestas

<?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";
}

Resultados:

==== 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

Respondido 06 Jul 16, 08:07

O simplemente puede usar esto:

Arr::isAssoc($array)

que verificará si la matriz contiene cualquier tecla no numérica o bien:

Arr:isAssoc($array, true)

para comprobar si matriz es estrictamente secuencial (contiene claves int generadas automáticamente 0 a n-1)

usar este biblioteca.

Respondido el 05 de Septiembre de 18 a las 23:09

A veces puede salirse con la suya con solo verificar si la clave de la primera matriz es 0.

$isSequential = array_keys($arr)[0] === 0

, o una versión más rápida, pero más detallada:

reset($arr); $isSequential = key($arr) === 0

respondido 17 mar '20, 10:03

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.