Как проверить, является ли массив PHP ассоциативным или последовательным? - PullRequest
724 голосов
/ 06 октября 2008

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

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

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

и это:

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

Ответы [ 51 ]

555 голосов
/ 06 октября 2008

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

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

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

Первый вопрос (просто проверка того, что все ключи числовые) - это , на который хорошо ответил капитан kurO .

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

function isAssoc(array $arr)
{
    if (array() === $arr) return false;
    return array_keys($arr) !== range(0, count($arr) - 1);
}

var_dump(isAssoc(array('a', 'b', 'c'))); // false
var_dump(isAssoc(array("0" => 'a', "1" => 'b', "2" => 'c'))); // false
var_dump(isAssoc(array("1" => 'a', "0" => 'b', "2" => 'c'))); // true
var_dump(isAssoc(array("a" => 'a', "b" => 'b', "c" => 'c'))); // true
415 голосов
/ 23 ноября 2010

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

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

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

127 голосов
/ 05 ноября 2008

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

<?php
$arr = array(1,2,3,4);
$isIndexed = array_values($arr) === $arr;
73 голосов
/ 11 мая 2011

Многие комментаторы в этом вопросе не понимают, как работают массивы в 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 в качестве ключа массива, он будет молча преобразован в "").

38 голосов
/ 23 ноября 2012

Как указано OP :

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

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

Клавиша может быть либо целым числом , либо строкой .

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

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

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

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

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

//! Check whether the input is an array whose keys are all integers.
/*!
    \param[in] $InputArray          (array) Input array.
    \return                         (bool) \b true iff the input is an array whose keys are all integers.
*/
function IsArrayAllKeyInt($InputArray)
{
    if(!is_array($InputArray))
    {
        return false;
    }

    if(count($InputArray) <= 0)
    {
        return true;
    }

    return array_unique(array_map("is_int", array_keys($InputArray))) === array(true);
}

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

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

//! Check whether the input is an array whose keys are all strings.
/*!
    \param[in] $InputArray          (array) Input array.
    \return                         (bool) \b true iff the input is an array whose keys are all strings.
*/
function IsArrayAllKeyString($InputArray)
{
    if(!is_array($InputArray))
    {
        return false;
    }

    if(count($InputArray) <= 0)
    {
        return true;
    }

    return array_unique(array_map("is_string", array_keys($InputArray))) === array(true);
}

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

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

//! Check whether the input is an array with at least one key being an integer and at least one key being a string.
/*!
    \param[in] $InputArray          (array) Input array.
    \return                         (bool) \b true iff the input is an array with at least one key being an integer and at least one key being a string.
*/
function IsArraySomeKeyIntAndSomeKeyString($InputArray)
{
    if(!is_array($InputArray))
    {
        return false;
    }

    if(count($InputArray) <= 0)
    {
        return true;
    }

    return count(array_unique(array_map("is_string", array_keys($InputArray)))) >= 2;
}

Отсюда следует:


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

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

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

Дело 3а. клавиши числовые / целые числа , последовательные и на основе нуля .

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

//! Check whether the input is an array whose keys are numeric, sequential, and zero-based.
/*!
    \param[in] $InputArray          (array) Input array.
    \return                         (bool) \b true iff the input is an array whose keys are numeric, sequential, and zero-based.
*/
function IsArrayKeyNumericSequentialZeroBased($InputArray)
{
    if(!is_array($InputArray))
    {
        return false;
    }

    if(count($InputArray) <= 0)
    {
        return true;
    }

    return array_keys($InputArray) === range(0, count($InputArray) - 1);
}

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

Целочисленные клавиши

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

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

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

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

array("fish and chips" => "b");
array("" => "b");                                   // An empty string is also a string.
array("stackoverflow_email@example.com" => "b");    // Strings may contain non-alphanumeric characters.
array("stack\t\"over\"\r\nflow's cool" => "b");     // Strings may contain special characters.
array('$tα€k↔øv∈rflöw⛄' => "b");                    // Strings may contain all kinds of symbols.
array("functіon" => "b");                           // You think this looks fine? Think again! (see https://stackoverflow.com/q/9246051/1402846)
array("ま말轉转ДŁ" => "b");                         // How about Japanese/Korean/Chinese/Russian/Polish?
array("fi\x0sh" => "b");                            // Strings may contain null characters.
array(file_get_contents("https://www.google.com/images/nav_logo114.png") => "b");   // Strings may even be binary!

Целочисленные клавиши в виде строк

Если вы думаете, что ключ в array("13" => "b") является строкой , , вы ошибаетесь . Из документа здесь :

Строки, содержащие действительные целые числа, будут преобразованы в целочисленный тип. Например. ключ «8» будет фактически сохранен в 8. С другой стороны, «08» не будет приведен, поскольку он не является действительным десятичным целым числом.

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

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

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

array("13." => "b");
array("+13" => "b");                        // Positive, not ok.
array("-013" => "b");
array("0x1A" => "b");                       // Not converted to integers even though it's a valid hexadecimal number.
array("013" => "b");                        // Not converted to integers even though it's a valid octal number.
array("18446744073709551616" => "b");       // Not converted to integers as it can't fit into a 64-bit integer.

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

Размер целого числа зависит от платформы, хотя максимальное значение около двух миллиардов является обычным значением (это 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-битным целым числом со знаком.

34 голосов
/ 06 августа 2011

Speed-накрест:

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

Память-накрест:

function isAssoc($array)
{
    $array = array_keys($array); return ($array !== array_keys($array));
}
19 голосов
/ 17 марта 2009

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

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

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

18 голосов
/ 15 марта 2010
function checkAssoc($array){
    return  ctype_digit( implode('', array_keys($array) ) );
}
17 голосов
/ 07 октября 2010

Я использовал и 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 блоков.

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

13 голосов
/ 16 июля 2012

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

function is_indexed_array(&$arr) {
  for (reset($arr); is_int(key($arr)); next($arr));
  return is_null(key($arr));
}

function is_sequential_array(&$arr, $base = 0) {
  for (reset($arr), $base = (int) $base; key($arr) === $base++; next($arr));
  return is_null(key($arr));
}

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...