PHP рассматривает все массивы как ассоциативные, поэтому встроенных функций нет. Может ли кто-нибудь порекомендовать довольно эффективный способ проверить, содержит ли массив только числовые ключи?
По сути, я хочу различать это:
$sequentialArray = [
'apple', 'orange', 'tomato', 'carrot'
];
и это:
$assocArray = [
'fruit1' => 'apple',
'fruit2' => 'orange',
'veg1' => 'tomato',
'veg2' => 'carrot'
];
У этого метода есть предостережения, но часто я просто использую if (isset($array[0])), это просто и быстро. Конечно, сначала вы должны убедиться, что массив не пуст, и вы должны иметь некоторые знания о возможном содержимом массива, чтобы метод не смог дать сбой (например, смешанный числовой / ассоциативный или непоследовательный).
@ OlleHärstedt Not в соответствии с Высокий суд США. ;-)






Если 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 :)
Но зачем беспокоиться? Предположим, что это массив того типа, который вы ожидаете. В противном случае он просто взорвется вам в лицо - это динамическое программирование для вас! Протестируйте свой код, и все будет хорошо ...
Обычно можно просто предположить, что массив является желаемым типом. Но в моем случае я просматриваю многомерный массив и форматирую вывод в зависимости от того, к какому типу массива относится данный узел.
Один дешевый и грязный способ - это проверить так:
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");
+1 для метода isset. Да, это грязно, но это единственный метод, который использует O (1) вместо O (n).
Полный arrayIsAssociative() возвращает правда как для array("a", "b", "c"), так и для array("a", "b"=>"b", "c"), но ложный для array("a") и array(2=>"a").
function is_associative($arr) {
return (array_merge($arr) !== $arr || count(array_filter($arr, 'is_string', ARRAY_FILTER_USE_KEY)) > 0);
}
implode принимает 2 аргумента, плюс эта функция вернет false для массива, определенного следующим образом: $ x = array ("1" => "b", "0" => "a");
Параметр glue для implode () стал необязательным в PHP 4.3.0. Ваш пример массива - $ x = array ("1" => "b", "0" => "a"); - имеет ассоциативный индекс непоследовательных строк. is_associative () вернет true для этого массива, как и ожидалось.
Мне нравится этот. Первое условное выражение обнаружит ассоциативные массивы, где числовые индексы не являются последовательными в числовом отношении или где первый индекс не равен «0», потому что array_merge будет повторно индексировать ключи численно индексированного (но, возможно, ассоциативного) массива.
-1; при этом используется дополнительная память O(n), когда $arr имеет элементы n, плюс нет объяснения того, что он делает, или исследования двусмысленности заданного вопроса. Он также рассматривает массив, который имеет последовательные числовые ключи и пустая строка в качестве ключа, как неассоциативный, что не поддается никакому разумному определению, которое можно было бы составить между «ассоциативным» и «последовательным» массивами.
@MarkAmery Интересный момент о пустой строке в качестве ключа.
Вы задали два вопроса, которые не совсем эквивалентны:
Подумайте, какое из этих поведений вам действительно нужно. (Возможно, для ваших целей подойдет любой из них.)
Первый вопрос (просто проверка того, что все клавиши числовые) - хорошо ответил капитан КурО.
Для второго вопроса (проверка того, является ли массив с нулевым индексом и последовательным), вы можете использовать следующую функцию:
function isAssoc(array $arr)
{
if (array() === $arr) return false;
return array_keys($arr) !== range(0, count($arr) - 1);
}
var_dump(isAssoc(['a', 'b', 'c'])); // false
var_dump(isAssoc(["0" => 'a', "1" => 'b', "2" => 'c'])); // false
var_dump(isAssoc(["1" => 'a', "0" => 'b', "2" => 'c'])); // true
var_dump(isAssoc(["a" => 'a', "b" => 'b', "c" => 'c'])); // true
Очень элегантное решение. Обратите внимание, что он возвращает ИСТИНА в (неоднозначном) случае пустого массива.
Я думаю, что более полезно рассматривать последовательные массивы как частный случай ассоциативных массивов. Таким образом, каждый массив ассоциативен, но только некоторые из них являются последовательными. Следовательно, функция isSequential() будет иметь больше смысла, чем isAssoc(). В такой функции пустой массив должен рассматривается как последовательный. Формула может быть array() === $arr || !isAssoc($arr).
Я думаю, что это позволило бы избежать большого количества потенциального времени и памяти процессора, если бы можно было проверить, является ли isset ($ arr [0]) ложным, прежде чем извлекать все ключи, поскольку он явно ассоциативен, если массив не пуст, но не имеет элемента в 0 позиция. Поскольку «большинство» реальных ассоциативных массивов имеют строки в качестве ключей, это должно быть хорошей оптимизацией для общего случая такой функции.
@OderWat - ваша оптимизация должна использовать array_key_exists вместо isset, потому что, если нулевой элемент является нулевым значением, isset вернет false неправильно. Нулевое значение обычно должно быть допустимым значением в таком массиве.
@MAChitgarha, ваша правка изменила поведение функции без объяснения причин и заставила его противоречить описанию в прозе выше того, что она на самом деле должна делать. Я вернул это.
почему второй корпус уступает false? из-за ключа "0"?
Не уверен, что это все еще работает с отрицательными ключами массива: wiki.php.net/rfc/negative_array_index
В этом ответе используется пространство O(n). Хорошее решение должно использовать O(1) IMHO.
Если вы ищете только нечисловые клавиши (независимо от порядка), вы можете попробовать
function IsAssociative($array)
{
return preg_match('/[a-z]/i', implode(array_keys($array)));
}
array("@"=>"foo");. And ... preg, В самом деле?
Еще один способ сделать это.
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;
}
Конечно, это лучшая альтернатива.
<?php
$arr = array(1,2,3,4);
$isIndexed = array_values($arr) === $arr;
Это приведет к дублированию значений в массиве, что потенциально очень дорого. Намного лучше изучить ключи массива.
Если одно из значений массива не установлено, этот код больше не возвращает допустимый результат (например, unset ($ arr [0]); $ isIndexed = array_values ($ arr) === $ arr; // возвращает false.)
Пожалуйста, посмотрите мой ответ ниже, который, хотя и не содержательный, один лайнер на самом деле работает для больших массивов.
$a=array(1=>"foo"); var_dump(array_values($a)===$a); or even: $a=array(1,2,3,4); unset($a[2]); var_dump(array_values($a)===$a);Я просто использовал ==; Я не думаю, что здесь нужен ===. Но чтобы ответить на вопрос «не установлен, и он не работает»: как только вы отключите первый элемент, это больше не массив с целочисленным индексом, начиная с 0. Итак, IMO это работает.
Согласитесь с @grantwparks: разреженный массив не индексируется. Интересно, потому что на самом деле нет способа удалить элемент из середины индексированного массива. PHP в основном объявляет все массивы как ассоциативные, а числовые - это просто версия «придумай для меня ключ».
Единственная проблема, с которой я столкнулся, заключается в том, что === будет тратить время на проверку равенства значений, даже если нас интересуют только ключи. По этой причине я предпочитаю версию $k = array_keys( $arr ); return $k === array_keys( $k );.
Дополнительное примечание, это не работает с массивами, указанными с помощью цифровых клавиш, которые не в порядке. например $ myArr = array (0 => 'a', 3 => 'b', 4 => 1, 2 => 2, 1 => '3'); Одно из возможных решений - запустить ksort ($ arr) перед выполнением теста.
function isAssoc($arr)
{
$a = array_keys($arr);
for($i = 0, $t = count($a); $i < $t; $i++)
{
if ($a[$i] != $i)
{
return false;
}
}
return true;
}
это предполагает, что массив проиндексирован с 0, что не обязательно верно и даст неправильные результаты для массивов, таких как [10=>'x', 20=>'y'].
Эта функция возвращает правда как для array("a"=>"b"), так и для array("a","b").
Я просто использую функцию 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????
?>
Таким образом, просто проверив ложь, вы можете определить, является ли массив ассоциативным или нет.
Нет. var_dump((bool)key(array(1=>"foo"))); возвращает истину. -1.
var_dump((bool)key(array("a"=>"foo"))) возвращает правда; var_dump((bool)key(array(""=>"foo"))) возвращает ложный. Кроме того, как var_dump((bool)key(array("foo","bar"))), так и var_dump((bool)key(array("0"=>"foo","a"=>"bar"))) возвращают ложный.
На самом деле наиболее эффективный способ:
function is_assoc($array){
$keys = array_keys($array);
return $keys !== array_keys($keys);
}
Это работает, потому что он сравнивает ключи (которые для последовательного массива всегда 0,1,2 и т. д.) С ключами ключей (которые всегда будут 0,1,2 и т. д.).
Умно, но нехорошо. Почему это «самый эффективный»? Было бы намного удобнее просто сравнить array_keys ($ a) с диапазоном (0, count ($ a)). По моему опыту, самое умное решение редко бывает лучшим. Особенно, когда умение буквально не добавляет ценности очевидной и чистой альтернативе.
Эта функция возвращает true для array(1=>"a"), но false для array("a"=>"a"). Было бы более значимым, если бы != был заменен !==.
@ Pang, вы правы. Сначала я подумал, что ваш комментарий наверняка ошибочен, но, к моему удивлению, [0] == ['a'] в PHP (начиная с 0 == 'a' и, действительно, 0 == 'banana'). Оператор PHP == безумен.
Это неэффективно, поскольку включает вызов array_keys вместо простой проверки до тех пор, пока вы не найдете непоследовательный целочисленный индекс. Под капотом вы делаете это в любом случае, но вы уже продублировали большой массив.
Я сравниваю разницу между ключами массива и ключами результата array_values () массива, который всегда будет массивом с целочисленными индексами. Если ключи одинаковые, это не ассоциативный массив.
function isHash($array) {
if (!is_array($array)) return false;
$diff = array_diff_assoc($array, array_values($array));
return (empty($diff)) ? false : true;
}
-1; при этом используется дополнительная память O(n), когда $array имеет элементы n, а запись (someboolean) ? false : true вместо !someboolean ужасна и необоснованно многословна.
function checkAssoc($array){
return ctype_digit( implode('', array_keys($array) ) );
}
Это ответ Только (на момент моего комментария), который может иметь дело со следующим: $ array = array (0 => 'blah', 2 => 'yep', 3 => 'wahey')
но array('1'=>'asdf', '2'=>'too') будет рассматриваться как ассоциативный массив, хотя на самом деле это не так (ключи на самом деле являются строкой)
@CaptainkurO Вы имеете в виду числовые. Это ассоциативный массив.
Эта функция возвращает true, если ключи: ноль, целые числа (только положительные), пустая строка или любая комбинация вышеперечисленного, например строка «09». Эта функция не принимает во внимание порядок клавиш. Итак, array(0=>'blah', 2=>'yep', 3=>'wahey'), array(0=>'blah', 2=>'yep', 1=>'wahey') и array('blah', 'yep', 'wahey') ассоциативны в соответствии с этой функцией, а array('a'=>'blah', 'b'=>'yep', 'c'=>'wahey') - нет.
@CaptainkurO вы не правы. «1» и «2» будут сохранены как целые числа. Прочтите процитированную часть ответа squirrel от 11 мая 2011 г. в 19:34. PHP не хранит строковые ключи, которые выглядят в точности как целые числа. Он преобразует их в целые числа.
Я использовал как 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_asso($a){
foreach(array_keys($a) as $key) {if (!is_int($key)) return TRUE;}
return FALSE;
}
Если один из ключей НЕ является целым числом, он ЯВЛЯЕТСЯ ассоциативным по своей природе, однако он является последовательным только в том случае, если ключи идут от 0 - длина (массив) - 1. Однако он ЧИСЛЕННЫЙ, если все ключи только пронумерованы, но могут или может не работать со многими функциями массива, которым требуется последовательный массив. Если вы преобразуете числовой массив с отверстиями в последовательный, запустив на нем array_values (array), то он будет преобразован в последовательный.
Чтобы просто проверить, имеет ли массив нецелочисленные ключи (а не то, индексируется ли массив последовательно или с нулевым индексом):
function has_string_keys(array $array) {
return count(array_filter(array_keys($array), 'is_string')) > 0;
}
Если есть хотя бы один строковый ключ, $array будет рассматриваться как ассоциативный массив.
Этот метод намного лучше, чем кажется. Если count (filter_array) == count (original_array), то это ассоциированный массив. Если count (filter_array) == 0, то это индексированный массив. Если count (filter_array) <count (original_array), то массив имеет как числовые, так и строковые ключи.
Способ замедлить. Это выполнит итерацию и применит функцию. Посмотрите ниже более быстрые альтернативы или просто проверьте первый ключ на int.
Это возвращает false для ["1" => "foo", 2, 3]. Неужели нет способа проверить, был ли ключ изначально определен как строка? Кажется, что PHP все переводит в int в определении массива, если может. Жестокий.
@MikePretzlaw из курс он повторяет; (очевидно) невозможно определить, все ли ключи массива являются целыми числами, не глядя на все ключи в массиве. Я предполагаю, что не повторяющиеся альтернативы, которые мы должны увидеть ниже, - это такие, как $isIndexed = array_values($arr) === $arr;? На что я спрашиваю: как вы думаете, работает array_values()? Как вы думаете, как работает === применительно к массивам? Ответ, конечно, заключается в том, что они также перебирают массив.
@ARW «Кажется, что PHP все переводит в int в определении массива, если может». - да, именно так и происходит. Самый большой WTF в том, что он делает это даже с плавающими; если вы попробуете var_dump([1.2 => 'foo', 1.5 => 'bar']);, вы обнаружите, что получаете массив [1 => 'bar']. Нет никакой возможности узнать оригинальный тип ключа. Да, все это ужасно; Массивы PHP - это, безусловно, худшая часть языка, и большая часть ущерба непоправима и связана с идеей использования единой конструкции для традиционных массивов и традиционных хэш-карт, которая с самого начала была ужасной.
@MarkAmery Вышеупомянутое, хотя и простое, гарантирует 100% обход массива. Это было бы более эффективно, особенно если вы имеете дело с большими массивами, если бы вы проверяли строку или int и выскакивали при первом обнаружении. Например: function isAssociative($arr) { foreach ($arr as $key => $value) { if (is_string($key)) return true; } return false; }
@ Thought Ваш код работает очень быстро, но не может обнаружить последовательный массив. Пример array(1 => 'a', 0 => 'b', 2 => 'c') станет false (последовательный массив), тогда как он должен быть true (ассоциативный массив). toolsqa.com/data-structures/array-in-programming Я не уверен, что ключ должен быть в порядке возрастания? (0, 1, ...)
@vee - действительно, в самой первой строке ответа говорится, что этот ответ не определяет, есть ли у массива последовательные индексы. Для этого см. Ответ Марка Амери, который использует array_keys.
@ToolmakerSteve Меня упомянули в комментарии Мысль, а не в ответе (Капитан КурО). Это отличается.
Может ли это быть решением?
public static function isArrayAssociative(array $array) {
reset($array);
return !is_int(key($array));
}
Предостережение, очевидно, заключается в том, что курсор массива сбрасывается, но я бы сказал, что, вероятно, функция используется до того, как массив будет даже пройден или использован.
Эта функция возвращает ложный как для array("a", "b"), так и для array("a", "b" => "B"), поскольку она проверяет только первый ключ. Кстати, is_long - это просто псевдоним is_int.
Откровенно говоря, я думаю, что это было бы очень эффективно в подавляющем большинстве случаев, и намного эффективнее, чем альтернативы. Если вы понимаете последствия этого метода и понимаете, что он сработает для вас, это, вероятно, лучший выбор.
Это просто неправильно; он смотрит только на первый ключ.
@MarkAmery задан вопрос, как отличить последовательные массивы чисто от ассоциативных массивов чисто. Этот ответ делает именно это и является наиболее эффективным из всех. Наличие неопределенного поведения для массивов смешанный совершенно нормально в контексте вопроса. +1
@Tobia Я не думаю, что большинство людей согласятся с тем, что вы классифицируете, скажем, [7 => 'foo', 2 => 'bar'] как "смешанный" массив, который является частично, но не "чисто" последовательным. Мне это кажется совершенно неправильным употреблением слов.
@MarkAmery Ваш пример представляет собой последовательный числовой массив? Нет. Это ассоциативный массив (со строковыми ключами)? Нет. Поэтому это выходит за рамки этого ответа, ИМХО. Вы можете подумать о выводе yaml_parse_file(), если хотите (и о обычном способе записи файлов YAML). Вы получаете либо последовательные числовые массивы (если вы начинаете свои разделы с -), либо ассоциативные массивы на основе строк (если вы начинаете свои разделы с name:.) Нет промежуточных.
Модификация по самому популярному ответу
Это требует немного большей обработки, но более точный.
<?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
?>
-1; это займет время у O(n²), учитывая последовательный массив размером n. Это будет ужасно неэффективно для достаточно больших массивов.
Многие комментаторы, ответившие на этот вопрос, не понимают, как массивы работают в PHP. Из документация по массиву:
A key may be either an integer or a string. If a key is the standard representation of an integer, it will be interpreted as such (i.e. "8" will be interpreted as 8, while "08" will be interpreted as "08"). Floats in key are truncated to integer. The indexed and associative array types are the same type in PHP, which can both contain integer and string indices.
Другими словами, не существует такой вещи, как ключ массива «8», потому что он всегда будет (незаметно) преобразован в целое число 8. Таким образом, попытки различать целые числа и числовые строки не нужны.
Если вам нужен наиболее эффективный способ проверки массива на наличие нецелочисленных ключей без создания копии части массива (например, array_keys ()) или всего его (например, foreach):
function keyedNext( &$arr, &$k){
$k = key($arr);
return next($arr);
}
for ($k = key(reset($my_array)); is_int($k); keyedNext($my_array,$k))
$onlyIntKeys = is_null($k);
Это работает, потому что key () возвращает NULL, когда текущая позиция массива недействительна, и NULL никогда не может быть допустимым ключом (если вы попытаетесь использовать NULL в качестве ключа массива, он будет незаметно преобразован в "").
Это не работает для непоследовательных целочисленных ключей. Попробуйте это с помощью [2 => 'a', 4 => 'b'].
@DavidJ, что значит "не работает"? Он успешно определяет, что все ключи являются целыми числами. Вы утверждаете, что массив, подобный тому, который вы опубликовали, не следует рассматривать как «числовой массив»?
Неассоциативный массив должен иметь ключи в диапазоне от 0 до count($array)-1 в этом строгом порядке. Предварительная проверка с помощью is_array() может помочь. Добавьте увеличивающуюся переменную, чтобы проверить последовательность клавиш: for ($k = 0, reset($array) ; $k === key($array) ; next($array)) ++$k; На этом сделка рассчитывается.
Использование foreach вместо явной итерации примерно в два раза быстрее.
Если вы хотите превратить это в функцию: function isAssocStr($array) { for (reset($array); is_int(key($array)); next($array)) { if (is_null(key($array))) return false; } return true; }
Числовые массивы не обязательно должны иметь ключи от 0 до count($arr)-1. Эти два примера представляют собой идеальные неассоциативные массивы с непоследовательными ключами: $arr = [ 'A', 'B', 'C', 'D' ]; unset( $arr[2] ); или $arr = [ 2 => 'A', 4 => 'B', 9 => 'C', 'D' ];.
Мой любимый ответ здесь. Дублирование всех ключей может стать дорогостоящим, и это требует больших затрат памяти, которые могут почти вдвое превышать общую память, занимаемую набором данных.
Другой вариант, который еще не показан, так как он просто не принимает числовые ключи, но мне очень нравится вариант Грега:
/* 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').
@Pang: если бы вы могли прочитать: «Он просто не принимает числовые ключи», вы могли бы просто удалить отрицательный голос. Спасибо.
Ваш is_associative_array() возвращает ложный для ассоциативных массивов, таких как array("0x"=>'a','f'=>'g') и array(""=>'b',20=>'c'). Теперь я в замешательстве.
Вот метод, который я использую:
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_assoc($arr) { return (array_values($arr) !== $arr); }
?>
не работает для одноэлементных ассоциативных массивов
Разве это не повторяющийся ответ?
Можешь привести пример, Джину? Я не вижу такого поведения. (Хотя я использую ==, а не ===; я не знаю, почему так много людей используют ===.)
По скорости:
function isAssoc($array)
{
return ($array !== array_values($array));
}
По памяти:
function isAssoc($array)
{
$array = array_keys($array); return ($array !== array_keys($array));
}
следующий массив: array (02 => 11,1,2,456); показан как не имеющий цифровых ключей с использованием вышеуказанного алгоритма, даже если 02 === 2
Простое и удобное решение, которое проверяет только первый ключ.
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).
Я снова столкнулся с этой проблемой несколько дней назад и решил воспользоваться специальным свойством array_merge:
If the input arrays have the same string keys, then the later value for that key will overwrite the previous one. If, however, the arrays contain numeric keys, the later value will not overwrite the original value, but will be appended. Values in the input array with numeric keys will be renumbered with incrementing keys starting from zero in the result array. So why not to use:
function Is_Indexed_Arr($arr){
$arr_copy = $arr;
if ((2*count($arr)) == count(array_merge($arr, $arr_copy))){
return 1;
}
return 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) с последовательными и ассоциативными массивами.
да, я думаю, я очень запутался и застрял с приведенными выше ответами. Который продолжал сравнивать array_keys с диапазоном и думал, что у них будет результат, который будет сравнивать, является ли это хешем или нет. Итак, я отвечаю им, а также тем, кто думает, что array_keys дает значения последовательными. это все. А также имя функции is_hash, поэтому да, оно не говорит вам, является ли оно последовательным или нет
<?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).
+1 Ваш is_list () - ИМО лучший ответ. Некоторые люди не имеют ни малейшего представления о сложности времени и пространства, а также о встроенной функции и функции сценария PHP ...
Это тоже сработает (демонстрация):
function array_has_numeric_keys_only(array $array)
{
try {
SplFixedArray::fromArray($array, true);
} catch (InvalidArgumentException $e) {
return false;
}
return true;
}
Обратите внимание, что основная цель этого ответа - сообщить вам о существовании SplFixedArray, а не побуждать вас использовать исключения для такого рода тестов.
Собственно, я оказался в похожей ситуации, пытаясь взять массив и преобразовать его в XML. Имена XML-элементов не могут начинаться с цифр, а найденные мной фрагменты кода некорректно работали с массивами с числовыми индексами.
Details on my particular situation are below
Ответ, предоставленный выше @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 ) ) ) );
}
Дополнительную информацию см. На страницах Последовательности побега PCRE и Синтаксис PCRE.
Вот пример массива, с которым я имею дело:
Case 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",
),
);
Если преобразовываемый мной массив похож на Случай А, я хочу вернуть:
<?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() возвращает ложный для array(4=>"four",9=>"nine"), array("002"=>"two","007"=>"james") и array("a", ""=>"empty", "b"), которые явно ассоциативны.
Я думаю, что следующие две функции - лучший способ проверить, является ли массив ассоциативным или числовым. Поскольку «числовой» может означать только цифровые клавиши или только последовательные цифровые клавиши, ниже перечислены две функции, которые проверяют любое условие:
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 после каждого сравнения, чтобы иметь возможность проверить, имеет ли следующий ключ правильное значение. Строгое сравнение позволяет также проверять, имеет ли ключ целочисленный тип. Часть $ base = (int) $ base в первом разделе цикла for может быть опущена, если $ base опущено или если вы убедитесь, что она вызывается только с использованием целого числа. Но поскольку я не могу быть уверен для всех, я оставил это. В любом случае оператор выполняется только один раз. Думаю, это самые эффективные решения:
Помните, что ключ массива может быть только целым числом или строкой, а строго числовая строка, такая как «1» (но не «01»), будет преобразована в целое число. Именно поэтому проверка целочисленного ключа является единственной необходимой операцией помимо подсчета, если вы хотите, чтобы массив был последовательным. Естественно, если is_indexed_array возвращает false, массив можно рассматривать как ассоциативный. Я говорю «видел», потому что на самом деле они все.
Это лучший ответ. Определение «ассоциативного» или «числового» массива зависит от конкретной ситуации.
Если foreach менее эффективен, чем используемый здесь метод, то, помимо неудобства использования двух разных функций, производительность этого решения лучше, чем у моего (предыдущего). Я подозреваю, что это не так, поскольку foreach рекомендуется как самый быстрый способ пройти через массив.
Как заявлено ОП:
PHP treats all arrays as associative
не совсем разумно (IMHO) писать функцию, которая проверяет, является ли массив ассоциативный. Итак, первым делом: что такое ключ в массиве PHP ?:
The key can either be an integer or a string.
Это означает, что есть 3 возможных случая:
Мы можем проверить каждый случай с помощью следующих функций.
Примечание: Эта функция также возвращает правда для пустых массивов.
//! 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);
}
Примечание: Эта функция также возвращает правда для пустых массивов.
//! 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);
}
Примечание: Эта функция также возвращает правда для пустых массивов.
//! 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;
}
Это следует из того:
Теперь, чтобы массив был «подлинный» массив, к которому мы все привыкли, то есть:
Мы можем проверить с помощью следующей функции.
Примечание: Эта функция также возвращает правда для пустых массивов.
//! 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'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") - это нить, Вы неправы. Из документа здесь:
Strings containing valid integers will be cast to the integer type. E.g. the key "8" will actually be stored under 8. On the other hand "08" will not be cast, as it isn't a valid decimal integer.
Например, ключ для этих массивов - целые числа:
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.
Более того, согласно док,
The size of an integer is platform-dependent, although a maximum value of about two billion is the usual value (that's 32 bits signed). 64-bit platforms usually have a maximum value of about 9E18, except for Windows, which is always 32 bit. PHP does not support unsigned integers.
Таким образом, ключом для этого массива может или не может будет целое число - это зависит от вашей платформы.
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"
}
но на это живая демонстрация на кодовой панели (PHP 5.2.5) то же выражение дает
array(1) {
["2147483647"]=>
string(1) "b"
}
Таким образом, ключ - это целое число в одной среде, но нить в другой, даже если 2147483647 является действительным подписанным 32-битным целое число.
За исключением того, что я упоминаю ниже, он включает в себя создание массива, дублирующего проверяемый, что делает его очень дорогостоящим для больших массивов и потенциальным источником сбоев из-за нехватки памяти на общих хостах.
Я удивлен, что вы можете преобразовать «-13» в строку, но не «+13». Думаю, никто бы не стал писать «+13» вместо «13», но это все же несколько странно
Я думаю, что определение скалярного массива будет зависеть от приложения. То есть для некоторых приложений потребуется более строгое понимание того, что квалифицируется как скалярный массив, а для некоторых приложений потребуется более точное понимание.
Ниже я представляю 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;
}
Я заметил два популярных подхода к этому вопросу: один с использованием 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 выглядит следующим образом:
Time taken with method #1 = 10.745ms
Time taken with method #2 = 18.239ms
Вывод на PHP 5.3 дал аналогичные результаты. Очевидно, использование array_values() намного быстрее.
плохой тест. Вы не тестировали большие массивы. На моем компьютере, начиная с 10К + элементов, метод №2 работает быстрее. Попробуйте с $arrays = Array( 'Array #1' => range(0, 50000), );
Я знаю, что добавлять ответ в эту огромную очередь бессмысленно, но вот удобочитаемое решение 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)
-1; isset() - неправильный инструмент, потому что он вернет false, если значение установлено, но null, как указано @lazycommit.
function is_array_assoc($foo) {
if (is_array($foo)) {
return (count(array_filter(array_keys($foo), 'is_string')) > 0);
}
return false;
}
-1 за полное отсутствие объяснения. Сброс еще одного образца кода на вопрос, на который есть ответы 42, без каких-либо объяснений, почему предпочесть его альтернативам, абсолютно никому не поможет. Кроме того, учитывая неоднозначность вопроса и аргументы, которые бушуют по нему в комментариях, здесь кажется необходимым некоторое объяснение того, как именно вы определяете «ассоциативный» или «последовательный» массив.
Мое решение:
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.
-1; это O(n) в дополнительной используемой памяти (поскольку он создал несколько новых массивов с таким же количеством элементов, как и $array), ответ не касается неоднозначности заданного вопроса и не объясняет, как именно он определяет список / неассоциативный массив, и даже если ни один из этих пунктов не был правдой, неясно, добавляет ли это какую-либо ценность по сравнению с другими уже опубликованными ответами.
Еще один пост от источник.
Подходит для кодировки json_encode (и bson_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__)
);
}
}
Почему isset и array_key_exists? разве последнего не хватит?
@mcfedr да, будет - проверка isset() здесь полностью избыточна.
@mcfedr, @ mark-amery по соображениям производительности. isset() быстрее, чем array_key_exists(). см. ilia.ws/archives/…
@lazycommit Это будет зависеть от вашего массива, а затем от того, лучше ли он с или без, не то, что он, вероятно, будет иметь массив с большим количеством null, но тогда это также маловероятно, что у вас достаточно большой массив, чтобы была заметная разница в производительности используя обе проверки
@mcfedr согласен. плохой контроль структуры данных означает чистый дизайн и плохой запах кода. Итак, тема вся тема :)
Это правда, что весь вопрос основан на плохо спроектированном коде.
если вам нужно проверить, подходит ли он json_encode, вы можете просто проверить первый символ строки, возвращенный json_encode($your_arr) - будь то [ или { ;-)
Используя расширение рентгеновский снимок PHP
Вы можете сделать это очень быстро (примерно в 30+ раз быстрее в PHP 5.6):
if (array_is_indexed($array)) { }
Или же:
if (array_is_assoc($array)) { }
Один из способов приблизиться к этому - использовать json_encode, у которого уже есть собственный внутренний метод различения между ассоциативным массивом и индексированным массивом для вывода правильного JSON.
Вы можете сделать это, проверив, является ли первый символ, возвращаемый после кодирования, { (ассоциативный массив) или [ (индексированный массив).
// Too short :)
function is_assoc($arr) {
ksort($arr);
return json_encode($arr)[0] === '{';
}
На мой взгляд, ksort () не нужен. Это решение работает, но оно должно проверить, является ли $ arr нулевым и если json_encode не работает, поэтому попробуйте / поймать. + это не совсем оптимально, если $ arr большой.
function array_is_assoc(array $a) {
$i = 0;
foreach ($a as $k => $v) {
if ($k !== $i++) {
return true;
}
}
return false;
}
Быстро, лаконично и эффективно с точки зрения памяти. Никаких дорогостоящих сравнений, вызовов функций или копирования массивов.
ответы уже даны, но слишком много дезинформации о производительности. Я написал этот небольшой тестовый скрипт, который показывает, что метод 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
Улучшение от Марка Амери
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)) ...
Подождите, что, где я это сделал? Или вы хотели сказать «Улучшение на, Марк Эмери», как улучшение принятого ответа, приписываемого мне? Имейте в виду, что в последнем случае этот ответ был написан не мной - мои правки просто привели к тому, что алгоритм атрибуции авторства для сообщений Community Wiki поместил на нем мое имя.
Простым способом вы можете проверить, является ли массив ассоциативным или нет, с помощью следующих шагов
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));
}
Уже есть много ответов, но вот метод, на который опирается 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
@Casey 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]). Надеюсь, это ясно (сложно подробно объяснить в комментарии, по крайней мере, для меня)
Этот алгоритм безумный, простой, понятный.
Это не сработает, если у вас есть последовательный массив ассоциативных строк.
Проверка наличия у массива всех связанных ключей. При использовании 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
И т.п.
Это не сработает. Доказательство eval.in/859508 $ asocArray - это массив с двумя массивами. Верните истину. Другие доказательства для других exaqmple также не работают, как ваш пример latets eval.in/859507 возвращает true
После некоторого локального тестирования, отладки, проверки компилятора, профилирования и злоупотребления 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;
}
<?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";
}
Это моя функция -
public function is_assoc_array($array){
if (is_array($array) !== true){
return false;
}else{
$check = json_decode(json_encode($array));
if (is_object($check) === true){
return true;
}else{
return false;
}
}
}
Некоторые примеры
print_r((is_assoc_array(['one','two','three']))===true?'Yes':'No'); \No
print_r(is_assoc_array(['one'=>'one','two'=>'two','three'=>'three'])?'Yes':'No'); \Yes
print_r(is_assoc_array(['1'=>'one','2'=>'two','3'=>'three'])?'Yes':'No'); \Yes
print_r(is_assoc_array(['0'=>'one','1'=>'two','2'=>'three'])?'Yes':'No'); \No
В одном из ответов было аналогичное решение от @ devios1, но это был просто еще один способ использования встроенных функций PHP, связанных с json. Я не проверял, насколько это решение демонстрирует производительность по сравнению с другими решениями, опубликованными здесь. Но это, безусловно, помогло мне решить эту проблему. Надеюсь это поможет.
Или вы можете просто использовать это:
Arr::isAssoc($array)
который проверит, содержит ли массив любой нечисловой ключ или:
Arr:isAssoc($array, true)
чтобы проверить, содержит ли массив строго последовательный (содержит автоматически сгенерированные ключи int от 0 до п-1)
с использованием библиотеки это.
Я придумал следующий метод:
function isSequential(array $list): bool
{
$i = 0;
$count = count($list);
while (array_key_exists($i, $list)) {
$i += 1;
if ($i === $count) {
return true;
}
}
return false;
}
var_dump(isSequential(array())); // false
var_dump(isSequential(array('a', 'b', 'c'))); // true
var_dump(isSequential(array("0" => 'a', "1" => 'b', "2" => 'c'))); // true
var_dump(isSequential(array("1" => 'a', "0" => 'b', "2" => 'c'))); // true
var_dump(isSequential(array("1a" => 'a', "0b" => 'b', "2c" => 'c'))); // false
var_dump(isSequential(array("a" => 'a', "b" => 'b', "c" => 'c'))); // false
* Обратите внимание, что пустой массив не считается последовательным массивом, но я думаю, что это нормально, поскольку пустые массивы похожи на 0 - не имеет значения, плюс или минус, он пуст.
Вот преимущества этого метода по сравнению с некоторыми из перечисленных выше:
array_values не предполагает копирование - что! ?? Безусловно, как будет видно ниже.)Я использовал тест, любезно предоставленный Артур Бодера, где я изменил один из массивов на 1M элементов (array_fill(0, 1000000, uniqid()), // big numeric array).
Вот результаты для 100 итераций:
PHP 7.1.16 (cli) (built: Mar 31 2018 02:59:59) ( NTS )
Initial memory: 32.42 MB
Testing my_method (isset check) - 100 iterations
Total time: 2.57942 s
Total memory: 32.48 MB
Testing method3 (array_filter of keys) - 100 iterations
Total time: 5.10964 s
Total memory: 64.42 MB
Testing method1 (array_values check) - 100 iterations
Total time: 3.07591 s
Total memory: 64.42 MB
Testing method2 (array_keys comparison) - 100 iterations
Total time: 5.62937 s
Total memory: 96.43 MB
* Методы упорядочены по потреблению памяти.
** Я использовал echo " Total memory: " . number_format(memory_get_peak_usage()/1024/1024, 2) . " MB\n"; для отображения использования памяти
если у вас /1024, то это МиБ (Мебибайт), если у вас /1000, то это Мб (мегабайт). Mega === 1000000, в разработке программного обеспечения и в физике, на Луне и на Земле, а также внутри вашего компьютера. Мега никогда не бывает 1024 * 1024. И Кило всегда 1000, а не 1024.
вау @DanFromGermany, спасибо за невероятно полезный комментарий! Это хорошо знать. PS. кстати на самом деле Мега у меня на компьютере ровно 1024 * 1024, он нестандартный :)
/*
iszba - Is Zero Based Array
Detects if an array is zero based or not.
PARAMS:
$chkvfnc
Callback in the loop allows to check the values of each element.
Signature:
bool function chkvfnc($v);
return:
true continue looping
false stop looping; iszba returns false too.
NOTES:
○ assert: $array is an array.
○ May be memory efficient;
it doesn't get extra arrays via array_keys() or ranges() into the function.
○ Is pretty fast without a callback.
○ With callback it's ~2.4 times slower.
*/
function iszba($array, $chkvfnc=null){
$ncb = !$chkvfnc;
$i = 0;
foreach($array as $k => $v){
if ($k === $i++)
if ($ncb || $chkvfnc($v))
continue;
return false;
}
return true;
}
• Без обратного вызова это примерно на 30% быстрее, чем текущий лидирующий ответ, и, возможно, более эффективное использование памяти.
• Просто отрицайте ответ, чтобы знать, следует ли считать массив ассоциативным.
Многие решения здесь элегантны и красивы, но плохо масштабируются и требуют интенсивного использования памяти или процессора. Большинство из них создают в памяти 2 новые точки данных с помощью этого решения с обеих сторон сравнения. Чем больше массив, тем сложнее и дольше используются процесс и память, и вы теряете преимущество оценки короткого замыкания. Я провел небольшое тестирование с несколькими разными идеями. Попытка избежать array_key_exists, поскольку это дорого, а также избежать создания новых больших наборов данных для сравнения. Я считаю, что это простой способ определить, является ли массив последовательным.
public function is_sequential( $arr = [] ){
if ( !is_array( $arr ) || empty( $arr ) ) return false;
$i = 0;
$total = count( $arr );
foreach( $arr as $key => $value ) if ( $key !== $i++ ) return false;
return true;
}
Вы запускаете один подсчет в основном массиве и сохраняете одно целое число. Затем вы просматриваете массив и проверяете точное совпадение при повторении счетчика. У вас должно быть от 1 до счета. Если это не удается, происходит короткое замыкание, что дает вам повышение производительности, когда оно ложно.
Первоначально я делал это с помощью цикла for и проверки isset ($ arr [$ i]), но это не обнаружит нулевые ключи, для которых требуется array_key_exists, и, как мы знаем, это худшая функция для скорости.
Постоянное обновление переменных через foreach для проверки вместе с итератором, никогда не превышающим свой целочисленный размер, позволяет PHP использовать его встроенные функции оптимизации памяти, кеширования и сборки мусора, чтобы поддерживать очень низкое использование ресурсов.
Кроме того, я утверждаю, что использование array_keys в foreach глупо, когда вы можете просто запустить $ key => $ value и проверить ключ. Зачем создавать новую точку данных? Как только вы абстрагируете ключи массива, вы сразу же потребляете больше памяти.
Этот вопрос на самом деле бесполезен, когда дело доходит до массива php, потому что с природой php массив не должен быть полностью ассоциативным или индексируемым, он может быть комбинацией обоих, способ, которым пользователь определил и присвоил значение массиву, может быть сочетание того и другого. см. пример ниже
$y= array(5);
$y["0x"] = "n";
$y["vbg"] = "12132";
$y[1] = "k";
var_dump($y); //this will output 4 element array
echo "</br>" .$y["0x"]."</br>".$y[0];
for($x=0;$x<sizeof($y);$x++){ // this will output all index elements & gives error after that
echo "</br> index elements ".$y[$x];
}
поэтому правильный вопрос, который должен задать, - все ли элементы в массиве ассоциативны или индексны. если вы действительно знаете, что это будет только ассоциативный или индексирующий, а не комбинация этих двух, вы можете просто использовать этот метод, чтобы определить, является ли это индексным или ассоциативным массивом.
function AssocTest(&$arr){
if (is_array($arr)){
reset($arr); // reset pointer to first element of array
if (gettype(key($arr)) == "string"){ //get the type(nature) of first element key
return true;
}else{
return false;
}
}else{
return false;
}
}
вы можете использовать его как обычную функцию
echo(AssocTest($y)? "Associative array": "Not an Associative array/ Not an array at all");
и важно помнить, что вы инициализировали массив как ассоциативный, но имена, которые вы дали ассоциативному массиву, - это просто числа, которые он будет рассматривать как индексный массив, когда он читается php, если вы явно не указали имена строк. взгляните на пример ниже.
$y["0"] = "n";
$y["1"] = "12132";
$y["22"] = "k";
//both will get the same output
echo "<br/> s0 ".$y["22"];
echo "<br/> s0 ".$y[22];
for($x=0;$x<count($y);$x++){
echo "<br/> arr ".$y[$x]; // this will output up to 2nd element and give an error after
}
поэтому, если вам нужно быть уверенным, что все элементы массива должны быть точно проиндексированы или ассоциативны, нет другого пути, кроме как выполнить все элементы и проверить каждый ключ элемента по сгенерированному массиву индексов, как опубликовано здесь многими людьми.
function fullAssocTest(&$arr)
{
if (is_array($arr)){
return (array_keys($arr) !== range(0, count($arr) - 1));
}
}
меньше кода, но это действительно интенсивный процесс и действительно ненужная работа.
Иногда можно обойтись только проверкой того, равен ли ключ первого массива нулю.
$isSequential = array_keys($arr)[0] === 0
, или, более быстрая, но более подробная версия:
reset($arr); $isSequential = key($arr) === 0
Большинство ответов имеют неоптимальную временную / пространственную сложность или меняют семантику. Итак, вот еще один ответ с решениями самый быстрый и наиболее функционально правильный:
function is_sequential_array(Array &$a) {
$n = count($a);
for($i=0; $i<$n; $i++) {
if (!array_key_exists($i, $a)) {
return false;
}
}
return true;
}
Этот ответ имеет следующие преимущества перед другими ответами:
O(1) (многие ответы здесь используют пространство O(n)!)array_key_exists вместо isset (помните, isset дополнительно проверяет "не является нулем", тем самым изменяя семантику)O(n) (многие ответы здесь имеют временную сложность в лучшем случае O(n))Я удивлен, что никто не упомянул array_key_first()
Для ваших тестовых случаев:
$sequentialArray = array('apple', 'orange', 'tomato', 'carrot');
$isIndexedArray = is_int( array_key_first($sequentialArray) ); // true
тогда как
$assocArray = array('fruit1' => 'apple',
'fruit2' => 'orange',
'veg1' => 'tomato',
'veg2' => 'carrot');
$isIndexedArray = is_int( array_key_first($assocArray) ); // false
Подробнее об этой функции здесь.
Это не сработает для смешанных массивов, когда первый индекс является последовательным, но следующие элементы имеют ассоциативные индексы.
/**
* Determines if an array is associative.
* @param array $array
* @return bool
*/
function isAssoc(array $array)
{
$keys = array_keys($array);
return array_keys($keys) !== $keys;
}
Спасибо! Я использую Laravel, поэтому могу просто использовать родныеArr::isAssoc
Абсолютно ..
В вашем коде есть ошибка: помидор - это фрукт.