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

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

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

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

и это:

$assocArray = array('fruit1' => 'apple', 
                    'fruit2' => 'orange', 
                    'veg1' => 'tomato', 
                    'veg2' => 'carrot');
716
06 окт. '08 в 10:01
источник поделиться
51 ответ
  • 1
  • 2

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

  • Во-первых, как определить, имеет ли массив только числовые клавиши
  • Во-вторых, как определить, имеет ли массив последовательные числовые клавиши, начиная с 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
549
06 окт. '08 в 10:36
источник

Связанные вопросы


Похожие вопросы

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

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

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

414
23 нояб. '10 в 11:24
источник

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

<?php
$arr = array(1,2,3,4);
$isIndexed = array_values($arr) === $arr;
127
05 нояб. '08 в 16:36
источник

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

73
11 мая '11 в 22:34
источник

Как указано в ОП:

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 cool" => "b");     // Strings may contain special characters.
array('$tα€k↔øv∈rflöw⛄' => "b");                    // Strings may contain all kinds of symbols.
array("functіon" => "b");                           // You think this looks fine? Think again! (see https://stackoverflow.com/q/9246051/1402846)
array("ま말轉转ДŁ" => "b");                         // How about Japanese/Korean/Chinese/Russian/Polish?
array("fi\x0sh" => "b");                            // Strings may contain null characters.
array(file_get_contents("https://www.google.com/images/nav_logo114.png") => "b");   // Strings may even be binary!

Целочисленные ключи, которые выглядят как строки

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

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

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

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

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

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

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

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

38
23 нояб. '12 в 5:51
источник

Speed-накрест:

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

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

function isAssoc($array)
{
    $array = array_keys($array); return ($array !== array_keys($array));
}
34
06 авг. '11 в 20:39
источник

Фактически наиболее эффективным способом является:

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

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

19
17 марта '09 в 4:38
источник
function checkAssoc($array){
    return  ctype_digit( implode('', array_keys($array) ) );
}
18
15 марта '10 в 4:59
источник

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

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

17
07 окт. '10 в 18:47
источник

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

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

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

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

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

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

13
16 июля '12 в 0:54
источник

Я заметил два популярных подхода к этому вопросу: один использовал array_values() и другие, используя key(). Чтобы узнать, что быстрее, я написал небольшую программу:

$arrays = Array(
  'Array #1' => Array(1, 2, 3, 54, 23, 212, 123, 1, 1),
  'Array #2' => Array("Stack", 1.5, 20, Array(3.4)),
  'Array #3' => Array(1 => 4, 2 => 2),
  'Array #4' => Array(3.0, "2", 3000, "Stack", 5 => "4"),
  'Array #5' => Array("3" => 4, "2" => 2),
  'Array #6' => Array("0" => "One", 1.0 => "Two", 2 => "Three"),
  'Array #7' => Array(3 => "asdf", 4 => "asdf"),
  'Array #8' => Array("apple" => 1, "orange" => 2),
);

function is_indexed_array_1(Array &$arr) {
  return $arr === array_values($arr);
}

function is_indexed_array_2(Array &$arr) {
  for (reset($arr), $i = 0; key($arr) === $i++; next($arr))
    ;
  return is_null(key($arr));
}

// Method #1
$start = microtime(true);
for ($i = 0; $i < 1000; $i++) {
  foreach ($arrays as $array) {
    $dummy = is_indexed_array_1($array);
  }
}
$end = microtime(true);
echo "Time taken with method #1 = ".round(($end-$start)*1000.0,3)."ms\n";

// Method #2
$start = microtime(true);
for ($i = 0; $i < 1000; $i++) {
  foreach ($arrays as $array) {
    $dummy = is_indexed_array_2($array);
  }
}
$end = microtime(true);
echo "Time taken with method #1 = ".round(($end-$start)*1000.0,3)."ms\n";

Вывод для программы на PHP 5.2 на CentOS выглядит следующим образом:

Время, затраченное на метод # 1 = 10,745мс
Время, затраченное на метод # 2 = 18.239мс

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

7
20 февр. '13 в 13:32
источник

