Jak sprawdzić, czy tablica PHP jest asocjacyjna czy sekwencyjna?

781

PHP traktuje wszystkie tablice jako asocjacyjne, więc nie ma żadnych wbudowanych funkcji. Czy ktoś może polecić dość skuteczny sposób sprawdzenia, czy tablica zawiera tylko klawisze numeryczne?

Zasadniczo chcę być w stanie odróżnić to:

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

i to:

$assocArray = array('fruit1' => 'apple', 
                    'fruit2' => 'orange', 
                    'veg1' => 'tomato', 
                    'veg2' => 'carrot');
Wilco
źródło
382
W kodzie jest błąd: Pomidor to owoc.
Olle Härstedt
9
Ta metoda ma pewne zastrzeżenia, ale często to robię if (isset($array[0])), co jest proste i szybkie. Oczywiście, powinieneś najpierw upewnić się, że tablica nie jest pusta, i powinieneś mieć trochę wiedzy na temat możliwej zawartości tablicy, aby metoda nie mogła zawieść (np. Mieszana liczba / asocjacja lub niesekwencyjna).
Gras Double
@ OlleHärstedt Nie według amerykańskiego sądu najwyższego. ;-)
MC Emperor

Odpowiedzi:

622

Zadałeś dwa pytania, które nie są do końca równoważne:

  • Po pierwsze, jak ustalić, czy tablica ma tylko klucze numeryczne
  • Po drugie, jak ustalić, czy tablica ma sekwencyjne klucze numeryczne, zaczynając od 0

Zastanów się, które z tych zachowań naprawdę potrzebujesz. (Może być tak, że albo zrobi to dla twoich celów.)

Na pierwsze pytanie (po prostu sprawdzenie, czy wszystkie klawisze są numeryczne) odpowiada dobrze kapitan kurO .

W przypadku drugiego pytania (sprawdzenie, czy tablica jest indeksowana zerowo i sekwencyjnie), możesz użyć następującej funkcji:

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
Mark Amery
źródło
32
Bardzo eleganckie rozwiązanie. Zauważ, że zwraca PRAWDA w (niejednoznacznym) przypadku pustej tablicy.
Jonathan Lidbeck
30
Myślę, że bardziej przydatne jest myślenie o tablicach sekwencyjnych jako szczególnym przypadku tablic asocjacyjnych. Każda tablica jest więc asocjacyjna, ale tylko niektóre są sekwencyjne. Dlatego funkcja isSequential()miałaby większy sens niż isAssoc(). W takiej funkcji pustą tablicę należy traktować jako sekwencyjną. Formuła może być array() === $arr || !isAssoc($arr).
donquixote
18
Myślę, że pozwoliłoby to uniknąć dużo potencjalnego czasu procesora i pamięci, gdyby sprawdzono, czy isset ($ arr [0]) jest fałszem przed wyodrębnieniem wszystkich kluczy, ponieważ jest to wyraźnie asocjacyjne, jeśli tablica nie jest pusta, ale nie ma elementu w 0 pozycja. Ponieważ „większość” prawdziwych tablic asocjacyjnych ma ciągi jako klucze, powinna to być przyjemna optymalizacja dla ogólnego przypadku takiej funkcji.
OderWat
10
@OderWat - array_key_existsZamiast tego należy użyć optymalizacji, issetponieważ jeśli element zerowy ma wartość zerową, zestaw zwróci wartość false niepoprawnie. Wartość null powinna zwykle być prawidłową wartością w takiej tablicy.
OCDev
@MAChitgarha, twoja edycja zmieniła zachowanie funkcji bez wyjaśnienia dlaczego i sprawiła, że ​​stała się ona sprzeczna z opisem w prozie powyżej tego, co powinna zrobić. Cofnąłem to.
Mark Amery
431

Aby tylko sprawdzić, czy tablica ma klucze niecałkowite (a nie, czy tablica jest indeksowana sekwencyjnie czy zerowo):

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

Jeśli istnieje co najmniej jeden klucz ciągu, $arraybędzie traktowany jako tablica asocjacyjna.

