Как проверить, является ли массив PHP ассоциативным или последовательным?

PHP рассматривает все массивы как ассоциативные, поэтому нет встроенных функций. Может ли кто-нибудь порекомендовать достаточно эффективный способ проверить, содержит ли массив только числовые ключи?

в принципе, я хочу иметь возможность различать это:

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

и так:

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

30 ответов


вы задали два вопроса, не совсем эквивалентны:

  • во-первых, как определить, имеет ли массив только числовые ключи
  • во-вторых, как определить, имеет ли массив последовательный цифровые клавиши, начиная с 0

Подумайте, Какое из этих действий вам действительно нужно. (Возможно, для ваших целей подойдет и то, и другое.)

первый вопрос (просто проверяя, что все ключи являются числовыми) хорошо ответил капитан Куро.

для второго вопроса (проверка, является ли массив нулевым индексированным и последовательным) вы можете использовать следующую функцию:

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

чтобы просто проверить, имеет ли массив нецелочисленные ключи (не является ли массив последовательно индексированным или нулевым):

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

Если есть хотя бы один ключевой строке, $array будет рассматриваться как ассоциативный массив.


конечно, это лучшая альтернатива.

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

многие комментаторы в этом вопросе не понимают, как работают массивы в PHP. От массив документации:

ключ может быть целым числом или строкой. Если ключ является стандартным представлением целого числа, он будет интерпретироваться как таковой (т. е. "8" будет интерпретироваться как 8, а "08" будет интерпретироваться как "08"). Поплавки в ключе усекаются до integer. Индексированные и ассоциативные типы массивов являются одним и тем же типом в PHP, который может содержать целочисленные и строковые индексы.

другими словами, нет такой вещи, как ключ массива "8", потому что он всегда будет (молча) преобразовать в целое число 8. Поэтому нет необходимости пытаться различать целые числа и числовые строки.

Если вы хотите наиболее эффективный способ проверить массив для нецелочисленных ключей, не делая копию части массива (например, array_keys() делает) или все это (как foreach делает):

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

это работает потому что key () возвращает NULL, когда текущая позиция массива недопустима, и NULL никогда не может быть допустимым ключом (если вы пытаетесь использовать NULL в качестве ключа массива, он тихо преобразуется в"").


As заявлено OP:

PHP рассматривает все массивы как ассоциативные

не совсем разумно (IMHO) писать функцию, которая проверяет, является ли массив ассоциативные. Итак, первым делом:что такое ключ в массиве PHP?:

на ключ может быть целое или строка.

это означает, что есть 3 возможных варианта:

  • корпус 1. все ключи цифровой / чисел.
  • корпус 2. все ключи строки.
  • корпус 3. некоторые ключи строки, некоторые клавиши цифровой / чисел.

мы можем проверить каждый случай со следующими функциями.

Случай 1: все ключи цифровой / чисел.

Примечание: эта функция возвращает правда для пустых массивов тоже.

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

случай 2: все ключи строки.

Примечание: эта функция возвращает правда для пустых массивов тоже.

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

корпус 3. некоторые ключи строки, некоторые клавиши цифровой / чисел.

Примечание: эта функция возвращает правда для пустых массивов тоже.

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

из этого следует, что:


теперь, чтобы массив был "подлинной" массива что мы все привыкли, что означает:

  • его ключи все цифровой / чисел.
  • его ключи последовательный (т. е. увеличение на Шаг 1).
  • ключи начать с нуля.

мы можем проверить с помощью следующей функции.

дело 3а. ключицифровой / чисел, последовательный и С нуля.

Примечание: эта функция возвращает правда для пустых массивов тоже.

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

предостережения / подводные камни (или, еще более странные факты о ключах массива в PHP)

целое число клавиш

ключи для этих массивов чисел:

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

строковые ключи

ключи для этих массивов строки:

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!

целочисленными ключами это похоже на струны

если вы думаете, что ключ в array("13" => "b") это строка, ты не прав. От доктора здесь:

строки, содержащие допустимые целые числа, будут приведены к целочисленному типу. Е. Г. ключом "8" будет храниться до 8 лет. С другой стороны," 08 " не будет приведено, поскольку это не допустимое десятичное целое число.

например, ключ для этих массивов чисел:

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

но ключом для этих массивов являются строки:

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.

более того, согласно doc,

размер integer зависит от платформы, хотя максимальное значение около двух миллиардов привычное значение (это 32-битное знаковое). 64-разрядные платформы обычно имеют максимальное значение около 9E18, за исключением Windows, которая всегда 32 бит. PHP не поддерживает целое без знака.

Итак, ключ для этого массива может или не может быть целое - это зависит от вашей платформы.

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

