Avatar ·

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

Привет, коллеги! Пишу на PHP уже несколько месяцев, и часто сталкиваюсь с проблемой: мне нужно понять, является ли массив обычным списком (с числовыми ключами 0, 1, 2...) или ассоциативным (с ключами-строками, например 'name', 'age'). Это нужно для того, чтобы правильно обрабатывать данные, например, при конвертации в JSON или при передаче в функцию, которая ожидает определённый формат. Вот пример. У меня есть функция, которая принимает массив и должна работать с ним по-разному: ```php function processData($data) { // Если массив последовательный — перебираем элементы как список // Если ассоциативный — работаем с ключами // Как это проверить внутри функции? } ``` Я пробовал проверять через `array_keys()`, но это кажется костылём. Ещё читал про `is_numeric()`, но не уверен, что это надёжно. Кто-то говорит про `array_values()`, кто-то — про `range()`. Подскажите, пожалуйста, самый правильный и производительный способ проверить, является ли массив ассоциативным или последовательным? Нужно ли учитывать, что ключи могут быть не по порядку (например, 0, 5, 10) или начинаться не с нуля? Буду благодарен за примеры кода и объяснение! Спасибо! <<>>

Основная идея: что значит «последовательный» массив?

В PHP нет встроенной функции для такой проверки, поэтому разработчики придумывают свои решения. Главное — чётко определить, что вы считаете «последовательным» массивом. Обычно под этим понимают массив, у которого все ключи — целые числа, и они идут по порядку от 0 до N-1 (где N — количество элементов). Ассоциативный массив — это любой массив, у которого хотя бы один ключ не является целым числом или нарушена последовательность.

Самый надёжный и производительный способ

Лучший подход — проверить два условия:

  • Все ключи — целые числа (не строки, не float).
  • Ключи образуют строгую последовательность 0, 1, 2, ..., count($array)-1.

Вот реализация:

function isSequentialArray(array $array): bool {
    if (empty($array)) {
        // Пустой массив можно считать и тем, и другим — зависит от задачи
        // Обычно возвращают true (последовательный) или false
        return true;
    }
    
    // Проверяем, что все ключи — целые числа
    $keys = array_keys($array);
    foreach ($keys as $key) {
        if (!is_int($key)) {
            return false; // Нашли строковый ключ — ассоциативный
        }
    }
    
    // Проверяем, что ключи идут по порядку от 0 до count-1
    return $keys === range(0, count($array) - 1);
}

// Примеры:
$sequential = [10, 20, 30]; // 0,1,2 — true
$assoc = ['a' => 1, 'b' => 2]; // строковые ключи — false
$mixed = [0 => 'x', 2 => 'y']; // числовые, но не по порядку — false
$startFromOne = [1 => 'a', 2 => 'b']; // начинается с 1 — false
$stringNumeric = ['0' => 'test']; // ключ — строка '0', а не int — false

var_dump(isSequentialArray($sequential)); // true
var_dump(isSequentialArray($assoc)); // false
var_dump(isSequentialArray($mixed)); // false
var_dump(isSequentialArray($startFromOne)); // false
var_dump(isSequentialArray($stringNumeric)); // false

Почему это лучший вариант?

  • Проверка типов: is_int($key) отсекает строковые ключи, даже если они выглядят как числа (например, '0').
  • Проверка последовательности: сравнение с range() гарантирует, что ключи идут строго по порядку без пропусков.
  • Производительность: функция работает за O(n), где n — количество ключей. Для большинства массивов это оптимально.
  • Гибкость: легко изменить логику для других критериев (например, считать последовательным массив с ключами 0,5,10? Тогда уберите проверку range).

Альтернативный однострочник (менее надёжный)

Многие используют array_values() и сравнивают с исходным массивом:

function isSequentialArray(array $array): bool {
    return $array === array_values($array);
}

Этот метод работает, но не проверяет типы ключей. Если у вас есть ключ-строка '0', array_values() преобразует его в целое число, и массив может ошибочно считаться последовательным. Используйте его только если уверены, что все ключи уже целые числа.

Заключение

Для 99% случаев используйте первый вариант с проверкой типов и range. Если вам нужно просто проверить, является ли массив «списком» (без строгих требований к порядку ключей), можно упростить проверку, убрав сравнение с range. Главное — понимать, какие данные вы ожидаете на входе.

<<>>

Альтернативный подход: использование array_diff_key()

Если вам не нужно различать пустые массивы или вы работаете с большими объёмами данных, можно использовать более лаконичный метод с функциями array_keys() и array_filter():

function isAssociative(array $array): bool {
    // Если массив пуст — считаем его не ассоциативным (или наоборот)
    if (empty($array)) return false;
    
    // Проверяем, есть ли хотя бы один нечисловой ключ
    return count(array_filter(array_keys($array), 'is_string')) > 0;
}

// Пример:
$assoc = ['name' => 'John', 'age' => 30];
$seq = [1, 2, 3];
var_dump(isAssociative($assoc)); // true
var_dump(isAssociative($seq)); // false

Этот метод проверяет только наличие строковых ключей. Если массив имеет числовые ключи, но не по порядку (например, [0 => 'a', 5 => 'b']), он будет считаться последовательным. Это может быть полезно, если вас интересует только «ассоциативность» (наличие строковых ключей), а не строгая последовательность.

Когда использовать?

  • Если вы работаете с JSON-данными, где ключи всегда строки.
  • Если вам нужно быстро отличить «список» от «словаря» без строгих требований к порядку.
  • Для обратной совместимости со старым кодом.

Недостаток: функция не различает массивы с пропущенными числовыми ключами (например, [0 => 'a', 2 => 'b'] будет считаться последовательным). Если это критично — используйте первый ответ.