Kapitan kurO
źródło
22
Ta metoda jest znacznie lepsza niż się wydaje. Jeśli count (filter_array) == count (original_array), to jest to tablica assoc. Jeśli liczba (filter_array) == 0, to jest to tablica indeksowana. Jeśli count (filter_array) <count (original_array), to tablica ma zarówno klucze numeryczne, jak i łańcuchowe.
Jamol
5
@MikePretzlaw oczywiście iteruje; nie ma (oczywiście) żadnego możliwego sposobu ustalenia, czy wszystkie klucze tablicy są liczbami całkowitymi bez spojrzenia na wszystkie klucze w tablicy. Zakładam, że nie iterujące alternatywy, które powinniśmy zobaczyć poniżej, to takie $isIndexed = array_values($arr) === $arr;? Na co pytam: jak myślisz, jak array_values()działa? Jak myślisz, w jaki sposób ===działa w odniesieniu do tablic? Odpowiedź jest oczywiście taka, że iterują również w tablicy.
Mark Amery
4
@ARW „Wydaje się, że PHP rzuca wszystko na int w definicji tablicy, jeśli to możliwe.” - tak, dokładnie tak się dzieje. Największy WTF polega na tym, że robi to nawet z pływakami; jeśli spróbujesz var_dump([1.2 => 'foo', 1.5 => 'bar']);, odkryjesz, że masz tablicę [1 => 'bar']. W żaden sposób nie można znaleźć oryginalnego typu klucza. Tak, wszystko to jest okropne; Tablice PHP są zdecydowanie najgorszą częścią języka, a większość szkód jest nieodwracalna i zawdzięcza to pomysłowi użycia pojedynczej konstrukcji dla tradycyjnych tablic, a tradycyjne mapy skrótów były okropne od samego początku.
Mark Amery
30
@MarkAmery Powyższe, choć proste, gwarantuje 100% chodzenie po tablicy. Byłoby to bardziej wydajne, szczególnie jeśli masz do czynienia z dużymi tablicami, jeśli sprawdzałeś ciąg znaków lub int i wybuchałeś przy pierwszym znalezionym. Na przykład: function isAssociative($arr) { foreach ($arr as $key => $value) { if (is_string($key)) return true; } return false; }
Myśli
1
@ Myśl My kod działa bardzo szybko, ale nie może wykryć tablicy sekwencyjnej . Przykład array(1 => 'a', 0 => 'b', 2 => 'c')stanie się false(tablica sekwencyjna), a powinien być true(tablica asocjacyjna). toolsqa.com/data-structures/array-in-programming Nie jestem pewien, czy klucz musi być w porządku rosnącym? (0, 1, ...)
vee
132

Z pewnością jest to lepsza alternatywa.

<?php
$arr = array(1,2,3,4);
$isIndexed = array_values($arr) === $arr;
Dave Marshall
źródło
52
Spowoduje to zduplikowanie wartości w tablicy, co jest potencjalnie bardzo drogie. O wiele lepiej jest sprawdzić klucze tablicy.
meagar
8
Właśnie użyłem ==; Nie wydaje mi się, że istnieje potrzeba === tutaj. Ale żeby odpowiedzieć na „unset i to nie działa”: kiedy rozłączysz pierwszy element, nie jest to już tablica indeksowana liczbami całkowitymi, zaczynająca się od 0. Więc IMO to działa.
grantwparks
4
Zgadzam się z @grantwparks: rzadka tablica nie jest indeksowana. Co ciekawe, ponieważ nie ma sposobu, aby usunąć element ze środka indeksowanej tablicy. PHP w zasadzie deklaruje wszystkie tablice jako asocjacyjne, a numeryczna jest po prostu wersją „uzupełnij mnie”.
RickMeasham
7
Jedyny problem, jaki mam z tym, to to, że ===tracę czas na sprawdzanie, czy wartości są równe, mimo że interesują nas tylko klucze. Z tego powodu wolę $k = array_keys( $arr ); return $k === array_keys( $k );wersję.
Jesse
5
Dodana uwaga, że ​​to się nie udaje w przypadku tablic określonych za pomocą klawiszy numerycznych, które są nieczynne. np. $ myArr = tablica (0 => „a”, 3 => „b”, 4 => 1, 2 => 2, 1 => „3”); Jednym potencjalnym obejściem jest uruchomienie ksorta ($ arr) przed wykonaniem testu
Scott
77

Wielu komentujących to pytanie nie rozumie, jak tablice działają w PHP. Z dokumentacji tablicy :

Klucz może być liczbą całkowitą lub łańcuchem. Jeśli klucz jest standardową reprezentacją liczby całkowitej, zostanie zinterpretowany jako taki (tzn. „8” zostanie zinterpretowane jako 8, a „08” zostanie zinterpretowane jako „08”). Zmienne w kluczu są obcinane do liczby całkowitej. Indeksowane i asocjacyjne typy tablic są tego samego typu w PHP, które mogą zawierać zarówno indeksy całkowite, jak i łańcuchowe.

Innymi słowy, nie ma czegoś takiego jak klucz tablicy „8”, ponieważ zawsze będzie (cicho) konwertowany na liczbę całkowitą 8. Dlatego próba rozróżnienia liczb całkowitych i ciągów liczbowych jest niepotrzebna.

Jeśli chcesz najskuteczniejszego sposobu sprawdzenia tablicy pod kątem kluczy niecałkowitych bez robienia kopii części tablicy (jak robi to array_keys ()) lub wszystkich (jak foreach robi):

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

Działa to, ponieważ key () zwraca NULL, gdy bieżąca pozycja tablicy jest niepoprawna, a NULL nigdy nie może być prawidłowym kluczem (jeśli spróbujesz użyć NULL jako klucza tablicy, zostanie on po cichu przekonwertowany na „”).

