PHP рассматривает все массивы как ассоциативные, поэтому встроенных функций для этого нет. Может ли кто-нибудь порекомендовать довольно эффективный способ проверить, содержит ли массив только числовые ключи?
По сути, я хочу различать это:
$sequentialArray = [
'apple', 'orange', 'tomato', 'carrot'
];
и это:
$assocArray = [
'fruit1' => 'apple',
'fruit2' => 'orange',
'veg1' => 'tomato',
'veg2' => 'carrot'
];
Ответ 1
Вы задали два вопроса, которые не совсем эквивалентны:
Во-первых, как определить, имеет ли массив только числовые ключи?
Во-вторых, как определить, есть ли в массиве последовательные числовые ключи, начиная с 0?
Подумайте, какое из этих поведений вам действительно нужно (возможно, для ваших целей подойдет любой из них).
Для второго вопроса (проверка того, является ли массив с нулевым индексом и последовательным) вы можете использовать следующую функцию:
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
Ответ 2
Многие ответившие на этот вопрос не понимают, как массивы работают в PHP. Из документации по массиву:
Ключ может быть целым числом или строкой. Если ключ является стандартным представлением целого числа, он будет интерпретироваться как таковой (т. е. «8» будет интерпретироваться как 8, а «08» будет интерпретироваться как «08»). Значения в ключе усекаются до целого числа. Индексированные и ассоциативные типы массивов — это один и тот же тип в PHP, который может содержать как целочисленные, так и строковые индексы.
Другими словами, не существует такой вещи, как ключ массива «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 в качестве ключа массива, он будет незаметно преобразован в "").
Ответ 3
Как указано в ОП:
PHP рассматривает все массивы как ассоциативные.
Не совсем разумно писать функцию, проверяющую ассоциативность массива. Итак, прежде всего: что такое ключ в массиве PHP?
Ключ может быть либо целым числом, либо строкой.
Это означает, что есть 3 возможных случая:
Случай 1. Все ключи — числовые/целые.
Случай 2. Все ключи — строки.
Случай 3. Некоторые ключи являются строками, некоторые — числовыми/целыми числами.
Мы можем проверить каждый случай с помощью следующих функций.
Случай 1: все ключи — числовые/целые.
Примечание. Эта функция также возвращает значение true для пустых массивов.
//! Проверьте, является ли входной массив массивом, все ключи которого являются целыми числами.
/*!
\param[in] $InputArray (array) Входной массив.
\return (bool) \b true, если входной массив является массивом, ключи которого — все целые числа.
*/
function IsArrayAllKeyInt($InputArray) {
if(!is_array($InputArray)) {
return false;
}
if(count($InputArray) <= 0) {
return true;
}
return array_unique(array_map("is_int", array_keys($InputArray))) === array(true);
}
Случай 2: все ключи — строки.
Примечание. Эта функция также возвращает значение true для пустых массивов.
//! Проверьте, является ли входной массив массивом, все ключи которого являются строками.
/*!
\param[in] $InputArray (array) Входной массив.
\return (bool) \b true, если входной массив является массивом, все ключи которого являются строками.
*/
function IsArrayAllKeyString($InputArray) {
if(!is_array($InputArray)) {
return false;
}
if(count($InputArray) <= 0) {
return true;
}
return array_unique(array_map("is_string", array_keys($InputArray))) === array(true);
}
Случай 3: некоторые ключи являются строками, другие — числовыми/целыми числами.
Примечание. Эта функция также возвращает значение true для пустых массивов.
//! Проверьте, является ли входной массив массивом, в котором хотя бы один ключ является целым числом и хотя бы один ключ является строкой.
/*!
\param[in] $InputArray (array) Входной массив.
\return (bool) \b true, если входной массив является массивом, в котором хотя бы один ключ является целым числом и хотя бы один ключ является строкой.
*/
function IsArraySomeKeyIntAndSomeKeyString($InputArray){
if(!is_array($InputArray)) {
return false;
}
if(count($InputArray) <= 0) {
return true;
}
return count(array_unique(array_map("is_string", array_keys($InputArray)))) >= 2;
}
Из этого следует, что:
Если значение не является массивом, все 3 функции возвращают false .
Если значение является пустым массивом, все 3 функции возвращают true
(что согласно определению, например, «пустой набор является подмножеством любого набора A, потому что все его элементы принадлежат A»).Если значение — непустой массив, ровно 1 функция вернет true.
Теперь, чтобы массив был «настоящим» массивом, к которому мы все привыкли, должно быть так:
Все его ключи являются числовыми/целыми числами.
Его ключи являются последовательными (т. е. увеличиваются на шаг 1).
Его ключи начинаются с нуля.
Это мы можем проверить с помощью следующей функции.
Случай 3а: ключи являются числовыми/целыми числами, последовательными и отсчитываются от нуля .
Примечание. Эта функция также возвращает значение true для пустых массивов.
//! Проверьте, является ли входной массив массивом, ключи которого являются числовыми, последовательными и нулевыми.
/*!
\param[in] $InputArray (array) Входной массив.
\return (bool) \b true, если входной массив является массивом, ключи которого являются числовыми, последовательными и нулевыми.
*/
function IsArrayKeyNumericSequentialZeroBased($InputArray) {
if(!is_array($InputArray)) {
return false;
}
if(count($InputArray) <= 0) {
return true;
}
return array_keys($InputArray) === range(0, count($InputArray) - 1);
}
Предостережения/подводные камни (или даже более специфические факты о ключах массивов в PHP).
Целочисленные ключи.
Ключи для этих массивов — целые числа:
array(0 => "b");
array(13 => "b");
array(-13 => "b"); // Отрицательные целые числа также являются целыми числами.
array(0x1A => "b"); // Шестнадцатеричная нотация.
Строковые ключи
Ключи для этих массивов — строки:
array("fish and chips" => "b");
array("" => "b"); // Пустая строка также является строкой.
array("stackoverflow_email@example.com" => "b"); // Строки могут содержать не алфавитно-цифровые символы.
array("stack\t\"over\"\r\nflow's cool" => "b"); // Строки могут содержать специальные символы.
array('$tα€k↔øv∈rflöw⛄' => "b"); // Строки могут содержать всевозможные символы.
array("ま말轉转ДŁ" => "b"); // Как насчет японского/корейского/китайского/русского/польского?
array("fi\x0sh" => "b"); // Строки могут содержать нулевые символы.
array(file_get_contents("https://www.google.com/images/nav_logo114.png") => "b"); // Строки могут быть даже двоичными!
Целочисленные ключи, похожие на строки
Если вы думаете, что ключ array("13" => "b")— это строка, вы ошибаетесь. Из документации:
Строки, содержащие допустимые целые числа, будут преобразованы в целочисленный тип. Например, ключ «8» будет фактически сохранен под номером 8. С другой стороны, «08» не будет приведено, так как это недопустимое десятичное целое число.
Например, ключом к этим массивам являются целые числа:
array("13" => "b");
array("-13" => "b"); // Негатив, ок.
А ключом к этим массивам являются строки:
array("13. => "b");
array("+13" => "b"); // позитивное, ошибка.
array("-013" => "b");
array("0x1A" => "b"); // Не преобразуются в целые числа, хотя это правильное шестнадцатеричное число.
array("013" => "b"); // Не преобразуется в целые числа, хотя это действительное восьмеричное число.
array("18446744073709551616" => "b"); // Не преобразуется в целые числа, так как не может поместиться в 64-битное целое число.
Более того, согласно этому документу:
Размер целого числа зависит от платформы, хотя максимальное значение около двух миллиардов является обычным значением (это 32 бита со знаком). 64-битные платформы обычно имеют максимальное значение около 9E18, за исключением Windows, которая всегда 32-битная. PHP не поддерживает целые числа без знака.
Таким образом, ключ для этого массива может быть целым числом, а может и не быть — это зависит от вашей платформы.
array("60000000000" => "b"); // Ключ массива может быть целым числом или строкой, он может помещаться в 64-битное (но не 32-битное) целое число.
Хуже того, PHP имеет тенденцию к ошибкам, если целое число находится рядом с границей 2 31 = 2 147 483 648. Например, в моей локальной среде (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-разрядным целым числом со знаком.
Web