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

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

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

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

и это:

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

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

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

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

Первый вопрос (просто проверка того, что все ключи являются числовыми) отвечает капитану kurO .

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

 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»). Поплавки в ключе усекаются до целого. Индексированные и ассоциативные типы массивов являются одним и тем же типом в 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 в качестве ключа массива, он беззвучно преобразуется в «»).

Как заявляет OP :

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

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

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

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

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

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

Случай 1: все ключи являются числовыми / целыми числами .

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

 //! 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: все клавиши являются строками .

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

 //! 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. Некоторые клавиши являются строками , некоторые ключи являются числовыми / целыми числами .

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

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

Следует, что:

  • Если значение не является массивом , все 3 функции возвращают false .
  • Если значение представляет собой пустой массив , все 3 функции возвращают true
    (что по определению, как и в « пустое множество, является подмножеством любого множества А, потому что все его элементы принадлежат А ").
  • Если значение является непустым массивом , точно 1 функция возвращает true .

Теперь, когда массив будет «подлинным» массивом, к которому мы все привыкли, это означает:

  • Его ключи – это числовые / целые числа .
  • Его ключи являются последовательными (т.е. увеличиваются на шаг 1).
  • Его ключи начинаются с нуля .

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

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

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

 //! 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("http://img.ruphp.com/php/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. 

Более того, согласно документу ,

Размер целого зависит от платформы, хотя максимальное значение около двух миллиардов – это обычное значение (это 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 имеет тенденцию быть ошибкой, если целое число находится рядом с границей 2 31 = 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" } 

но на этом живом демо на кодепаде (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, если все ключи были целыми. Если нет, цикл заканчивается преждевременно, потому что ключ имеет строку типа, а оператор после цикла for возвращает false. Последняя функция дополнительно добавляет от одной до $ базы после каждого сравнения, чтобы проверить, имеет ли следующий ключ правильное значение. Строгое сравнение также позволяет проверить, имеет ли ключ целочисленный тип. Базовая часть $ base = (int) $ в первом разделе цикла for может быть опущена, когда $ base опущена или если вы убедитесь, что она вызывается только с помощью целого числа. Но так как я не могу быть уверен в всех, я оставил его. Заявление выполняется только один раз. Я думаю, что это наиболее эффективные решения:

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

Помните, что ключ массива может быть только целым числом или строкой, а строчная числовая строка, такая как «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мс
Время, затраченное на метод # 2 = 18,239 мс

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

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

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

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

Это тоже будет работать ( демо ):

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

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

Используя расширение PHP xarray

Вы можете сделать это очень быстро (примерно в 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]) тогда array_merge , список переиндексируется, начиная с 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) ); с $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)

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

Ниже я представляю 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; } 

Could this be the solution?

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

The caveat is obviously that the array cursor is reset but I'd say probably the function is used before the array is even traversed or used.

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

Fast, concise, and memory efficient. No expensive comparisons, function calls or array copying.

I know it's a bit pointless adding an answer to this huge queue, but here's a readable O(n) solution that doesn't require duplicating any values:

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

Rather than check the keys to see if they are all numeric, you iterate over the keys that would be there for a numeric array and make sure they exist.

One more fast from source . Fit encoding of json_encode (and bson_encode ). So has javascript Array compliance.

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

answers are already given but there's too much disinformation about performance. I wrote this little benchmark script that shows that the foreach method is the fastest.

Disclaimer: following methods were copy-pasted from the other answers

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

There are many answers already, but here is the method that Laravel relies on within its Arr class:

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

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

One way to approach this is to piggyback on json_encode , which already has its own internal method of differentiating between an associative array and an indexed array in order to output the correct JSON.

You can do this by checking to see if the first character returned after encoding is a { (associative array) or a [ (indexed array).

 echo substr(json_encode($the_array), 0, 1) == '{' ? 'yes' : 'no'; 

I compare the difference between the keys of the array and the keys of the result of array_values() of the array, which will always be an array with integer indices. If the keys are the same, it's not an associative array.

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

Modification on the most popular answer.
This takes a little more processing, but is more accurate.

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

In my opinion, an array should be accepted as associative if any of its keys is not integer eg float numbers and empty string ''.

Also non-sequenced integers has to be seen as associative like (0,2,4,6) because these kind of arrays cannot be used with for loops by this way:

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

The second part of the function below does check if the keys are indexed or not.It also works for keys with negative values. For example (-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; } 
 function is_array_assoc($foo) { if (is_array($foo)) { return (count(array_filter(array_keys($foo), 'is_string')) > 0); } return false; }