wiewiórka
źródło
To nie działa dla niesekwencyjnych kluczy całkowitych. Wypróbuj z [2 => 'a', 4 => 'b'].
DavidJ
2
@DavidJ, Co rozumiesz przez „nie działa”? Pomyślnie określa, że ​​wszystkie klucze są liczbami całkowitymi. Czy twierdzisz, że tablica taka jak ta, którą opublikowałeś, nie powinna być uważana za „tablicę numeryczną”?
coredumperror
7
Tablica niepowiązana musi mieć klucze od 0do count($array)-1, w tej ścisłej kolejności. Pomocna is_array()może być wstępna kontrola . Dodaj rosnącą zmienną, aby sprawdzić sekwencję klawiszy: for ($k = 0, reset($array) ; $k === key($array) ; next($array)) ++$k;To rozstrzygnie umowę.
ofavre
2
Używanie foreachzamiast jawnej iteracji jest około dwa razy szybsze.
ofavre
1
Jeśli chcesz zrobić z tego funkcję: function isAssocStr($array) { for (reset($array); is_int(key($array)); next($array)) { if (is_null(key($array))) return false; } return true; }
GreeKatrina
39

Jak stwierdził PO :

PHP traktuje wszystkie tablice jako asocjacyjne

napisanie funkcji, która sprawdza, czy tablica jest asocjacyjna, nie jest rozsądne (IMHO) . Po pierwsze: co jest kluczem w tablicy PHP ?:

Klucz może być albo całkowitą lub ciąg .

Oznacza to, że są 3 możliwe przypadki:

  • Przypadek 1. wszystkie klucze są liczbami / liczbami całkowitymi .
  • Przypadek 2. wszystkie klucze są ciągami znaków .
  • Przypadek 3. niektóre klucze są ciągami , niektóre klucze są liczbami / liczbami całkowitymi .

Możemy sprawdzić każdy przypadek za pomocą następujących funkcji.

Przypadek 1: wszystkie klucze są liczbami / liczbami całkowitymi .

Uwaga : Ta funkcja zwraca wartość true również dla pustych tablic.

//! 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);
}

Przypadek 2: wszystkie klucze są ciągami znaków .

Uwaga : Ta funkcja zwraca wartość true również dla pustych tablic.

//! 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);
}

Przypadek 3. niektóre klucze są ciągami , niektóre klucze są liczbami / liczbami całkowitymi .

Uwaga : Ta funkcja zwraca wartość true również dla pustych tablic.

//! 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;
}

Wynika, że:


Teraz, aby tablica była „prawdziwą” tablicą, do której wszyscy jesteśmy przyzwyczajeni, co oznacza:

  • Jej klawisze są wszystkie liczbowe / całkowitymi .
  • Jego klucze są sekwencyjne (tzn. Zwiększane o krok 1).
  • Jego klucze zaczynają się od zera .

Możemy to sprawdzić za pomocą następującej funkcji.

Przypadek 3a klucze są liczbowe / całkowite , sekwencyjne i zerowe .

Uwaga : Ta funkcja zwraca wartość true również dla pustych tablic.

//! 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);
}

Ostrzeżenia / pułapki (lub jeszcze bardziej osobliwe fakty na temat kluczy tablic w PHP)

Klucze całkowite

Kluczami dla tych tablic są liczby całkowite :

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

Klucze strunowe

Kluczami do tych tablic są ciągi znaków :

