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

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

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

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

и это:

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

Ответы [ 51 ]

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

Один дешевый и грязный способ - проверить вот так:

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");
0 голосов
/ 10 октября 2016

Простым способом вы можете проверить, является ли массив ассоциативным или нет, с помощью следующих шагов

  1. преобразовать все ключи массива в один массив, используя array_keys()
  2. отфильтровать нечисловой ключ из массива, используя array_filter() и is_numeric()
  3. сравнить количество элементов в фильтрованном или фактическом массиве. Если количество элементов не равно в обоих массивах, то это ассоциативный массив.

Функция для вышеприведенного шага указана ниже.

 function isAssociative(array $array)
    {
        return count(array_filter(array_keys($array), function($v){return is_numeric($v);})) !== count($array));
    }
0 голосов
/ 01 августа 2011

Лучшая функция для обнаружения ассоциативного массива (хеш-массив)

<?php
function is_assoc($arr) { return (array_values($arr) !== $arr); }
?>
0 голосов
/ 28 июня 2017

Проверка наличия в массиве всех ассоциированных ключей. С использованием 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

Etc.

0 голосов
/ 13 августа 2011

Простое и эффективное решение, которое проверяет только первый ключ.

function isAssoc($arr = NULL)
{
    if ($arr && is_array($arr))
    {
        foreach ($arr as $key => $val)
        {
            if (is_numeric($key)) { return true; }

            break;
        }
    }

    return false;
}
0 голосов
/ 04 мая 2018

Это моя функция -

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 было похожее решение, но это был просто еще один способ использования встроенных json-функций PHP. Я не проверял, как это решение выглядит с точки зрения производительности по сравнению с другими решениями, которые были размещены здесь. Но это, безусловно, помогло мне решить эту проблему. Надеюсь, это поможет.

0 голосов
/ 03 сентября 2011

Я столкнулся с этой проблемой еще раз несколько дней назад, и я подумал воспользоваться специальным свойством array_merge:

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

function Is_Indexed_Arr($arr){
    $arr_copy = $arr;
    if((2*count($arr)) == count(array_merge($arr, $arr_copy))){
        return 1;
    }
    return 0;
}
0 голосов
/ 11 сентября 2018

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

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 - не важно, плюс это или минус, он пуст.

Вот преимущества этого метода по сравнению с некоторыми из перечисленных выше:

  • Это не связано с копированием массивов (кто-то упомянул в этой сущности 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"; для отображения использования памяти

0 голосов
/ 17 декабря 2011

Мое решение состоит в том, чтобы получить ключи массива, как показано ниже, и проверить, не является ли ключ целым:

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"
)

Так что не стоит сравнивать его с диапазоном чисел, как указано в ответе с самым высоким рейтингом. Всегда будет сказано, что это хеш-массив, если вы попытаетесь сравнить ключи с диапазоном.

0 голосов
/ 10 мая 2019

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

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