еще хуже, PHP имеет тенденцию быть багги если целое число возле 231 = граница 2,147,483,648 (см. 51430 ошибка, 52899 ошибка). Например, в моей локальной среде (PHP 5.3.8 на XAMPP 1.7.7 в Windows 7),var_dump(array("2147483647" => "b")) дает

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

но на это живая демонстрация на codepad (PHP 5.2.5), то же выражение дает

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

Итак, ключ-это целое в одной среде, но строка в другом, хотя 2147483647 является допустимым 32-разрядным со знаком целое.


скорость-мудрый:

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

памяти-мудрый:

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

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

на самом деле самый эффективный способ таков:

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

это работает, потому что она сравнивает ключи (которые для последовательного массива всегда 0,1,2 и т. д.) ключи ключи (которые всегда быть 0,1,2 и т. д.).


я использовал как array_keys($obj) !== range(0, count($obj) - 1) и array_values($arr) !== $arr (которые являются двойниками друг друга, хотя второе дешевле первого), но не для очень больших массивов.

это так array_keys и array_values очень дорогостоящие операции (так как они строят целый новый массив размером примерно с оригиналом).

следующая функция более надежна, чем методы, приведенные выше:

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

также обратите внимание, что если вы не хотите различать разреженные массивы из ассоциативных массивов вы можете просто вернуть 'assoc' как if блоки.

наконец, хотя это может показаться гораздо менее "элегантным", чем многие" решения " на этой странице, на практике это намного эффективнее. Практически любой ассоциативный массив будет обнаружен мгновенно. Только индексированные массивы будут проверены исчерпывающе, и описанные выше методы не только проверяют индексированные массивы исчерпывающе, они дублируют их.


Я думаю, что следующие две функции-лучший способ проверить "является ли массив ассоциативным или числовым". Поскольку "числовые" могут означать только числовые ключи или только последовательные числовые ключи, ниже перечислены две функции, которые проверяют любое условие:

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

первая функция проверяет, является ли каждый ключ целочисленным значением. Вторая функция проверяет, является ли каждый ключ целочисленным значением и, кроме того, проверяет, являются ли все ключи последовательными, начиная с $base, который по умолчанию равен 0 и таким образом, можно опустить, если вам не нужно указывать другое базовое значение. key ($my_array) возвращает null, если указатель чтения перемещается за конец массива, что завершает цикл for и делает оператор после цикла for возвращаемым true, если все ключи были целочисленными. Если нет, цикл заканчивается преждевременно, потому что ключ имеет тип string, а оператор после цикла for возвращает false. Последняя функция дополнительно добавляет один к $base после каждого сравнения, чтобы иметь возможность проверить, является ли следующий ключ правильного значения. Строгое сравнение также проверяет, имеет ли ключ тип integer. Часть $base = (int) $base в первом разделе цикла for может быть оставлена, когда $base опущена или если вы убедитесь, что она вызывается только с помощью целого числа. Но так как я не могу быть уверен за всех, я оставил его. В любом случае, оператор выполняется только один раз. Я думаю, что это наиболее эффективные решения:

  • память мудрая: нет копирования данных или диапазонов ключей. Делаю array_values или array_keys может показаться короче (меньше кода), но имейте в виду, что происходит в фоновом режиме, как только вы сделать этот призыв. Да, есть больше (видимых) утверждений, чем в некоторых других решениях, но это не то, что имеет значение, не так ли?
  • Time wise: помимо того, что копирование / извлечение данных и / или ключей также требует времени, это решение более эффективно, чем выполнение foreach. Опять же, foreach может показаться более эффективным для некоторых, потому что он короче в нотации, но в фоновом режиме foreach также вызывает сброс, ключ и рядом, чтобы сделать это цикл. Но кроме того, он также вызывает valid для проверки конечного условия, которого здесь избегают из-за комбинации с целочисленной проверкой.

помните, что ключ массива может быть только целым числом или строкой, а строго числовая строка, такая как "1" (но не "01"), будет переведена в целое число. Что делает проверку целочисленного ключа единственной необходимой операцией, кроме подсчета, если вы хотите, чтобы массив был последовательным. Естественно, если is_indexed_array возвращает false, массив можно рассматривать как ассоциативный. Я говорю "видел", потому что на самом деле они все такие.


эта функция может обрабатывать:

  • массив с отверстиями в индексе (например, 1,2,4,5,8,10)
  • массив с ключами "0x": например, ключ " 08 "ассоциативен, а ключ" 8 " последователен.

идея проста: если один из ключей не является целым числом, это ассоциативный массив, иначе он последовательный.

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

Я заметил два популярных подхода к этому вопросу: один с использованием array_values() и другие, используя key(). Чтобы узнать, что быстрее, я написал небольшую программу:

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

вывод для программы на PHP 5.2 на CentOS выглядит следующим образом:

время, затраченное на метод #1 = 10.745 ms
Время, затраченное с помощью метода #2 = 18.239 ms

вывод на PHP 5.3 дал аналогичные результаты. Очевидно, используя array_values() гораздо быстрее.


ответов уже много, но вот метод, на который опирается Laravel в своем классе 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;
}

источник:https://github.com/laravel/framework/blob/5.4/src/Illuminate/Support/Arr.php


один из способов подойти к этому-вернуться на json_encode, который уже имеет свой собственный внутренний метод дифференцирования между ассоциативным массивом и индексированным массивом для вывода правильного JSON.

вы можете сделать это, проверив, является ли первый символ, возвращенный после кодирования { (ассоциативный массив) или a [ (индексный массив).

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

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

быстрый, сжатый, и память эффективная. Никаких дорогостоящих сравнений, вызовов функций или копирования массива.


С помощью xarray расширение PHP

Вы можете сделать это очень быстро (примерно в 30 раз быстрее в PHP 5.6):

if (array_is_indexed($array)) {  }

или:

if (array_is_assoc($array)) {  }

мое решение:

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

array_merge на одном массиве будет переиндексировать все integer ключи, но не другие. Например:

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

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

Итак, если создается список (неассоциативный массив)['a', 'b', 'c'] затем значение удалено unset($a[1]) затем , список переиндексируется, начиная с 0.


вот метод, который я использую:

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

обратите внимание, что это не учитывает особые случаи, такие как:

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

unset($a[1]);

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

к сожалению, не могу помочь вам с этим. Он также несколько эффективен для массивов приличного размера, поскольку он не делает ненужных копий. Именно эти мелочи делают Python и Ruby намного приятнее писать... : P


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

?>

оба этих примера, которые набрали наибольшее количество очков, не работают правильно с массивами, такими как $array = array('foo' => 'bar', 1)


это тоже сработает (демо):

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

обратите внимание, что основной смысл этого ответа-сообщить вам о существовании SplFixedArray и не поощрять вас использовать исключения для этих видов тестов.


Я думаю, что определение скалярного массива будет варьироваться в зависимости от приложения. То есть некоторым приложениям потребуется более строгое понимание того, что квалифицируется как скалярный массив, а некоторым приложениям потребуется более свободный смысл.

ниже я представляю 3 метода различной строгости.

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

может ли это быть решением?

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

оговорка, очевидно, что курсор массива сбрасывается, но я бы сказал, что функция используется до того, как массив даже будет пройден или использован.


Если PHP не имеет встроенного для этого, вы не сможете сделать это менее чем за O(n) - перечисление всех ключей и проверка на целочисленный тип. На самом деле, вы также хотите убедиться, что нет отверстий, поэтому ваш алгоритм может выглядеть так:

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 :)

но зачем? Просто предположим, что массив имеет тип, который вы ожидаете. Если это не так, он просто взорвется в вашем лице - это динамическое программирование для вас! Проверьте свой код и все будет хорошо...


Я знаю, что немного бессмысленно добавлять ответ в эту огромную очередь, но вот читаемое решение O(n), которое не требует дублирования каких-либо значений:

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

вместо того, чтобы проверять ключи, чтобы видеть, являются ли они все числовыми, вы перебираете ключи, которые б будьте там для числового массива и убедитесь, что они существуют.


еще один быстрый от источник. Подходит кодировка json_encodebson_encode). Так же как и соответствие массиву 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__)
        );
    }
}

ответы уже даны, но слишком много дезинформации о производительности. Я написал этот небольшой тестовый скрипт, который показывает, что метод foreach является самым быстрым.

отказ от ответственности: следующие методы были скопированы с других ответов

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

результаты:

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

после некоторого локального бенчмаркинга, отладки, проверки компилятора, профилирования и злоупотребления 3v4l.org чтобы проверить больше версий (да, я получил предупреждение остановиться) и сравнивая со всеми вариациями, которые я мог найти...

Я даю вам органически производное лучший-средний-худший сценарий ассоциативная тестовая функция массива, которая находится в хуже примерно так хорошо, как или лучше, чем все другие типичные сценарии.

да помилует Бог наших людская душа.

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

от 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";
}

Я сравниваю разницу между ключами массива и ключами результата array_values () массива, который всегда будет массивом с целочисленными индексами. Если ключи одинаковы, это не ассоциативный массив.

function isHash($array) {
    if (!is_array($array)) return false;
    $diff = array_diff_assoc($array, array_values($array));
    return (empty($diff)) ? false : true;
}

модификация самого популярного ответа.
Это требует немного больше обработки, но более точно.

<?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,2,4,6), потому что такие массивы не могут использоваться с циклами таким образом:

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

вторая часть функции ниже проверяет, индексируются ли ключи или нет.Он также работает для ключей с отрицательными значениями. Например (-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;
}