array("fish and chips" => "b");
array("" => "b");                                   // An empty string is also a string.
array("[email protected]" => "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!

Klucze całkowite, które wyglądają jak łańcuchy

Jeśli uważasz, że klucz array("13" => "b")jest ciągiem , jesteś w błędzie . Z dokumentu tutaj :

Ciągi zawierające prawidłowe liczby całkowite zostaną rzutowane na typ całkowity. Np. Klucz „8” faktycznie będzie przechowywany pod 8. Z drugiej strony „08” nie zostanie rzucony, ponieważ nie jest to poprawna liczba dziesiętna.

Na przykład kluczem do tych tablic są liczby całkowite :

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

Ale kluczem do tych tablic są ciągi :

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.

Co więcej, zgodnie z dokumentem ,

Rozmiar liczby całkowitej zależy od platformy, chociaż maksymalna wartość wynosząca około dwóch miliardów jest zwykle wartością (to 32 bitów ze znakiem). Platformy 64-bitowe zwykle mają maksymalną wartość około 9E18, z wyjątkiem systemu Windows, który zawsze jest 32-bitowy. PHP nie obsługuje liczb całkowitych bez znaku.

Tak więc kluczem do tej tablicy może być lub nie być liczbą całkowitą - to zależy od platformy.

array("60000000000" => "b");                // Array key could be integer or string, it can fit into a 64-bit (but not 32-bit) integer.

Co gorsza, PHP ma tendencję do błędów, jeśli liczba całkowita znajduje się w pobliżu granicy 2 31 = 2 147 483 648 (patrz błąd 51430 , błąd 52899 ). Na przykład w moim lokalnym środowisku (PHP 5.3.8 na XAMPP 1.7.7 na Windows 7) var_dump(array("2147483647" => "b"))daje

array(1) {
    [2147483647]=>
    string(1) "b"
}   

ale w tej wersji demonstracyjnej na żywo na codepad (PHP 5.2.5) daje to samo wyrażenie

array(1) {
    ["2147483647"]=>
    string(1) "b"
}

Zatem kluczem jest liczba całkowita w jednym środowisku, ale ciąg znaków w innym, mimo że 2147483647jest to 32-bitowa liczba całkowita ze znakiem .

Pang
źródło
2
Poza tym, jak wspomniałem poniżej, wymaga utworzenia duplikatu tablicy do sprawdzanej, co czyni ją bardzo kosztowną dla dużych tablic i potencjalnym źródłem awarii pamięci na współdzielonych hostach.
podperson
35

Pod względem prędkości:

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

Pod względem pamięci:

function isAssoc($array)
{
    $array = array_keys($array); return ($array !== array_keys($array));
}
Alix Axel
źródło
następująca tablica: tablica (02 => 11,1,2,456); jest pokazany jako nieposiadający klawiszy numerycznych przy użyciu powyższego algorytmu, nawet jeśli 02 === 2
Galileo_Galilei
20
function checkAssoc($array){
    return  ctype_digit( implode('', array_keys($array) ) );
}
dsims
źródło
2
To jedyna odpowiedź (w momencie mojego komentarza), która może poradzić sobie z następującymi kwestiami: $ array = array (0 => 'blah', 2 => 'yep', 3 => 'wahey')
Shabbyrobe
ale array('1'=>'asdf', '2'=>'too')będzie traktowany jako tablica asocjacyjna, podczas gdy tak naprawdę nie jest (klucze są tak naprawdę ciągami znaków)
Captain kurO
1
@CaptainkurO Masz na myśli numeryczne. Jest to tablica asocjacyjna.
devios1
1
Ta funkcja zwraca, truejeśli kluczami są: zero, liczby całkowite (tylko dodatnie), pusty ciąg znaków lub dowolna kombinacja powyższych, na przykład ciąg „09”. Ta funkcja nie uwzględnia kolejności kluczy. Tak array(0=>'blah', 2=>'yep', 3=>'wahey'), array(0=>'blah', 2=>'yep', 1=>'wahey')i array('blah', 'yep', 'wahey')to wszystko asocjacyjne według tej funkcji, a array('a'=>'blah', 'b'=>'yep', 'c'=>'wahey')nie jest.
Pang
@CaptainkurO jesteś w błędzie. „1” i „2” będą przechowywane jako liczby całkowite. Przeczytaj cytowaną część odpowiedzi wiewiórki z 11 maja 2011 o 19:34. PHP nie przechowuje kluczy ciągów, które wyglądają dokładnie jak liczby całkowite. Konwertuje je na liczby całkowite.
Buttle Butkus
20

W rzeczywistości najbardziej efektywnym sposobem jest:

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

Działa to, ponieważ porównuje klucze (które dla tablicy sekwencyjnej są zawsze 0,1,2 itd.) Z kluczami kluczy (które zawsze będą 0,1,2 itd.).

anon
źródło
1
Sprytne, ale nie dobre. Dlaczego jest to „najbardziej wydajne”? Byłoby o wiele bardziej czytelne po prostu porównać klucze tablicowe ($ a) do zakresu (0, liczba ($ a)). Najbardziej sprytne rozwiązanie rzadko jest najlepszym z mojego doświadczenia. Zwłaszcza gdy jest sprytny, nie dodaje dosłownie żadnej wartości nad oczywistą i czystą alternatywą.
Shane H
4
Funkcja ta zwraca truena array(1=>"a")ale falseza array("a"=>"a"). Bardziej sensowne byłoby !=zastąpienie go przez !==.
Pang
1
@ Pang masz rację. Myślałem, że twój komentarz z pewnością musi być na początku zły, ale, ku mojemu zaskoczeniu, [0] == ['a']w PHP (od 0 == 'a', i rzeczywiście 0 == 'banana'). ==Operator PHP jest szalony.
Mark Amery
2
Nie jest to wydajne, ponieważ wymaga wywołania tablic_kluczy, a jedynie sprawdzenia, dopóki nie znajdziesz niesekwencyjnego indeksu liczb całkowitych. Pod maską robisz, że tak czy owak , ale już duplikowane dużą tablicę.
podperson
17

Użyłem obu array_keys($obj) !== range(0, count($obj) - 1) i array_values($arr) !== $arr(które są podwójnymi względem siebie, chociaż drugi jest tańszy niż pierwszy), ale oba zawodzą w przypadku bardzo dużych tablic.

To dlatego, że array_keys i array_valuessą bardzo kosztowne operacje (ponieważ zbudować zupełnie nową tablicę o rozmiarze mniej więcej, że z oryginałem).

Następująca funkcja jest bardziej niezawodna niż metody przedstawione powyżej:

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;
}

Zauważ też, że jeśli nie chcesz rozróżniać rzadkich tablic od tablic asocjacyjnych, możesz po prostu wrócić 'assoc'z obuif bloków.

Wreszcie, chociaż może się to wydawać znacznie mniej „eleganckie” niż wiele „rozwiązań” na tej stronie, w praktyce jest znacznie wydajniejsze. Niemal każda tablica asocjacyjna zostanie wykryta natychmiast. Tylko indeksowane tablice zostaną sprawdzone wyczerpująco, a metody opisane powyżej nie tylko sprawdzą indeksowane tablice wyczerpująco, ale je powielają.

podperson
źródło
13

Myślę, że następujące dwie funkcje są najlepszym sposobem na sprawdzenie „czy tablica jest asocjacyjna czy numeryczna”. Ponieważ „numeryczne” może oznaczać tylko klawisze numeryczne lub tylko sekwencyjne klawisze numeryczne, poniżej wymieniono dwie funkcje, które sprawdzają jeden z warunków:

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));
}