Эта функция может обрабатывать:

  • с отверстиями в индексе (например, 1,2,4,5,8,10)
  • с клавишами "0x": например. ключ "08" ассоциативен, а ключ "8" является последовательным.

идея проста: если один из ключей НЕ является целым числом, это ассоциативный массив, в противном случае он последователен.

function is_asso($a){
    foreach(array_keys($a) as $key) {if (!is_int($key)) return TRUE;}
    return FALSE;
}
7
08 окт. '10 в 3:42
источник

Одним из способов решения этой проблемы является использование json_encode, который уже имеет свой собственный внутренний метод различения ассоциативного массива и индексированного массива для вывода правильного JSON.

Вы можете сделать это, проверив, является ли первый символ, возвращаемый после кодирования, { (ассоциативный массив) или [ (индексированный массив).

// Too short :)
function is_assoc($arr) {
    ksort($arr);
    return json_encode($arr)[0] === '{';
}
6
20 янв. '16 в 22:18
источник

Уже есть много ответов, но вот метод, на который полагается 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

5
05 апр. '17 в 20:27
источник
function array_is_assoc(array $a) {
    $i = 0;
    foreach ($a as $k => $v) {
        if ($k !== $i++) {
            return true;
        }
    }
    return false;
}

Быстрая, лаконичная и эффективная память. Нет дорогостоящих сравнений, вызовов функций или копирования массивов.

5
08 марта '16 в 7:02
источник

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

Вы можете сделать это очень быстро (примерно в 30 + раз быстрее в PHP 5.6):

if (array_is_indexed($array)) {  }

Или:

if (array_is_assoc($array)) {  }
4
16 окт. '15 в 9:19
источник

Мое решение:

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.

3
09 июля '14 в 12:46
источник

Здесь используется метод, который я использую:

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

2
22 июля '11 в 22:29
источник

После некоторого локального бенчмаркинга, отладки, проверки компилятора, профилирования и злоупотребления 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;
}

С https://3v4l.org/rkieX:

<?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";
}
2
14 янв. '18 в 11:36
источник

Я знаю это немного бессмысленно, добавляя ответ на эту огромную очередь, но здесь читаемое O (n) решение, которое не требует дублирования любых значений:

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

Вместо того, чтобы проверять ключи, чтобы увидеть, все ли они числовые, вы перебираете ключи, которые будут там для числового массива, и убедитесь, что они существуют.

2
11 июля '13 в 2:29
источник

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

Ниже представлены 3 метода различной строгости.

<?php
/**
 * Since PHP stores all arrays as associative internally, there is no proper
 * definition of a scalar array.
 * 
 * As such, developers are likely to have varying definitions of scalar array,
 * based on their application needs.
 * 
 * In this file, I present 3 increasingly strict methods of determining if an
 * array is scalar.
 * 
 * @author David Farrell <DavidPFarrell@gmail.com>
 */

/**
 * isArrayWithOnlyIntKeys defines a scalar array as containing
 * only integer keys.
 * 
 * If you are explicitly setting integer keys on an array, you
 * may need this function to determine scalar-ness.
 * 
 * @param array $a
 * @return boolean
 */ 
function isArrayWithOnlyIntKeys(array $a)
{
    if (!is_array($a))
        return false;
    foreach ($a as $k => $v)
        if (!is_int($k))
            return false;
    return true;
}

/**
 * isArrayWithOnlyAscendingIntKeys defines a scalar array as
 * containing only integer keys in ascending (but not necessarily
 * sequential) order.
 * 
 * If you are performing pushes, pops, and unsets on your array,
 * you may need this function to determine scalar-ness.
 * 
 * @param array $a
 * @return boolean
 */ 
function isArrayWithOnlyAscendingIntKeys(array $a)
{
    if (!is_array($a))
        return false;
    $prev = null;
    foreach ($a as $k => $v)
    {
        if (!is_int($k) || (null !== $prev && $k <= $prev))
            return false;
        $prev = $k;
    }
    return true;
}

/**
 * isArrayWithOnlyZeroBasedSequentialIntKeys defines a scalar array
 * as containing only integer keys in sequential, ascending order,
 * starting from 0.
 * 
 * If you are only performing operations on your array that are
 * guaranteed to either maintain consistent key values, or that
 * re-base the keys for consistency, then you can use this function.
 * 
 * @param array $a
 * @return boolean
 */
