Pregunta ¿Cómo comprobar si PHP array es asociativo 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 claves numéricas?

Básicamente, quiero ser capaz de diferenciar entre esto:

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

y esto:

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

662


origen


Respuestas:


Has 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 lo haga para sus propósitos).

La primera pregunta (simplemente verificando que todas las teclas sean numéricas) es respondió bien por el Capitán kurO.

Para la segunda pregunta (verificando si la matriz está indexada en cero y 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(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



Para simplemente verificar si la matriz tiene claves no enteras (no si la matriz está indexada secuencialmente o sin índice):

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 será considerado como una matriz asociativa.


388



Sin duda esta es una mejor alternativa.

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

122



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

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

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

Si desea la manera más eficiente de comprobar una matriz para claves no enteras sin hacer una copia de una parte de la matriz (como array_keys () lo hace) o todo (como foreach):

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

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 una clave de matriz, se convierte silenciosamente a "").


73



Como declarado por el OP:

PHP trata todas las matrices como asociativas

no es del todo sensato (en mi humilde opinión) escribir una función que compruebe si una matriz es de asociación. Entonces, lo primero es lo primero: qué es una clave en una matriz de PHP?:

los llave puede ser un entero o una cuerda.

Eso significa que hay 3 posibles casos:

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

Podemos verificar cada caso con las siguientes funciones.

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

Nota: Esta función regresa cierto para arreglos vacíos 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 regresa cierto para arreglos vacíos 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 regresa cierto para arreglos vacíos 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 una arreglo "genuino" a lo que todos estamos acostumbrados, es decir:

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

Podemos verificar con la siguiente función.

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

Nota: Esta función regresa cierto para arreglos vacíos 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 / Errores (o incluso hechos más peculiares sobre las teclas 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!

Claves enteras que parecen cadenas

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

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

Por ejemplo, la clave para estas matrices es enteros:

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

Pero la clave para estas matrices son 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 doc,

El tamaño de un entero depende de la plataforma, aunque un valor máximo de aproximadamente dos mil millones es el valor habitual (es decir, 32 bits firmados). Las plataformas de 64 bits generalmente tienen un valor máximo de aproximadamente 9E18, a excepción de Windows, que siempre es de 32 bits. PHP no admite enteros sin signo.

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 de los 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 la pantalla de códigos (PHP 5.2.5), la misma expresión da

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

Entonces la clave es una entero en un ambiente pero una cuerda en otro, aunque 2147483647 es un válido firmado de 32 bits entero.


36



Velocidad-sabio:

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

31



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

18



En realidad, la forma más eficiente es así:

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

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


18



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

Esto es porque array_keys y array_values son operaciones muy costosas (ya que construyen una matriz completamente nueva de tamaño aproximadamente la del original).

La siguiente función es más sólida 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 las matrices dispersas de las matrices asociativas, simplemente puede devolver 'assoc' de ambos if bloques.

Finalmente, aunque 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 exhaustivamente, sino que las duplican.


16



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 las teclas numéricas o solo las teclas numéricas secuenciales, a continuación se enumeran dos funciones que verifican cualquiera de las dos 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 clave es un valor entero. La segunda función comprueba si cada clave es un valor entero y además comprueba 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 null 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 instrucción después del ciclo for devuelva true si todas las claves son enteros. De lo contrario, el bucle finaliza prematuramente porque una clave es de tipo cadena, y la instrucción después del bucle for devolverá falsa. 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 compruebe si la clave es de tipo entero. La parte $ base = (int) $ base en la primera sección del ciclo for se puede omitir cuando se omite $ base o si se asegura de que solo se invoque usando un número entero. Pero como no puedo estar seguro para todos, lo dejé. La declaración se ejecuta solo una vez, de todos modos. Creo que estas son las soluciones más eficientes:

  • En cuanto a la memoria: no se copian datos o rangos de teclas. Hacer un array_values ​​o array_keys puede parecer más corto (menos código), pero tenga en cuenta lo que sucede en el fondo una vez que realiza esa llamada. Sí, hay más declaraciones (visibles) que en otras soluciones, pero eso no es lo que cuenta, ¿verdad?
  • Timewise: 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. De nuevo, un foreach puede parecer más eficiente para algunos porque es más breve en notación, pero en el fondo foreach también llama a reset, key y next to do it's looping. Pero además también llama válido para verificar la condición final, que se evita aquí debido a la combinación con la verificación de número entero.

Recuerde que una clave de matriz solo puede ser un entero o una cadena, y una cadena estrictamente numérica como "1" (pero no "01") se traducirá en un entero. Esto es lo que hace que buscar 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 false, la matriz puede verse como asociativa. Digo 'visto', porque de hecho todos lo son.


13