Pierwsza funkcja sprawdza, czy każdy klawisz jest liczbą całkowitą. Druga funkcja sprawdza, czy każdy klucz jest liczbą całkowitą, a ponadto sprawdza, czy wszystkie klucze są sekwencyjne, zaczynając od $ base, która domyślnie wynosi 0, a zatem można je pominąć, jeśli nie trzeba podawać innej wartości podstawowej. key ($ my_array) zwraca null, jeśli wskaźnik odczytu zostanie przesunięty poza koniec tablicy, co kończy pętlę for i sprawia, że ​​instrukcja po pętli for true jest prawdziwa, jeśli wszystkie klucze były liczbami całkowitymi. Jeśli nie, pętla kończy się przedwcześnie, ponieważ klucz jest typu string, a instrukcja po pętli for zwróci false. Ta ostatnia funkcja dodatkowo dodaje jeden do $ base po każdym porównaniu, aby móc sprawdzić, czy następny klucz ma poprawną wartość. Ścisłe porównanie sprawia, że ​​sprawdza się również, czy klucz jest typu liczba całkowita. $ Base = (int) $ base część w pierwszej sekcji pętli for można pominąć, gdy $ base zostanie pominięte lub jeśli upewnisz się, że jest wywoływana tylko za pomocą liczby całkowitej. Ale ponieważ nie mogę być pewien wszystkich, zostawiłem to. W każdym razie instrukcja jest wykonywana tylko raz. Myślę, że są to najbardziej wydajne rozwiązania:

  • Jeśli chodzi o pamięć: brak kopiowania danych lub kluczowych zakresów. Wykonywanie wartości array_values ​​lub array_keys może wydawać się krótsze (mniej kodu), ale pamiętaj, co dzieje się w tle po wykonaniu tego wywołania. Tak, jest więcej (widocznych) stwierdzeń niż w niektórych innych rozwiązaniach, ale nie to się liczy, prawda?
  • Oszczędność czasu: oprócz faktu, że kopiowanie / wyodrębnianie danych i / lub kluczy również wymaga czasu, to rozwiązanie jest bardziej wydajne niż wykonywanie foreach. Ponownie Foreach może wydawać się niektórym bardziej efektywny, ponieważ ma krótszą notację, ale w tle foreach również wywołuje reset, klucz, a następnie wykonuje pętlę. Ale dodatkowo wywołuje także sprawdzenie poprawności warunku końcowego, którego tu się unikamy ze względu na kombinację ze sprawdzaniem liczby całkowitej.

Pamiętaj, że klucz tablicy może być tylko liczbą całkowitą lub łańcuchem, a łańcuch ściśle liczbowy, taki jak „1” (ale nie „01”), zostanie przetłumaczony na liczbę całkowitą. To sprawia, że ​​sprawdzanie klucza liczb całkowitych jest jedyną potrzebną operacją oprócz liczenia, jeśli chcesz, aby tablica była sekwencyjna. Oczywiście, jeśli is_indexed_array zwraca false, tablica może być postrzegana jako asocjacyjna. Mówię „widziałem”, bo tak naprawdę wszyscy są.

Niels Ockeloen
źródło
1
To najlepsza odpowiedź. Definicja tablicy „asocjacyjnej” lub „numerycznej” zależy od konkretnej sytuacji.
Pato
Jeśli foreach jest mniej wydajny niż zastosowana tutaj metoda, to oprócz niedogodności korzystania z dwóch różnych funkcji, wydajność tego rozwiązania jest lepsza niż moja (poprzednie). Podejrzewam, że tak nie jest, ponieważ Foreach jest zalecany jako najszybszy sposób na przejrzenie tablicy.
podperson
7

Ta funkcja obsługuje:

  • tablica z dziurami w indeksie (np. 1,2,4,5,8,10)
  • tablica z kluczami „0x”: np. klucz „08” jest skojarzony, a klucz „8” jest sekwencyjny.

pomysł jest prosty: jeśli jeden z kluczy NIE jest liczbą całkowitą, jest to tablica asocjacyjna, w przeciwnym razie jest sekwencyjna.

function is_asso($a){
    foreach(array_keys($a) as $key) {if (!is_int($key)) return TRUE;}
    return FALSE;
}
LazNiko
źródło
1
„jeśli jeden z kluczy NIE jest liczbą całkowitą, jest to tablica asocjacyjna, w przeciwnym razie jest sekwencyjny” - co? Nie, to po prostu źle. Jest miejsce na spór o to, co stanowi tablicę „asocjacyjną”, ale znaczenie „sekwencyjnego” jest dość jednoznaczne i nie jest takie samo, jak wszystkie klucze będące liczbami.
Mark Amery
Jeśli jeden z kluczy NIE jest liczbą całkowitą, jest z natury asocjatywny, jednak jest sekwencyjny tylko, jeśli klucze idą od 0 - długość (tablica) - 1. Jest jednak NUMERYCZNY, jeśli wszystkie klucze są tylko numerowane, ale mogą lub może nie działać z wieloma funkcjami tablicowymi, które wymagają tablicy sekwencyjnej. Jeśli przekształcisz tablicę numeryczną z dziurami w sekwencyjną, uruchamiając na niej wartości_wartości (tablica), wówczas zostanie przekonwertowana na sekwencyjną.
geilt
7