function isArrayWithOnlyZeroBasedSequentialIntKeys(array $a)
{
    if (!is_array($a))
        return false;
    $i = 0;
    foreach ($a as $k => $v)
        if ($i++ !== $k)
            return false;
    return true;
}
2
25 дек. '12 в 3:46
источник
<?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)

2
16 мая '12 в 11:16
источник

Может ли это быть решением?

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

Предостережение очевидно, что курсор массива reset, но я бы сказал, вероятно, что функция используется до того, как массив прошел или используется.

2
05 февр. '11 в 0:55
источник

Это тоже сработает (demo):

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

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

2
21 июня '12 в 14:14
источник

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

Отказ от ответственности: следующие методы были скопированы с других ответов

<?php

function method_1(Array &$arr) {
    return $arr === array_values($arr);
}

function method_2(Array &$arr) {
    for (reset($arr), $i = 0; key($arr) !== $i++; next($arr));
    return is_null(key($arr));
}

function method_3(Array &$arr) {
    return array_keys($arr) === range(0, count($arr) - 1);
}

function method_4(Array &$arr) {
    $idx = 0;
    foreach( $arr as $key => $val ){
        if( $key !== $idx )
            return FALSE;
        $idx++;
    }
    return TRUE;
}




function benchmark(Array $methods, Array &$target){    
    foreach($methods as $method){
        $start = microtime(true);
        for ($i = 0; $i < 1000; $i++)
            $dummy = call_user_func($method, $target);

        $end = microtime(true);
        echo "Time taken with $method = ".round(($end-$start)*1000.0,3)."ms\n";
    }
}



$targets = [
    'Huge array' => range(0, 30000),
    'Small array' => range(0, 1000),
];
$methods = [
    'method_1',
    'method_2',
    'method_3',
    'method_4',
];
foreach($targets as $targetName => $target){
    echo "==== Benchmark using $targetName ====\n";
    benchmark($methods, $target);
    echo "\n";
}

результаты:

==== Benchmark using Huge array ====
Time taken with method_1 = 5504.632ms
Time taken with method_2 = 4509.445ms
Time taken with method_3 = 8614.883ms
Time taken with method_4 = 2720.934ms

==== Benchmark using Small array ====
Time taken with method_1 = 77.159ms
Time taken with method_2 = 130.03ms
Time taken with method_3 = 160.866ms
Time taken with method_4 = 69.946ms
1
04 июля '16 в 14:42
источник

Еще один быстрый из источник. Установите кодировку json_encodebson_encode). Таким образом, соответствие javascript Array.

function isSequential($value){
    if(is_array($value) || ($value instanceof \Countable && $value instanceof \ArrayAccess)){
        for ($i = count($value) - 1; $i >= 0; $i--) {
            if (!isset($value[$i]) && !array_key_exists($i, $value)) {
                return false;
            }
        }
        return true;
    } else {
        throw new \InvalidArgumentException(
            sprintf('Data type "%s" is not supported by method %s', gettype($value), __METHOD__)
        );
    }
}
1
08 авг. '14 в 17:45
источник

Я придумал следующий метод:

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 https://gist.github.com/Thinkscape/1965669, что 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"; echo " Total memory: ". number_format(memory_get_peak_usage()/1024/1024, 2). " MB\n"; отображать использование памяти

0
11 сент. '18 в 14:53
источник
function is_associative($arr) {
  return (array_merge($arr) !== $arr || count(array_filter($arr, 'is_string', ARRAY_FILTER_USE_KEY)) > 0);
}
0
06 окт. '08 в 10:17
источник

Многие решения здесь элегантны и симпатичны, но плохо масштабируются и требуют большого объема памяти или ресурсов процессора. Большинство из них создают 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 и проверить ключ. Зачем создавать новую точку данных? Когда вы абстрагируете ключи массива, вы сразу же потребляете больше памяти.

0
10 мая '19 в 6:56
источник

Еще один способ сделать это.

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;

}
0
06 окт. '08 в 12:56
источник
  • 1
  • 2

Посмотрите другие вопросы по меткам или Задайте вопрос