PHP обрабатывает все массивы как ассоциативные, поэтому нет встроенных функций. Может ли кто-нибудь рекомендовать достаточно эффективный способ проверить, содержит ли массив только числовые ключи?
В принципе, я хочу иметь возможность различать это:
$sequentialArray = array('apple', 'orange', 'tomato', 'carrot');
и это:
$assocArray = array('fruit1' => 'apple',
'fruit2' => 'orange',
'veg1' => 'tomato',
'veg2' => 'carrot');
Вы задали два вопроса, которые не совсем эквивалентны:
Подумайте, какое из этих поведений вам действительно нужно. (Возможно, это будет либо для ваших целей.)
Первый вопрос (просто проверка того, что все ключи являются числовыми) ответил хорошо капитаном 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
isSequential()
будет иметь больше смысла, чем isAssoc()
. В такой функции пустой массив должен рассматриваться как последовательный. Формула может быть array() === $arr || !isAssoc($arr)
.
Чтобы просто проверить, имеет ли массив нецелые ключи (а не индексируется ли массив индексированным или нулевым индексом):
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:
Ключ может быть либо целым числом, либо строкой.
Это означает, что возможны 3 случая:
Мы можем проверить каждый случай со следующими функциями.
Примечание. Эта функция возвращает 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);
}
Примечание. Эта функция возвращает 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);
}
Примечание. Эта функция возвращает 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;
}
Отсюда следует, что:
Теперь, чтобы массив был "подлинным" массивом, к которому мы все привыкли, это означает:
Мы можем проверить со следующей функцией.
Примечание. Эта функция возвращает 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);
}
Ключи для этих массивов целые числа:
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("[email protected]" => "b"); // Strings may contain non-alphanumeric characters.
array("stack\t\"over\"\r\nflow 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 a valid hexadecimal number.
array("013" => "b"); // Not converted to integers even though it a valid octal number.
array("18446744073709551616" => "b"); // Not converted to integers as it can't fit into a 64-bit integer.
Что еще, в соответствии с doc,
Размер целого числа зависит от платформы, хотя максимальное значение около двух миллиардов - это обычное значение (это 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-битным целым числом.
Speed-накрест:
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 и т.д.).
true
для array(1=>"a")
но false
для array("a"=>"a")
. Будет более значимым, если !=
Будет заменен на !==
.
Я использовал как 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 опущена или если вы убедитесь, что она вызывается только с помощью целого числа. Но так как я не могу быть уверен в всех, я оставил его. Заявление выполняется только один раз. Я думаю, что это наиболее эффективные решения:
Помните, что ключ массива может быть только целым числом или строкой, а строчная числовая строка, такая как "1" (но не "01" ), будет переведена в целое число. Это то, что делает проверку целочисленного ключа единственной необходимой операцией, кроме подсчета, если вы хотите, чтобы массив был последовательным. Естественно, если is_indexed_array возвращает false, массив можно рассматривать как ассоциативный. Я говорю "видел", потому что на самом деле все они есть.
Эта функция может обрабатывать:
идея проста: если один из ключей НЕ является целым числом, это ассоциативный массив, в противном случае он последователен.
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()
выполняется намного быстрее.
$arrays = Array( 'Array #1' => range(0, 50000), );
Используя 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])
, тогда вызывается array_merge
, список переиндексируется, начиная с 0.
O(n)
в используемой дополнительной памяти (поскольку он создал несколько новых массивов с таким количеством элементов, как $array
), ответ не решает неоднозначность вопроса, который был задан, и не объясняет, как именно он определяет список / не -ассоциативный массив, и даже если ни одна из этих точек не соответствует действительности, неясно, добавляет ли это какую-либо ценность по сравнению с другими уже опубликованными ответами.
Это тоже сработает (demo):
function array_has_numeric_keys_only(array $array)
{
try {
SplFixedArray::fromArray($array, true);
} catch (InvalidArgumentException $e) {
return false;
}
return true;
}
Обратите внимание, что основная часть этого ответа состоит в том, чтобы сообщить вам о существовании SplFixedArray
и не поощрять использование исключений для этих видов тестов.
Если PHP не имеет встроенного для этого, вы не сможете сделать это меньше, чем O (n) - перечислите все ключи и проверите для целочисленного типа. Фактически, вы также хотите убедиться, что нет отверстий, поэтому ваш алгоритм может выглядеть так:
for i in 0 to len(your_array):
if not defined(your-array[i]):
# this is not an array array, it an associative array :)
Но зачем беспокоиться? Предположим, что массив имеет тот тип, который вы ожидаете. Если это не так, это просто взорвется вам в лицо - это динамическое программирование для вас! Проверьте свой код, и все будет хорошо...
function array_is_assoc(array $a) {
$i = 0;
foreach ($a as $k => $v) {
if ($k !== $i++) {
return true;
}
}
return false;
}
Быстрая, лаконичная и эффективная память. Нет дорогостоящих сравнений, вызовов функций или копирования массивов.
Я думаю, что определение скалярного массива будет отличаться в зависимости от приложения. То есть для некоторых приложений требуется более строгое представление о том, что квалифицируется как скалярный массив, а для некоторых приложений требуется более свободный смысл.
Ниже представлены 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 <[email protected]>
*/
/**
* 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;
}
<?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 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
Может ли это быть решением?
public static function isArrayAssociative(array $array) {
reset($array);
return !is_int(key($array));
}
Предостережение очевидно, что курсор массива reset, но я бы сказал, вероятно, что функция используется до того, как массив прошел или используется.
array("a", "b")
и для array("a", "b" => "B")
поскольку она проверяет только первый ключ. Кстати, is_long
это просто псевдоним is_int
.
Уже есть много ответов, но вот метод, который 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
array_keys($keys)
вернет последовательный массив чисел (0 ... X), который имеет ту же длину исходного массива. Например, array_keys(["a", "b", "c"]) = [0, 1, 2];
array_keys([0, 1, 2]) = [0, 1, 2]
(это последовательный массив, потому что [0, 1, 2] !== [0, 1, 2]
). Другой пример: array_keys(["a" => 5, "b" => 7, "c" => 10]) = ["a", "b", "c"];
array_keys(["a", "b", "c"]) = [0, 1, 2]
(это ассоциативный массив, потому что ["a", "b", "c"] !== [0, 1, 2]
). Надеюсь, это понятно (сложно объяснить в комментариях, по крайней мере, для меня)
ответы уже даны, но слишком много дезинформации о производительности. Я написал этот маленький тест script, который показывает, что метод 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
Один из способов приблизиться к этому - связать с json_encode
, у которого уже есть свой собственный внутренний метод дифференциации ассоциативного массива и индексированного массива для вывода правильного JSON.
Вы можете сделать это, проверив, является ли первый символ, возвращаемый после кодирования, {
(ассоциативный массив) или [
(индексированный массив).
echo substr(json_encode($the_array), 0, 1) == '{' ? 'yes' : 'no';
Еще один быстрый из источник.
Установите кодировку json_encode
(и bson_encode
). Таким образом, соответствие javascript Array.
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__)
);
}
}
isset
и array_key_exists
? Разве последнего не будет достаточно?
isset()
здесь полностью избыточна.
Я знаю это немного бессмысленно, добавляя ответ на эту огромную очередь, но здесь читаемое O (n) решение, которое не требует дублирования любых значений:
function isNumericArray($array) {
$count = count($array);
for ($i = 0; $i < $count; $i++) {
if (!isset($array[$i])) {
return FALSE;
}
}
return TRUE;
}
Вместо того, чтобы проверять ключи, чтобы увидеть, все ли они числовые, вы перебираете ключи, которые будут там для числового массива, и убедитесь, что они существуют.
[1,2,null,4]
потерпит неудачу, но это правильный массив. поэтому я добавил некоторые улучшения в stackoverflow.com/a/25206156/501831 с array_key_exists
добавления array_key_exists
)
isset()
здесь не тот инструмент, потому что он вернет false, если значение установлено, но имеет значение null
, как указывает @lazycommit.
Проверка наличия массива всех связанных ключей. С помощью stdClass
и get_object_vars ^):
$assocArray = array('fruit1' => 'apple',
'fruit2' => 'orange',
'veg1' => 'tomato',
'veg2' => 'carrot');
$assoc_object = (object) $assocArray;
$isAssoc = (count($assocArray) === count (get_object_vars($assoc_object)));
var_dump($isAssoc); // true
Почему? Функция get_object_vars
возвращает только доступные свойства (подробнее о том, что происходит при конвертировании array
в object
здесь). Тогда просто логически: если количество элементов базового массива равно количество доступных свойств объекта - все ключи связаны.
Несколько тестов:
$assocArray = array('apple', 'orange', 'tomato', 'carrot');
$assoc_object = (object) $assocArray;
$isAssoc = (count($assocArray) === count (get_object_vars($assoc_object)));
var_dump($isAssoc); // false
//...
$assocArray = array( 0 => 'apple', 'orange', 'tomato', '4' => 'carrot');
$assoc_object = (object) $assocArray;
$isAssoc = (count($assocArray) === count (get_object_vars($assoc_object)));
var_dump($isAssoc); // false
//...
$assocArray = array('fruit1' => 'apple',
NULL => 'orange',
'veg1' => 'tomato',
'veg2' => 'carrot');
$assoc_object = (object) $assocArray;
$isAssoc = (count($assocArray) === count (get_object_vars($assoc_object)));
var_dump($isAssoc); //false
Etc.
Простым способом вы можете проверить, является ли массив ассоциативным или нет по шагам
array_keys()
array_filter()
и
is_numeric()
Функция для вышеуказанного шага выглядит следующим образом.
function isAssociative(array $array)
{
return count(array_filter(array_keys($array), function($v){return is_numeric($v);})) !== count($array));
}
Улучшение от Mark Amery
function isAssoc($arr)
{
// Is it set, is an array, not empty and keys are not sequentialy numeric from 0
return isset($arr) && is_array($arr) && count($arr)!=0 && array_keys($arr) !== range(0, count($arr) - 1);
}
Это проверяет, существует ли переменная, если она является массивом, если она не является пустым массивом и если ключи не являются последовательными от 0.
Чтобы узнать, является ли массив ассоциативным
if (isAssoc($array)) ...
Чтобы узнать, является ли числовое число
if (!isAssoc($array)) ...
function is_array_assoc($foo) {
if (is_array($foo)) {
return (count(array_filter(array_keys($foo), 'is_string')) > 0);
}
return false;
}
На мой взгляд, массив должен приниматься как ассоциативный, если какой-либо из его ключей не является целым числом, например. float numbers и пустую строку ''.
Также целые числа без последовательности должны рассматриваться как ассоциативные (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;
}
IsAssoc()
возвращает false для массивов, отличных от нуля, например, array(91 => "a")
(как указано в ответе). 2. IsAssoc()
возвращает false, даже если ключи массива расположены не по порядку, например, array(1 => "a", 0 => "b", 2 => "c")
.
$i
в качестве переменной цикла в диапазоне от 0 до count($arr) - 1
, тогда массив должен быть проиндексирован с нуля , а не просто последовательно индексируется. Тем не менее, ваш код просто проверяет, что массив последовательно индексируется.
Я встретил эту проблему еще раз несколько дней назад, и я решил воспользоваться специальным свойством array_merge:
Если входные массивы имеют те же строковые ключи, то более позднее значение для этого ключа перезапишет предыдущий. Если, однако, массивы содержат числовые клавиши, значение позже не будет перезаписывать исходное значение, но будет добавлено. Значения во входном массиве с числовыми клавишами будут перенумерованы с добавочными клавишами, начиная с нуля в массиве результатов. Так почему бы не использовать:
function Is_Indexed_Arr($arr){
$arr_copy = $arr;
if((2*count($arr)) == count(array_merge($arr, $arr_copy))){
return 1;
}
return 0;
}
Модификация на самый популярный ответ.
Это требует немного большей обработки, но более точно.
<?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
?>
O(n²)
время для завершения, учитывая последовательный массив размером 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;
}
O(n)
дополнительной памяти, когда $array
имеет n
элементов, и запись (someboolean) ? false : true
вместо !someboolean
ужасен и !someboolean
.
function is_associative($arr) {
return (array_merge($arr) !== $arr || count(array_filter($arr, 'is_string', ARRAY_FILTER_USE_KEY)) > 0);
}
Мое решение состоит в том, чтобы получить ключи массива, как показано ниже, и проверить, что если ключ не является целым числом:
private function is_hash($array) {
foreach($array as $key => $value) {
return ! is_int($key);
}
return false;
}
Неправильно получить array_keys хэш-массива, как показано ниже:
array_keys(array(
"abc" => "gfb",
"bdc" => "dbc"
)
);
выведет:
array(
0 => "abc",
1 => "bdc"
)
Таким образом, это не очень хорошая идея, чтобы сравнить его с диапазоном чисел, как упоминается в рейтинге. Он всегда будет говорить, что это хэш-массив, если вы пытаетесь сравнить ключи с диапазоном.
array(1 => 'foo', 0 => 'bar')
не является последовательным, но пройдет ваш тест. Чтобы понять, почему это json_encode($array)
, попробуйте json_encode($array)
с последовательными и ассоциативными массивами.
Простое и удобное в работе решение, которое проверяет только первый ключ.
function isAssoc($arr = NULL)
{
if ($arr && is_array($arr))
{
foreach ($arr as $key => $val)
{
if (is_numeric($key)) { return true; }
break;
}
}
return false;
}
array("a", "b")
и для array("a", "b" => "B")
поскольку она проверяет только первый ключ (обратите внимание на return
и break
).
Лучшая функция для обнаружения ассоциативного массива (хэш-массив)
<?php
function is_assoc($arr) { return (array_values($arr) !== $arr); }
?>
Я просто использую функцию key(). Обратите внимание:
<?php
var_dump(key(array('hello'=>'world', 'hello'=>'world'))); //string(5) "hello"
var_dump(key(array('world', 'world'))); //int(0)
var_dump(key(array("0" => 'a', "1" => 'b', "2" => 'c'))); //int(0) who makes string sequetial keys anyway????
?>
Таким образом, просто проверяя false, вы можете определить, является ли массив ассоциативным или нет.
var_dump((bool)key(array(1=>"foo")));
возвращает истину. -1.
var_dump((bool)key(array("a"=>"foo")))
возвращает true ; var_dump((bool)key(array(""=>"foo")))
возвращает false . Кроме того, оба var_dump((bool)key(array("foo","bar")))
и var_dump((bool)key(array("0"=>"foo","a"=>"bar")))
возвращает false .
Еще один способ сделать это.
function array_isassociative($array)
{
// Create new Array, Make it the same size as the input array
$compareArray = array_pad(array(), count($array), 0);
// Compare the two array_keys
return (count(array_diff_key($array, $compareArray))) ? true : false;
}
Собственно, я оказался в подобной ситуации, пытаясь взять массив и проанализировать его в XML. Имена элементов XML не могут начинаться с цифр - и обнаруженные фрагменты кода неправильно обрабатывали массивы с числовыми индексами.
Подробности о моей конкретной ситуации ниже
Ответ, приведенный выше на @null (http://stackoverflow.com/a/173589/293332), на самом деле был довольно черным. Я был встревожен тем, что его проголосовали за то, что те, кто не понимает регулярное выражение, ведут очень разочаровывающие жизни.
В любом случае, основываясь на его ответе, вот что я закончил:
/**
* Checks if an array is associative by utilizing REGEX against the keys
* @param $arr <array> Reference to the array to be checked
* @return boolean
*/
private function isAssociativeArray( &$arr ) {
return (bool)( preg_match( '/\D/', implode( array_keys( $arr ) ) ) );
}
Смотрите Последовательности Escape PCRE и PCRE Синтаксис для более подробной информации.
Вот пример массива, с которым я имею дело:
Случай Areturn array(
"GetInventorySummary" => array(
"Filters" => array(
"Filter" => array(
array(
"FilterType" => "Shape",
"FilterValue" => "W",
),
array(
"FilterType" => "Dimensions",
"FilterValue" => "8 x 10",
),
array(
"FilterType" => "Grade",
"FilterValue" => "A992",
),
),
),
"SummaryField" => "Length",
),
);
Вывод состоит в том, что ключ filter
является переменной. Например:
return array(
"GetInventorySummary" => array(
"Filters" => array(
"Filter" => array(
"foo" => "bar",
"bar" => "foo",
),
),
"SummaryField" => "Length",
),
);
Если массив, который я преобразовываю, похож на Случай A, то я хочу вернуть:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<GetInventorySummary>
<Filters>
<Filter>
<FilterType>Shape</FilterType>
<FilterValue>W</FilterValue>
</Filter>
<Filter>
<FilterType>Dimensions</FilterType>
<FilterValue>8 x 10</FilterValue>
</Filter>
<Filter>
<FilterType>Grade</FilterType>
<FilterValue>A992</FilterValue>
</Filter>
</Filters>
<SummaryField>Length</SummaryField>
</GetInventorySummary>
... Однако, если массив, который я преобразовываю, похож на Случай B, то, что я хочу вернуть:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<GetInventorySummary>
<Filters>
<Filter>
<foo>bar</foo>
<bar>foo</bar>
</Filter>
</Filters>
<SummaryField>Length</SummaryField>
</GetInventorySummary>
isAssociativeArray()
возвращает false для array(4=>"four",9=>"nine")
, array("002"=>"two","007"=>"james")
и array("a", ""=>"empty", "b")
, которые явно ассоциативны.
function isAssoc($arr)
{
$a = array_keys($arr);
for($i = 0, $t = count($a); $i < $t; $i++)
{
if($a[$i] != $i)
{
return false;
}
}
return true;
}
[10=>'x', 20=>'y']
array("a"=>"b")
и для array("a","b")
.
Один дешевый и грязный способ - это проверить:
isset($myArray[count($myArray) - 1])
... вы можете получить ложное положительное значение, если ваш массив выглядит следующим образом:
$myArray = array("1" => "apple", "b" => "banana");
Более тщательный способ проверки ключей:
function arrayIsAssociative($myArray) {
foreach (array_keys($myArray) as $ind => $key) {
if (!is_numeric($key) || (isset($myArray[$ind + 1]) && $myArray[$ind + 1] != $key + 1)) {
return true;
}
}
return false;
}
// this will only return true if all the keys are numeric AND sequential, which
// is what you get when you define an array like this:
// array("a", "b", "c", "d", "e");
или
function arrayIsAssociative($myArray) {
$l = count($myArray);
for ($i = 0; $i < $l, ++$i) {
if (!isset($myArray[$i])) return true;
}
return false;
}
// this will return a false positive on an array like this:
$x = array(1 => "b", 0 => "a", 2 => "c", 4 => "e", 3 => "d");
arrayIsAssociative()
возвращает true для array("a", "b", "c")
и array("a", "b"=>"b", "c")
, но false для обоих array("a")
и array(2=>"a")
.
Еще один вариант пока не показан, так как он просто не принимает числовые ключи, но мне очень нравится Greg:
/* Returns true if $var associative array */
function is_associative_array( $array ) {
return is_array($array) && !is_numeric(implode('', array_keys($array)));
}
array(2=>'a',3=>'b')
, array('a','b')
, array("0x"=>'a','f'=>'g')
, array("90"=>'a',"17"=>'b')
, array(""=>'b',20=>'c')
.
Если вы ищете только нечисловые ключи (независимо от порядка), вы можете попробовать
function IsAssociative($array)
{
return preg_match('/[a-z]/i', implode(array_keys($array)));
}
array("@"=>"foo");
, И ... правда , правда ?
if (isset($array[0]))
, что просто и быстро. Конечно, вы должны сначала убедиться, что массив не пустой, и у вас должны быть некоторые знания о возможном содержимом массива, чтобы метод не мог завершиться с ошибкой (например, смешанный числовой / ассоциативный или непоследовательный).