Zauważyłem dwa popularne podejścia do tego pytania: jedno za pomocą, array_values()a drugie za pomocą key(). Aby dowiedzieć się, co jest szybsze, napisałem mały program:

$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";

Dane wyjściowe dla programu w PHP 5.2 na CentOS są następujące:

Czas zajęty metodą nr 1 = 10,745 ms
Czas zajęty metodą nr 2 = 18,239 ms

Dane wyjściowe w PHP 5.3 dały podobne wyniki. Oczywiście korzystanie array_values()jest znacznie szybsze.

Manu Manjunath
źródło
zły poziom odniesienia. Nie testowałeś dużych tablic. Na moim komputerze od 10K + elementów metoda # 2 jest szybsza. Spróbuj z$arrays = Array( 'Array #1' => range(0, 50000), );
nonsensei
7

Jednym ze sposobów podejścia do tego jest skorzystanie z funkcji piggyback json_encode, która ma już własną wewnętrzną metodę rozróżniania między tablicą asocjacyjną a tablicą indeksowaną w celu uzyskania poprawnego JSON.

Możesz to zrobić, sprawdzając, czy pierwszy znak zwrócony po kodowaniu to {(tablica asocjacyjna) lub [(tablica indeksowana).

// Too short :)
function is_assoc($arr) {
    ksort($arr);
    return json_encode($arr)[0] === '{';
}
MAChitgarha
źródło
Według mnie ksort () nie jest konieczny. To rozwiązanie działa, ale musi sprawdzić, czy $ arr ma wartość NULL, a jeśli json_encode zawiedzie, więc spróbuj / złap. + nie jest naprawdę optymalne, jeśli $ arr jest duży.
lucbonnin
7

Istnieje już wiele odpowiedzi, ale oto metoda, na której Laravel polega w swojej klasie 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;
}

Źródło: https://github.com/laravel/framework/blob/5.4/src/Illuminate/Support/Arr.php

Ben
źródło
1
@Casey array_keys($keys)zwróci sekwencyjną tablicę liczb (0 ... X), która ma tę samą długość oryginalnej tablicy. Na przykład array_keys(["a", "b", "c"]) = [0, 1, 2]; array_keys([0, 1, 2]) = [0, 1, 2](jest to tablica sekwencyjna, ponieważ [0, 1, 2] !== [0, 1, 2]). Kolejny przykład: array_keys(["a" => 5, "b" => 7, "c" => 10]) = ["a", "b", "c"]; array_keys(["a", "b", "c"]) = [0, 1, 2](jest to tablica asocjacyjna, ponieważ ["a", "b", "c"] !== [0, 1, 2]). Mam nadzieję, że to jasne (trudne do wyjaśnienia w komentarzu, przynajmniej dla mnie)
valepu,
Ten algorytm jest zwariowany, łatwy i zrozumiały.
Benyi,
To nie zadziała, jeśli masz sekwencyjną tablicę wierszy asocjacyjnych.
lucbonnin
5
function array_is_assoc(array $a) {
    $i = 0;
    foreach ($a as $k => $v) {
        if ($k !== $i++) {
            return true;
        }
    }
    return false;
}

Szybka, zwięzła i wydajna pamięć. Bez kosztownych porównań, wywołań funkcji i kopiowania tablic.

Jesse
źródło
4

Korzystając z rozszerzenia xarray PHP

Możesz to zrobić bardzo szybko (około 30+ razy szybciej w PHP 5.6):

if (array_is_indexed($array)) {  }

Lub:

if (array_is_assoc($array)) {  }
c9s
źródło
3

Wiem, że dodawanie odpowiedzi do tej wielkiej kolejki jest nieco bezcelowe, ale oto czytelne rozwiązanie O (n), które nie wymaga powielania żadnych wartości:

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

Zamiast sprawdzać klucze, czy wszystkie są numeryczne, iterujesz po klawiszach, które byłyby tam dla tablicy numerycznej i upewniasz się, że istnieją.

cloudfeet
źródło
jeszcze jeden punkt. tablica w formie [1,2,null,4]zawiedzie, ale jest to poprawna tablica. więc dodałem pewne ulepszenia na stackoverflow.com/a/25206156/501831 z array_key_existskontrolą dodawania )
lazycommit
-1; isset()jest niewłaściwym narzędziem, ponieważ zwróci wartość false, jeśli wartość jest ustawiona, ale jest null, jak wskazał @lazycommit.
Mark Amery
3

Moje rozwiązanie:

function isAssociative(array $array)
{
    return array_keys(array_merge($array)) !== range(0, count($array) - 1);
}

array_mergena jednej tablicy reindeksuje wszystkie integerklucze, ale nie inne. Na przykład:

array_merge([1 => 'One', 3 => 'Three', 'two' => 'Two', 6 => 'Six']);

// This will returns [0 => 'One', 1 => 'Three', 'two' => 'Two', 2 => 'Six']

Więc jeśli tworzona jest lista (tablica niepowiązana), to ['a', 'b', 'c']wartość jest usuwana, unset($a[1])a następnie array_mergewywoływana, lista jest ponownie indeksowana, zaczynając od 0.

ByScripts
źródło
-1; jest to wykorzystywane O(n)w dodatkowej pamięci (ponieważ stworzyło wiele nowych tablic z tyloma elementami $array), odpowiedź nie rozwiązuje dwuznaczności zadanego pytania ani nie wyjaśnia dokładnie, w jaki sposób definiuje tablicę listy / niepowiązaną tablicę, a nawet jeśli żaden z tych punktów nie był prawdziwy, nie jest jasne, czy to dodaje wartości w porównaniu z innymi odpowiedziami już opublikowanymi.
Mark Amery
3

Po kilku lokalnych testach porównawczych, debugowaniu, sondowaniu kompilatora, profilowaniu i nadużywaniu 3v4l.org do porównywania kolejnych wersji (tak, dostałem ostrzeżenie, aby przestać) i porównywania z każdą odmianą, jaką mogłem znaleźć ...

Podaję ci funkcję testu macierzy asocjacyjnej opartej na analizie macierzy według najlepszego, przeciętnego i najgorszego przypadku, która w najgorszym przypadku jest w przybliżeniu tak dobra lub lepsza niż wszystkie inne scenariusze średnich przypadków.

/**
 * 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;
}

Od 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";
}
TylerY86
źródło
2

Oto metoda, której używam:

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')) );

Pamiętaj, że nie uwzględnia to szczególnych przypadków, takich jak:

$a = array( 1, 2, 3, 4 );

unset($a[1]);

assert( true === is_associative($a) );

Niestety nie mogę ci w tym pomóc. Jest również dość wydajny w przypadku tablic o przyzwoitych rozmiarach, ponieważ nie tworzy niepotrzebnych kopii. To te małe rzeczy sprawiają, że Python i Ruby są o wiele ładniejsi w pisaniu ...: P

AL the X
źródło
2
<?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);
}

?>

Oba te przykłady, które uzyskały najwięcej punktów, nie działają poprawnie z takimi tablicami $array = array('foo' => 'bar', 1)

KillEveryBody
źródło
+1 Twoja is_list () to IMO najlepsza odpowiedź. Niektórzy ludzie nie mają pojęcia o złożoności czasu i przestrzeni oraz natywnej funkcji skryptowej PHP ...
e2-e4
2

To też działałoby ( demo ):

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

Należy pamiętać, że głównym celem tej odpowiedzi jest poinformowanie Cię o istnieniu SplFixedArrayi nie zachęcanie do korzystania z wyjątków dla tego rodzaju testów.

Gordon
źródło
2

Myślę, że definicja tablicy skalarnej będzie się różnić w zależności od aplikacji. Oznacza to, że niektóre aplikacje będą wymagały bardziej ścisłego wyczucia tego, co kwalifikuje się jako tablica skalarna, a niektóre aplikacje będą wymagały bardziej luźnego wyczucia.

Poniżej przedstawiam 3 metody o różnej surowości.

<?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 <[email protected]>
 */

/**
 * 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;
}
David Farrell
źródło
2

Jeszcze jeden szybki ze źródła . Dopasuj kodowanie json_encode(i bson_encode). Podobnie jak zgodność z 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__)
        );
    }
}
rev lazycommit
źródło
1
Dlaczego isseti array_key_exists? czy to drugie nie wystarczy?
mcfedr
@mcfedr tak, byłoby - isset()czek tutaj jest całkowicie zbędny.
Mark Amery
@mcfedr, @ mark-amery ze względu na wydajność. isset()jest szybszy niż array_key_exists(). patrz ilia.ws/archives/…
lazycommit
@lazycommit Będzie to zależeć od twojej tablicy, a następnie od tego, czy jest lepsza z czy bez, nie to, że prawdopodobnie będzie miała tablicę z dużą ilością nulls, ale to też nie jest tak prawdopodobne, że masz tablicę wystarczająco dużą, aby zauważalna była różnica wydajności za pomocą obu kontroli
mcfedr
2
jeśli chcesz sprawdzić, czy będzie pasować json_encode, możesz po prostu sprawdzić pierwszy symbol łańcucha, zwrócony przez json_encode($your_arr)- czy to [lub {;-)
pilat
2

Czy to może być rozwiązanie?

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

Zastrzeżeniem jest oczywiście to, że kursor tablicy został zresetowany, ale powiedziałbym, że prawdopodobnie funkcja jest używana, zanim tablica zostanie jeszcze przemierzona lub użyta.

Kat Lim Ruiz
źródło
Ta funkcja zwraca wartość false dla obu array("a", "b")i array("a", "b" => "B")ponieważ sprawdza tylko pierwszy klucz. BTW, is_longto tylko pseudonimis_int .
Pang
1
całkiem szczerze, myślę, że byłoby to bardzo skuteczne w zdecydowanej większości przypadków i jest znacznie wydajniejsze niż alternatywy. Jeśli zrozumiesz konsekwencje tej metody i zdasz sobie sprawę, że zadziała ona dla Ciebie, prawdopodobnie jest to najlepszy wybór.
Gershom
To jest po prostu złe; patrzy tylko na pierwszy klucz.
Mark Amery
@ MarkAmery pytanie, jak odróżnić tablice czysto sekwencyjne od tablic czysto asocjacyjnych. Ta odpowiedź robi dokładnie to i jest najbardziej wydajna ze wszystkich. Niezdefiniowane zachowanie dla mieszanych tablic jest w porządku w kontekście pytania. +1
Tobia,
@Tobia Nie sądzę, aby większość ludzi zgodziła się z tobą klasyfikując, powiedzmy, [7 => 'foo', 2 => 'bar']jako „mieszaną” tablicę, która jest częściowo, ale nie „czysto” sekwencyjna. Wydaje mi się, że jest to po prostu niewłaściwe użycie słów.
Mark Amery
2

Wiele z tych rozwiązań jest eleganckich i ładnych, ale nie skaluje się dobrze i wymaga dużej ilości pamięci lub procesora. Większość tworzy 2 nowe punkty danych w pamięci za pomocą tego rozwiązania z obu stron porównania. Im większa matryca, tym trudniejszy i dłuższy proces i używana pamięć oraz utrata korzyści z oceny zwarcia. Zrobiłem kilka testów z kilkoma różnymi pomysłami. Próba uniknięcia tablicy_klucza_istnieje, ponieważ jest to kosztowne, a także unikanie tworzenia nowych dużych zestawów danych do porównania. Wydaje mi się, że jest to prosty sposób na stwierdzenie, czy tablica jest sekwencyjna.

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;
}

Uruchamiasz jedną liczbę na głównej tablicy i zapisujesz jedną liczbę całkowitą. Następnie zapętlasz tablicę i sprawdzasz dokładne dopasowanie podczas iteracji licznika. Powinieneś mieć od 1, aby liczyć. Jeśli zawiedzie, nastąpi zwarcie, co daje wzrost wydajności, gdy jest fałszywy.

Pierwotnie robiłem to z pętlą for i sprawdzaniem isset ($ arr [$ i]), ale to nie wykryje pustych kluczy, które wymagają tablicy_klucz_istniejących, a jak wiemy, jest to najgorsza funkcja do użycia dla prędkości.

Ciągłe aktualizowanie zmiennych za pomocą foreach w celu sprawdzenia wraz z iteratorem, który nigdy nie rośnie, jego rozmiar jest liczbą całkowitą, wykorzystajmy PHP, jest on wbudowany w optymalizację pamięci, buforowanie i wyrzucanie elementów bezużytecznych, aby utrzymać bardzo niskie zużycie zasobów.

Będę również argumentować, że użycie kluczy tablicowych w foreach jest głupie, kiedy można po prostu uruchomić $ key => $ wartość i sprawdzić klucz. Po co tworzyć nowy punkt danych? Po wyodrębnieniu kluczy macierzy natychmiast zużyłeś więcej pamięci.

geilt
źródło
1

odpowiedzi są już podane, ale zbyt wiele dezinformacji dotyczących wydajności. Napisałem ten mały skrypt porównawczy, który pokazuje, że metoda Foreach jest najszybsza.

Uwaga: następujące metody zostały skopiowane z innych odpowiedzi

<?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";
}

wyniki:

==== 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
nonsensei
źródło
1

Lub możesz po prostu użyć tego:

Arr::isAssoc($array)

który sprawdzi, czy tablica zawiera jakiś klucz nienumeryczny lub:

Arr:isAssoc($array, true)

aby sprawdzić, czy tablica jest ściśle sekwencyjna (zawiera automatycznie generowane klucze int od 0 do n-1 )

za pomocą tej biblioteki.

Minwork
źródło
0

Jeśli PHP nie ma wbudowanego do tego celu, nie będziesz w stanie tego zrobić w mniej niż O (n) - wyliczając wszystkie klucze i sprawdzając typ liczb całkowitych. W rzeczywistości chcesz również upewnić się, że nie ma żadnych dziur, więc Twój algorytm może wyglądać następująco:

for i in 0 to len(your_array):
    if not defined(your-array[i]):
        # this is not an array array, it's an associative array :)

Ale po co zawracać sobie głowę? Załóżmy, że tablica jest tego typu, którego oczekujesz. Jeśli tak nie jest, po prostu wybuchnie ci w twarz - to dla ciebie dynamiczne programowanie! Przetestuj swój kod, a wszystko będzie dobrze ...

Daren Thomas
źródło
1
Zwykle wystarczy założyć, że tablica jest pożądanym typem. Ale w moim przypadku przechodzę przez wielowymiarową tablicę i formatuję dane wyjściowe w zależności od typu tablicy danego węzła.
Wilco
0

Porównuję różnicę między kluczami tablicy a kluczami wyniku array_values ​​() tablicy, która zawsze będzie tablicą z indeksami liczb całkowitych. Jeśli klucze są takie same, nie jest to tablica asocjacyjna.

function isHash($array) {
    if (!is_array($array)) return false;
    $diff = array_diff_assoc($array, array_values($array));
    return (empty($diff)) ? false : true;
}
philroy
źródło
-1; zużywa to O(n)dodatkową pamięć, gdy $arrayma nprzedmioty, a pisanie (someboolean) ? false : truezamiast !somebooleanjest okropne i nieokreślone.
Mark Amery