Wie überprüfe ich, ob das PHP-Array assoziativ oder sequentiell ist?


781

PHP behandelt alle Arrays als assoziativ, daher sind keine Funktionen integriert. Kann jemand eine ziemlich effiziente Methode empfehlen, um zu überprüfen, ob ein Array nur numerische Schlüssel enthält?

Grundsätzlich möchte ich in der Lage sein, Folgendes zu unterscheiden:

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

und das:

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

382
Ihr Code enthält einen Fehler: Tomate ist eine Frucht.
Olle Härstedt

9
Diese Methode hat Vorbehalte, aber oft mache ich es einfach if (isset($array[0])), was einfach und schnell ist. Natürlich sollten Sie zuerst sicherstellen, dass das Array nicht leer ist, und Sie sollten einige Kenntnisse über den möglichen Inhalt des Arrays haben, damit die Methode nicht fehlschlagen kann (wie gemischte numerische / assoziative oder nicht sequentielle).
Gras Double

@ OlleHärstedt Nicht nach US High Court. ;-)
MC Emperor

Antworten:


622

Sie haben zwei Fragen gestellt, die nicht ganz gleichwertig sind:

  • Erstens, wie man bestimmt, ob ein Array nur numerische Schlüssel hat
  • Zweitens, wie man bestimmt, ob ein Array sequentielle Zifferntasten hat, beginnend mit 0

Überlegen Sie, welches dieser Verhaltensweisen Sie tatsächlich benötigen. (Es kann sein, dass beides für Ihre Zwecke ausreicht.)

Die erste Frage (einfach überprüfen, ob alle Schlüssel numerisch sind) wird von Captain KurO gut beantwortet .

Für die zweite Frage (Überprüfen, ob das Array nullindiziert und sequentiell ist) können Sie die folgende Funktion verwenden:

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

32
Sehr elegante Lösung. Beachten Sie, dass im (mehrdeutigen) Fall eines leeren Arrays TRUE zurückgegeben wird.
Jonathan Lidbeck

30
Ich denke, es ist sinnvoller, sich sequentielle Arrays als einen Sonderfall von assoziativen Arrays vorzustellen. Jedes Array ist also assoziativ, aber nur einige sind sequentiell. Daher isSequential()wäre eine Funktion sinnvoller als isAssoc(). In eine solche Funktion, die leere Array sollte als sequenzielle gesehen werden. Die Formel könnte sein array() === $arr || !isAssoc($arr).
Donquijote

18
Ich denke, dies würde viel potenzielle CPU-Zeit und Speicher vermeiden, wenn man prüfen würde, ob isset ($ arr [0]) falsch ist, bevor alle Schlüssel extrahiert werden, da es eindeutig assoziativ ist, wenn das Array nicht leer ist, aber kein Element in 0 hat Position. Da "die meisten" echten assoziativen Arrays Zeichenfolgen als Schlüssel haben, sollte dies eine gute Optimierung für den allgemeinen Fall einer solchen Funktion sein.
OderWat

10
@OderWat - Ihre Optimierung sollte array_key_existsanstelle von verwendet werden, issetda das isset falsch false zurückgibt, wenn das Nullelement ein Nullwert ist. Ein Nullwert sollte normalerweise ein legitimer Wert in einem solchen Array sein.
OCDev

@MAChitgarha, Ihre Bearbeitung hat das Verhalten der Funktion ohne Erklärung des Grundes geändert und dazu geführt, dass sie der Beschreibung in der obigen Prosa widerspricht, was sie eigentlich tun soll. Ich habe es zurückgesetzt.
Mark Amery

431

So überprüfen Sie lediglich, ob das Array nicht ganzzahlige Schlüssel enthält (nicht, ob das Array sequenziell oder nullindiziert ist):

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

Wenn mindestens ein Zeichenfolgenschlüssel vorhanden ist, $arraywird dies als assoziatives Array betrachtet.


22
Diese Methode ist viel besser als es scheint. Wenn count (filtered_array) == count (original_array) ist, handelt es sich um ein Assoc-Array. Wenn count (filtered_array) == 0 ist, handelt es sich um ein indiziertes Array. Wenn count (filtered_array) <count (original_array) ist, verfügt das Array sowohl über numerische als auch über Zeichenfolgenschlüssel.
Jamol

5
@ MikePretzlaw iteriert natürlich ; Es gibt (offensichtlich) keine Möglichkeit festzustellen, ob alle Schlüssel des Arrays Ints sind, ohne alle Schlüssel im Array zu betrachten. Ich nehme an, die nicht iterierenden Alternativen, die wir unten sehen sollen, sind solche wie $isIndexed = array_values($arr) === $arr;? Zu dem frage ich: Wie denkst du array_values()funktioniert das? Wie funktioniert Ihrer Meinung nach die ===Anwendung auf Arrays? Die Antwort ist natürlich, dass sie auch über das Array iterieren.
Mark Amery

4
@ARW "PHP scheint alles in ein int in einer Array-Definition umzuwandeln, wenn es kann." - Ja, genau das passiert. Das größte WTF ist, dass es dies sogar tut, um zu schweben; Wenn Sie es versuchen, werden var_dump([1.2 => 'foo', 1.5 => 'bar']);Sie feststellen, dass Sie das Array erhalten [1 => 'bar']. Es gibt überhaupt keine Möglichkeit, den Originaltyp eines Schlüssels herauszufinden. Ja, das alles ist schrecklich; PHPs Arrays sind bei weitem der schlechteste Teil der Sprache, und der größte Teil des Schadens ist irreparabel und beruht auf der Idee, ein einziges Konstrukt für traditionelle Arrays zu verwenden, und traditionelle Hashmaps sind von Anfang an schrecklich.
Mark Amery

30
@MarkAmery Das Obige ist zwar einfach, garantiert aber einen 100% igen Spaziergang durch das Array. Es wäre effizienter, insbesondere wenn Sie mit großen Arrays arbeiten, wenn Sie nach Zeichenfolge oder Int suchen und beim ersten Mal ausbrechen. Zum Beispiel: function isAssociative($arr) { foreach ($arr as $key => $value) { if (is_string($key)) return true; } return false; }
Dachte

1
@Thought Ihr Code arbeitet sehr schnell, kann jedoch kein sequentielles Array erkennen . Beispiel array(1 => 'a', 0 => 'b', 2 => 'c')wird false(sequentielles Array), während es sein sollte true(assoziatives Array). toolsqa.com/data-structures/array-in-programming Ich bin mir nicht sicher, ob der Schlüssel in aufsteigender Reihenfolge sein muss. (0, 1, ...)
vee

132

Dies ist sicherlich eine bessere Alternative.

<?php
$arr = array(1,2,3,4);
$isIndexed = array_values($arr) === $arr;

52
Dadurch werden die Werte im Array dupliziert, was möglicherweise sehr teuer ist. Sie sind viel besser dran, die Array-Schlüssel zu untersuchen.
Meagar

8
Ich habe gerade == verwendet; Ich glaube nicht, dass hier === notwendig ist. Um das "nicht gesetzt und es funktioniert nicht" zu beantworten: Sobald Sie das erste Element deaktiviert haben, ist es kein ganzzahlig indiziertes Array mehr, das bei 0 beginnt. IMO funktioniert es also.
Grantwparks

4
Stimmen Sie mit @grantwparks überein: Ein Array mit geringer Dichte wird nicht indiziert. Interessanterweise, weil es keine Möglichkeit gibt, ein Element tatsächlich aus der Mitte eines indizierten Arrays zu löschen, deklariert PHP im Grunde alle Arrays als assoziativ und numerisch. Dies ist nur eine Version, die den Schlüssel für mich ausmacht.
RickMeasham

7
Das einzige Problem, das ich damit habe, ist, dass die Zeit ===damit verschwendet wird, zu überprüfen, ob die Werte gleich sind, obwohl wir nur an den Schlüsseln interessiert sind. Aus diesem Grund bevorzuge ich die $k = array_keys( $arr ); return $k === array_keys( $k );Version.
Jesse

5
Ein zusätzlicher Hinweis: Dies schlägt bei Arrays fehl, die mit nicht ordnungsgemäßen Zifferntasten angegeben wurden. zB $ myArr = Array (0 => 'a', 3 => 'b', 4 => 1, 2 => 2, 1 => '3'); Eine mögliche Lösung besteht darin, ksort ($ arr) vor dem Test auszuführen
Scott

77

Viele Kommentatoren in dieser Frage verstehen nicht, wie Arrays in PHP funktionieren. Aus der Array-Dokumentation :

Ein Schlüssel kann entweder eine Ganzzahl oder eine Zeichenfolge sein. Wenn ein Schlüssel die Standarddarstellung einer Ganzzahl ist, wird er als solcher interpretiert (dh "8" wird als 8 interpretiert, während "08" als "08" interpretiert wird). Gleitkommazahlen in Schlüssel werden auf Ganzzahl abgeschnitten. Der indizierte und der assoziative Array-Typ sind in PHP vom selben Typ, der sowohl Ganzzahl- als auch Zeichenfolgenindizes enthalten kann.

Mit anderen Worten, es gibt keinen Array-Schlüssel von "8", da er immer (stillschweigend) in die Ganzzahl 8 konvertiert wird. Daher ist es nicht erforderlich, zwischen Ganzzahlen und numerischen Zeichenfolgen zu unterscheiden.

Wenn Sie die effizienteste Methode zum Überprüfen eines Arrays auf nicht ganzzahlige Schlüssel suchen, ohne eine Kopie eines Teils des Arrays (wie bei array_keys ()) oder des gesamten Arrays (wie bei foreach) zu erstellen:

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

Dies funktioniert, weil key () NULL zurückgibt, wenn die aktuelle Array-Position ungültig ist und NULL niemals ein gültiger Schlüssel sein kann (wenn Sie versuchen, NULL als Array-Schlüssel zu verwenden, wird es stillschweigend in "" konvertiert).


Dies funktioniert nicht für nicht sequentielle Ganzzahlschlüssel. Versuchen Sie es mit [2 => 'a', 4 => 'b'].
DavidJ

2
@ DavidJ, was meinst du mit "funktioniert nicht"? Es wird erfolgreich festgestellt, dass alle Schlüssel Ganzzahlen sind. Behaupten Sie, dass ein Array wie das von Ihnen veröffentlichte nicht als "numerisches Array" betrachtet werden sollte?
Coredumperror

7
Ein nicht assoziatives Array muss Schlüssel von 0bis count($array)-1in dieser strengen Reihenfolge haben. Eine vorläufige Überprüfung mit is_array()kann helfen. Fügen Sie eine zunehmende Variable hinzu, um die Tastenfolge zu überprüfen: Damit ist for ($k = 0, reset($array) ; $k === key($array) ; next($array)) ++$k;das Geschäft abgeschlossen.
ofavre

2
Die Verwendung foreachanstelle der expliziten Iteration ist etwa doppelt so schnell.
ofavre

1
Wenn Sie dies zu einer Funktion machen möchten: function isAssocStr($array) { for (reset($array); is_int(key($array)); next($array)) { if (is_null(key($array))) return false; } return true; }
GreeKatrina

39

Wie vom OP angegeben :

PHP behandelt alle Arrays als assoziativ

Es ist nicht ganz sinnvoll (IMHO), eine Funktion zu schreiben, die prüft, ob ein Array assoziativ ist . Also als erstes: Was ist ein Schlüssel in einem PHP-Array ?

Der Schlüssel kann entweder eine Ganzzahl oder eine Zeichenfolge sein .

Das heißt, es gibt 3 mögliche Fälle:

  • Fall 1. Alle Schlüssel sind numerisch / ganzzahlig .
  • Fall 2. Alle Schlüssel sind Zeichenfolgen .
  • Fall 3. Einige Schlüssel sind Zeichenfolgen , einige Schlüssel sind numerische / ganze Zahlen .

Wir können jeden Fall mit den folgenden Funktionen überprüfen.

Fall 1: Alle Schlüssel sind numerisch / ganzzahlig .

Hinweis : Diese Funktion gibt auch für leere Arrays true zurück .

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

Fall 2: Alle Schlüssel sind Zeichenfolgen .

Hinweis : Diese Funktion gibt auch für leere Arrays true zurück .

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

Fall 3. Einige Schlüssel sind Zeichenfolgen , einige Schlüssel sind numerische / ganze Zahlen .

Hinweis : Diese Funktion gibt auch für leere Arrays true zurück .

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

Es folgt dem:


Nun, damit ein Array ein "echtes" Array ist , an das wir alle gewöhnt sind, was bedeutet:

  • Die Schlüssel sind alle numerisch / ganzzahlig .
  • Die Tasten sind sequentiell (dh um Schritt 1 erhöht).
  • Die Tasten beginnen bei Null .

Wir können mit der folgenden Funktion überprüfen.

Fall 3a. Schlüssel sind numerisch / ganzzahlig , sequentiell und nullbasiert .

Hinweis : Diese Funktion gibt auch für leere Arrays true zurück .

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

Vorsichtsmaßnahmen / Fallstricke (oder noch eigentümlichere Fakten zu Array-Schlüsseln in PHP)

Ganzzahlige Schlüssel

Die Schlüssel für diese Arrays sind Ganzzahlen :

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

String-Schlüssel

Die Schlüssel für diese Arrays sind Zeichenfolgen :

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

Ganzzahlige Schlüssel, die wie Zeichenfolgen aussehen

Wenn Sie glauben, dass der Schlüssel array("13" => "b")eine Zeichenfolge ist , liegen Sie falsch . Aus dem Dokument hier :

Zeichenfolgen, die gültige Ganzzahlen enthalten, werden in den Ganzzahltyp umgewandelt. ZB wird der Schlüssel "8" tatsächlich unter 8 gespeichert. Andererseits wird "08" nicht umgewandelt, da es sich nicht um eine gültige Dezimalzahl handelt.

Der Schlüssel für diese Arrays sind beispielsweise Ganzzahlen :

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

Der Schlüssel für diese Arrays sind jedoch Zeichenfolgen :

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.

Was mehr ist, laut dem Dokument ,

Die Größe einer Ganzzahl ist plattformabhängig, obwohl ein Maximalwert von etwa zwei Milliarden der übliche Wert ist (das sind 32 Bit Vorzeichen). 64-Bit-Plattformen haben normalerweise einen Maximalwert von etwa 9E18, mit Ausnahme von Windows, das immer 32-Bit ist. PHP unterstützt keine vorzeichenlosen Ganzzahlen.

Der Schlüssel für dieses Array kann also eine Ganzzahl sein oder auch nicht - dies hängt von Ihrer Plattform ab.

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

Noch schlimmer ist , neigt dazu , PHP zu sein Buggy , wenn die ganzen Zahl in der Nähe der 2 31 = 2147483648 Grenze (siehe Bug 51430 , bug 52899 ). Zum Beispiel in meiner lokalen Umgebung (PHP 5.3.8 unter XAMPP 1.7.7 unter Windows 7) var_dump(array("2147483647" => "b"))gibt

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

In dieser Live-Demo auf dem Codepad (PHP 5.2.5) gibt es jedoch den gleichen Ausdruck

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

Der Schlüssel ist also eine Ganzzahl in einer Umgebung, aber eine Zeichenfolge in einer anderen, obwohl 2147483647es sich um eine gültige vorzeichenbehaftete 32-Bit- Ganzzahl handelt .


2
Außer, wie ich weiter unten erwähne, muss ein doppeltes Array zu dem zu überprüfenden Array erstellt werden, was es für große Arrays sehr teuer macht und eine potenzielle Ursache für Abstürze aufgrund von Speichermangel auf gemeinsam genutzten Hosts darstellt.
Podperson

35

In Bezug auf die Geschwindigkeit:

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

In Bezug auf das Gedächtnis:

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

das folgende Array: Array (02 => 11,1,2,456); wird gezeigt, dass unter Verwendung des obigen Algorithmus keine numerischen Schlüssel vorhanden sind, selbst wenn 02 === 2
Galileo_Galilei

20
function checkAssoc($array){
    return  ctype_digit( implode('', array_keys($array) ) );
}

2
Dies ist die einzige Antwort (zum Zeitpunkt meines Kommentars), die sich mit Folgendem befassen kann: $ array = array (0 => 'blah', 2 => 'yep', 3 => 'wahey')
Shabbyrobe

sondern array('1'=>'asdf', '2'=>'too')wird als assoziatives Array angesehen werden , während es eigentlich nicht (die Tasten sind eigentlich string)
Kapitän Kuro

1
@CaptainkurO Du meinst numerisch. Es ist ein assoziatives Array.
Devios1

1
Diese Funktion gibt zurück, truewenn die Schlüssel Null sind, Ganzzahlen (nur positiv), eine leere Zeichenfolge oder eine beliebige Kombination der oben genannten, z. B. die Zeichenfolge "09". Diese Funktion berücksichtigt nicht die Reihenfolge der Schlüssel. Also array(0=>'blah', 2=>'yep', 3=>'wahey'), array(0=>'blah', 2=>'yep', 1=>'wahey')und array('blah', 'yep', 'wahey')sind alle gemäß dieser Funktion assoziativ, während dies array('a'=>'blah', 'b'=>'yep', 'c'=>'wahey')nicht der Fall ist.
Pang

@CaptainkurO Sie sind falsch. '1' und '2' werden als ganze Zahlen gespeichert. Lesen Sie den zitierten Teil der Antwort von Eichhörnchen vom 11. Mai 2011 um 19:34 Uhr. PHP speichert keine Zeichenfolgenschlüssel, die genau wie Ganzzahlen aussehen. Es konvertiert diese in ganze Zahlen.
Buttle Butkus

20

Eigentlich ist der effizienteste Weg also:

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

Dies funktioniert, weil die Schlüssel (die für ein sequentielles Array immer 0,1,2 usw. sind) mit den Schlüsseln der Schlüssel (die immer 0,1,2 usw. sind) verglichen werden.


1
Clever, aber nicht gut. Warum ist das "am effizientesten"? Es wäre viel besser lesbar, nur die array_keys ($ a) mit dem Bereich (0, count ($ a)) zu vergleichen. Die klügste Lösung ist meiner Erfahrung nach selten die beste. Besonders wenn man klug ist, hat die offensichtliche und saubere Alternative buchstäblich keinen Wert.
Shane H

4
Diese Funktion gibt truefür array(1=>"a")aber falsefür zurück array("a"=>"a"). Wäre sinnvoller, wenn !=durch ersetzt wird !==.
Pang

1
@Pang du bist richtig. Ich dachte, Ihr Kommentar muss zunächst sicherlich falsch sein, aber zu meiner Überraschung [0] == ['a']in PHP (seit 0 == 'a'und in der Tat 0 == 'banana'). Der ==Betreiber von PHP ist verrückt.
Mark Amery

2
Es ist insofern nicht effizient, als es darum geht, array_keys aufzurufen und nur zu prüfen, bis Sie einen nicht sequentiellen Integer-Index finden. Unter der Haube machst du das sowieso , aber du hast bereits ein großes Array dupliziert.
Podperson

17

Ich habe beide array_keys($obj) !== range(0, count($obj) - 1)und array_values($arr) !== $arr(die Duals voneinander sind, obwohl die zweite billiger als die erste ist) verwendet, aber beide scheitern bei sehr großen Arrays.

Dies liegt daran , array_keysund array_valuessind beide sehr teuer Operationen (da sie eine ganz neue Reihe von Größe in etwa die den ursprünglichen bauen).

Die folgende Funktion ist robuster als die oben angegebenen Methoden:

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

Beachten Sie auch, dass Sie einfach 'assoc'von beiden ifBlöcken zurückkehren können, wenn Sie keine spärlichen Arrays von assoziativen Arrays unterscheiden möchten .

Während dies auf dieser Seite weniger "elegant" erscheint als viele "Lösungen", ist es in der Praxis weitaus effizienter. Fast jedes assoziative Array wird sofort erkannt. Nur indizierte Arrays werden ausführlich geprüft, und die oben beschriebenen Methoden prüfen indizierte Arrays nicht nur ausführlich, sondern duplizieren sie auch.


13

Ich denke, die folgenden zwei Funktionen sind der beste Weg, um zu überprüfen, ob ein Array assoziativ oder numerisch ist. Da 'numerisch' nur Zifferntasten oder nur sequentielle Zifferntasten bedeuten kann, sind nachfolgend zwei Funktionen aufgeführt, die beide Bedingungen überprüfen:

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

Die erste Funktion prüft, ob jeder Schlüssel ein ganzzahliger Wert ist. Die zweite Funktion prüft, ob jeder Schlüssel ein ganzzahliger Wert ist, und prüft außerdem, ob alle Schlüssel ab $ base sequentiell sind. Der Standardwert ist 0 und kann daher weggelassen werden, wenn Sie keinen anderen Basiswert angeben müssen. key ($ my_array) gibt null zurück, wenn der Lesezeiger über das Ende des Arrays hinausbewegt wird. Dies beendet die for-Schleife und macht die Anweisung nach der for-Schleife true, wenn alle Schlüssel ganzzahlig sind. Wenn nicht, endet die Schleife vorzeitig, da ein Schlüssel vom Typ string ist und die Anweisung nach der for-Schleife false zurückgibt. Die letztere Funktion addiert zusätzlich nach jedem Vergleich eine zu $ ​​base, um zu überprüfen, ob der nächste Schlüssel den richtigen Wert hat. Durch den strengen Vergleich wird auch geprüft, ob der Schlüssel vom Typ Integer ist. Der $ base = (int) $ base-Teil im ersten Abschnitt der for-Schleife kann weggelassen werden, wenn $ base weggelassen wird oder wenn Sie sicherstellen, dass er nur mit einer Ganzzahl aufgerufen wird. Aber da ich nicht für alle sicher sein kann, habe ich es belassen. Die Anweisung wird sowieso nur einmal ausgeführt. Ich denke, das sind die effizientesten Lösungen:

  • Speicher weise: Kein Kopieren von Daten oder Schlüsselbereichen. Das Ausführen von array_values ​​oder array_keys mag kürzer erscheinen (weniger Code), aber denken Sie daran, was im Hintergrund passiert, wenn Sie diesen Aufruf tätigen. Ja, es gibt mehr (sichtbare) Aussagen als in einigen anderen Lösungen, aber das ist nicht das, was zählt, oder?
  • Zeitlich gesehen: Neben der Tatsache, dass das Kopieren / Extrahieren von Daten und / oder Schlüsseln auch Zeit in Anspruch nimmt, ist diese Lösung effizienter als ein Foreach. Wiederum mag ein foreach für manche effizienter erscheinen, weil er in der Notation kürzer ist, aber im Hintergrund ruft foreach auch reset, key und als nächstes eine Schleife auf. Zusätzlich wird aber auch gültig aufgerufen, um die Endbedingung zu überprüfen, was hier aufgrund der Kombination mit der Ganzzahlprüfung vermieden wird.

Denken Sie daran, dass ein Array-Schlüssel nur eine Ganzzahl oder eine Zeichenfolge sein kann und eine streng numerische Zeichenfolge wie "1" (aber nicht "01") in eine Ganzzahl übersetzt wird. Aus diesem Grund ist die Suche nach einem Ganzzahlschlüssel neben dem Zählen die einzige erforderliche Operation, wenn das Array sequentiell sein soll. Wenn is_indexed_array false zurückgibt, kann das Array natürlich als assoziativ angesehen werden. Ich sage "gesehen", weil sie es tatsächlich alle sind.


1
Dies ist die beste Antwort. Die Definition eines "assoziativen" oder "numerischen" Arrays hängt von der spezifischen Situation ab.
Pato

Wenn foreach weniger effizient ist als die hier verwendete Methode, ist die Leistung dieser Lösung, abgesehen von der Unannehmlichkeit, zwei verschiedene Funktionen zu verwenden, besser als meine (die vorhergehende). Ich vermute, dass dies nicht der Fall ist, da foreach als schnellster Weg zum Durchlaufen eines Arrays empfohlen wird.
Podperson

7

Diese Funktion kann Folgendes verarbeiten:

  • Array mit Löchern im Index (zB 1,2,4,5,8,10)
  • Array mit "0x" -Tasten: zB ist der Schlüssel '08' assoziativ, während der Schlüssel '8' sequentiell ist.

Die Idee ist einfach: Wenn einer der Schlüssel KEINE Ganzzahl ist, handelt es sich um ein assoziatives Array, andernfalls handelt es sich um ein sequentielles Array.

function is_asso($a){
    foreach(array_keys($a) as $key) {if (!is_int($key)) return TRUE;}
    return FALSE;
}

1
"Wenn einer der Schlüssel KEINE Ganzzahl ist, ist er ein assoziatives Array, andernfalls ist er sequentiell" - huh? Nein, das ist einfach falsch. Es gibt Raum für Streit darüber, was ein "assoziatives" Array ausmacht, aber die Bedeutung von "sequentiell" ist ziemlich eindeutig und nicht dasselbe wie alle Schlüssel, die Zahlen sind.
Mark Amery

Wenn einer der Schlüssel KEINE Ganzzahl ist, ist er von Natur aus assoziativ. Er ist jedoch nur sequentiell, wenn die Schlüssel von 0 - Länge (Array) - 1 reichen. Er ist jedoch NUMERISCH, wenn alle Schlüssel nur nummeriert sind, aber möglicherweise oder funktioniert möglicherweise nicht mit vielen Array-Funktionen, die ein sequentielles Array erfordern. Wenn Sie das numerische Array mit Löchern in sequentiell konvertieren, indem Sie array_values ​​(Array) darauf ausführen, wird es in sequentiell konvertiert.
Geilt

7

Ich habe zwei beliebte Ansätze für diese Frage bemerkt: einen mit array_values()und einen mit key(). Um herauszufinden, was schneller ist, habe ich ein kleines Programm geschrieben:

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

Die Ausgabe für das Programm unter PHP 5.2 unter CentOS lautet wie folgt:

Zeitaufwand mit Methode 1 = 10,745 ms
Zeitaufwand mit Methode 2 = 18,239 ms

Die Ausgabe von PHP 5.3 ergab ähnliche Ergebnisse. Offensichtlich ist die Verwendung array_values()viel schneller.


schlechter Benchmark. Sie haben nicht auf große Arrays getestet. Auf meinem Computer ist Methode 2 ab 10K + Elementen schneller. Versuchen Sie mit$arrays = Array( 'Array #1' => range(0, 50000), );
nonsensei

7

Eine Möglichkeit, dies zu erreichen, ist das Huckepack- json_encodeVerfahren, das bereits über eine eigene interne Methode zur Unterscheidung zwischen einem assoziativen Array und einem indizierten Array verfügt, um den richtigen JSON auszugeben.

Sie können dies tun, indem Sie überprüfen, ob das erste nach der Codierung zurückgegebene Zeichen ein {(assoziatives Array) oder ein [(indiziertes Array) ist.

// Too short :)
function is_assoc($arr) {
    ksort($arr);
    return json_encode($arr)[0] === '{';
}

Das ksort () ist meiner Meinung nach nicht notwendig. Diese Lösung funktioniert, muss aber testen, ob $ arr null ist und ob json_encode fehlschlägt, also ein Versuch / Fang. + Es ist nicht wirklich optimal, wenn $ arr groß ist.
Lucbonnin

7

Es gibt bereits viele Antworten, aber hier ist die Methode, auf die sich Laravel in seiner Arr-Klasse stützt:

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

Quelle: https://github.com/laravel/framework/blob/5.4/src/Illuminate/Support/Arr.php


1
@Casey array_keys($keys)gibt ein sequentielles Array von Zahlen (0 ... X) zurück, das dieselbe Länge wie das ursprüngliche Array hat. Zum Beispiel array_keys(["a", "b", "c"]) = [0, 1, 2]; array_keys([0, 1, 2]) = [0, 1, 2](es ist ein sequentielles Array, weil [0, 1, 2] !== [0, 1, 2]). Ein weiteres Beispiel: array_keys(["a" => 5, "b" => 7, "c" => 10]) = ["a", "b", "c"]; array_keys(["a", "b", "c"]) = [0, 1, 2](Es ist ein assoziatives Array, weil ["a", "b", "c"] !== [0, 1, 2]). Hoffe es ist klar (schwer ausführlich in einem Kommentar zu erklären, zumindest für mich)
Valepu

Dieser Algorithmus ist verrückt, einfach und verständlich.
Benyi

Dies funktioniert nicht, wenn Sie ein sequentielles Array von assoziativen Zeilen haben.
Lucbonnin

5
function array_is_assoc(array $a) {
    $i = 0;
    foreach ($a as $k => $v) {
        if ($k !== $i++) {
            return true;
        }
    }
    return false;
}

Schnell, präzise und speichereffizient. Keine teuren Vergleiche, Funktionsaufrufe oder Array-Kopien.


4

Mit der xarray PHP-Erweiterung

Sie können dies sehr schnell tun (ungefähr 30+ mal schneller in PHP 5.6):

if (array_is_indexed($array)) {  }

Oder:

if (array_is_assoc($array)) {  }

3

Ich weiß, dass es ein bisschen sinnlos ist, eine Antwort auf diese große Warteschlange hinzuzufügen, aber hier ist eine lesbare O (n) -Lösung, bei der keine Werte dupliziert werden müssen:

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

Anstatt die Schlüssel zu überprüfen, um festzustellen, ob sie alle numerisch sind, durchlaufen Sie die Schlüssel, die für ein numerisches Array vorhanden wären , und stellen sicher, dass sie vorhanden sind.


noch ein Punkt. Array in Form [1,2,null,4]wird fehlschlagen, aber es ist korrektes Array. Also habe ich einige Verbesserungen unter stackoverflow.com/a/25206156/501831 mit Zusatzprüfung hinzugefügt array_key_exists)
Lazycommit

-1; isset()ist hier das falsche Werkzeug, da es false nullzurückgibt, wenn der Wert gesetzt ist , dies jedoch ist , wie von @lazycommit hervorgehoben.
Mark Amery

3

Meine Lösung:

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

array_mergeAuf einem einzelnen Array werden alle integerSchlüssel neu indiziert , andere jedoch nicht. Zum Beispiel:

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

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

Wenn also eine Liste (ein nicht assoziatives Array) erstellt wird, ['a', 'b', 'c']wird ein Wert entfernt und unset($a[1])dann array_mergeaufgerufen. Die Liste wird ab 0 neu indiziert.


-1; Dies wird O(n)in zusätzlichem Speicher verwendet (da mehrere neue Arrays mit so vielen Elementen wie erstellt wurden $array). In der Antwort wird weder die Mehrdeutigkeit der gestellten Frage angesprochen noch genau erklärt, wie eine Liste / ein nicht assoziatives Array definiert wird, und sogar Wenn keiner dieser Punkte zutrifft, ist es unklar, dass dies im Vergleich zu anderen bereits veröffentlichten Antworten einen Mehrwert bringt.
Mark Amery

3

Nach einigem lokalen Benchmarking, Debugging, Compiler-Testen, Profiling und Missbrauch von 3v4l.org, um über mehrere Versionen hinweg Benchmarks zu erstellen (ja, ich habe eine Warnung zum Stoppen erhalten) und mit jeder Variation zu vergleichen, die ich finden konnte ...

Ich gebe Ihnen eine organisch abgeleitete assoziative Array-Testfunktion für das Best-Average-Worst-Case-Szenario , die im schlimmsten Fall ungefähr so ​​gut oder besser ist als alle anderen Average-Case-Szenarien.

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

Von 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

Hier ist die Methode, die ich benutze:

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

Beachten Sie, dass dies keine Sonderfälle wie Folgendes berücksichtigt:

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

unset($a[1]);

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

Entschuldigung, ich kann dir dabei nicht helfen. Es ist auch für Arrays mit anständiger Größe etwas performant, da es keine unnötigen Kopien erstellt. Es sind diese kleinen Dinge, die Python und Ruby so viel schöner machen, in ...: P.


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

?>

Beide Beispiele, die die meisten Punkte erzielt haben, funktionieren mit Arrays wie nicht richtig $array = array('foo' => 'bar', 1)


+1 Ihre is_list () ist IMO die beste Antwort. Einige Leute haben keine Ahnung von Zeit- und Raumkomplexität und nativen vs PHP-Skriptfunktionen ...
e2-e4

2

Dies würde auch funktionieren ( Demo ):

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

Bitte beachten Sie, dass der Hauptpunkt dieser Antwort darin besteht, Sie über die Existenz von zu informieren SplFixedArrayund Sie nicht zu ermutigen, Ausnahmen für diese Art von Tests zu verwenden.


2

Ich denke, die Definition eines skalaren Arrays variiert je nach Anwendung. Das heißt, einige Anwendungen erfordern ein strengeres Verständnis dessen, was als skalares Array qualifiziert ist, und einige Anwendungen erfordern ein lockereres Verständnis.

Im Folgenden stelle ich 3 Methoden unterschiedlicher Strenge vor.

<?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

Noch eine schnelle von der Quelle . Passen Sie die Codierung von json_encode(und bson_encode) an. Dies gilt auch für die Konformität mit Javascript-Arrays.

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
Warum issetund array_key_exists? würde letzteres nicht ausreichen?
McFedr

@mcfedr ja, das würde es - die isset()Prüfung hier ist völlig überflüssig.
Mark Amery

@mcfedr, @ mark-amery aus Leistungsgründen. isset()ist schneller als array_key_exists(). siehe ilia.ws/archives/…
Lazycommit

@lazycommit Es wird von Ihrem Array abhängen, ob es besser mit oder ohne ist, nicht, dass es wahrscheinlich ein Array mit vielen nulls hat, aber es ist auch nicht so wahrscheinlich, dass Sie ein Array haben, das groß genug ist, dass es spürbare Leistungsunterschiede gibt mit beiden Schecks
mcfedr

2
Wenn Sie überprüfen müssen, ob es passt json_encode, können Sie einfach das erste Symbol der Zeichenfolge überprüfen, das von json_encode($your_arr)- ob es ist [oder {;-)
Pilat

2

Könnte dies die Lösung sein?

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

Die Einschränkung ist offensichtlich, dass der Array-Cursor zurückgesetzt wird, aber ich würde sagen, dass die Funktion wahrscheinlich verwendet wird, bevor das Array überhaupt durchlaufen oder verwendet wird.


Diese Funktion gibt für beide false zurückarray("a", "b") und array("a", "b" => "B")überprüft nur den ersten Schlüssel. Übrigens is_longist nur ein Alias ​​vonis_int .
Pang

1
Ehrlich gesagt denke ich, dass dies in den allermeisten Fällen sehr effektiv wäre und weitaus effizienter als die Alternativen. Wenn Sie die Konsequenzen dieser Methode verstehen und feststellen, dass sie für Sie funktioniert, ist sie wahrscheinlich die beste Wahl.
Gershom

Das ist einfach falsch; es schaut nur auf den ersten Schlüssel.
Mark Amery

@MarkAmery stellte die Frage, wie rein sequentielle Arrays von rein assoziativen Arrays unterschieden werden können. Diese Antwort macht genau das und ist die effizienteste von allen. Ein undefiniertes Verhalten für gemischte Arrays ist im Kontext der Frage vollkommen in Ordnung. +1
Tobia

@Tobia Ich glaube nicht, dass die meisten Leute damit einverstanden sind, dass Sie [7 => 'foo', 2 => 'bar']beispielsweise als "gemischtes" Array klassifizieren, das teilweise, aber nicht "rein" sequentiell ist. Das scheint mir eine eindeutig falsche Verwendung von Wörtern zu sein.
Mark Amery

2

Viele der hier aufgeführten Lösungen sind elegant und hübsch, lassen sich jedoch nicht gut skalieren und sind speicher- oder CPU-intensiv. Die meisten erstellen mit dieser Lösung von beiden Seiten des Vergleichs zwei neue Datenpunkte im Speicher. Je größer das Array, desto schwieriger und länger werden der Prozess und der Speicher verwendet, und Sie verlieren den Vorteil einer Kurzschlussbewertung. Ich habe einige Tests mit verschiedenen Ideen durchgeführt. Es wird versucht, zu vermeiden, dass array_key_exists teuer ist, und es wird auch vermieden, neue große Datensätze zum Vergleichen zu erstellen. Ich denke, dies ist eine einfache Möglichkeit, um festzustellen, ob ein Array sequentiell ist.

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

Sie führen eine einzelne Zählung für das Hauptarray aus und speichern eine einzelne Ganzzahl. Anschließend durchlaufen Sie das Array und prüfen, ob eine genaue Übereinstimmung vorliegt, während Sie den Zähler durchlaufen. Sie sollten von 1 zu zählen haben. Wenn es fehlschlägt, wird es kurzgeschlossen, was zu einer Leistungssteigerung führt, wenn es falsch ist.

Ursprünglich habe ich dies mit einer for-Schleife gemacht und nach isset gesucht ($ arr [$ i]), aber dies erkennt keine Nullschlüssel, für die array_key_exists erforderlich ist, und wie wir wissen, ist dies die schlechteste Funktion für die Geschwindigkeit.

Durch die ständige Aktualisierung von Variablen über foreach, um zu überprüfen, ob der Iterator nie über seine ganzzahlige Größe hinaus wächst, kann PHP die integrierte Speicheroptimierung, das Caching und die Speicherbereinigung verwenden, um den Ressourcenverbrauch sehr gering zu halten.

Außerdem werde ich argumentieren, dass die Verwendung von array_keys in einem foreach dumm ist, wenn Sie einfach $ key => $ value ausführen und den Schlüssel überprüfen können. Warum den neuen Datenpunkt erstellen? Sobald Sie die Array-Schlüssel entfernt haben, haben Sie sofort mehr Speicher verbraucht.


1

Antworten sind bereits gegeben, aber es gibt zu viele Desinformationen über die Leistung. Ich habe dieses kleine Benchmark-Skript geschrieben, das zeigt, dass die foreach-Methode die schnellste ist.

Haftungsausschluss: Die folgenden Methoden wurden aus den anderen Antworten kopiert

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

Ergebnisse:

==== 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

Oder Sie können einfach Folgendes verwenden:

Arr::isAssoc($array)

Hiermit wird überprüft, ob das Array einen nicht numerischen Schlüssel enthält oder:

Arr:isAssoc($array, true)

um zu überprüfen, ob das Array streng sequenziell ist (enthält automatisch generierte int-Schlüssel 0 bis n-1 )

mit dieser Bibliothek.


0

Wenn PHP nicht über eine integrierte Funktion verfügt, können Sie dies nicht in weniger als O (n) tun - indem Sie alle Schlüssel auflisten und nach dem Integer-Typ suchen. In der Tat möchten Sie auch sicherstellen, dass keine Löcher vorhanden sind, sodass Ihr Algorithmus möglicherweise folgendermaßen aussieht:

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

Aber warum sich die Mühe machen? Nehmen Sie einfach an, dass das Array vom erwarteten Typ ist. Wenn nicht, wird es nur in Ihrem Gesicht explodieren - das ist dynamische Programmierung für Sie! Testen Sie Ihren Code und alles wird gut ...


1
Normalerweise ist es der richtige Weg, nur anzunehmen, dass das Array der gewünschte Typ ist. In meinem Fall durchlaufe ich jedoch ein mehrdimensionales Array und formatiere die Ausgabe abhängig davon, welcher Array-Typ ein bestimmter Knoten ist.
Wilco

0

Ich vergleiche den Unterschied zwischen den Schlüsseln des Arrays und den Schlüsseln des Ergebnisses von array_values ​​() des Arrays, das immer ein Array mit ganzzahligen Indizes sein wird. Wenn die Schlüssel identisch sind, handelt es sich nicht um ein assoziatives Array.

function isHash($array) {
    if (!is_array($array)) return false;
    $diff = array_diff_assoc($array, array_values($array));
    return (empty($diff)) ? false : true;
}

-1; diese verwendet O(n)zusätzlichen Speicher , wenn $arrayhat nElemente, und das Schreiben (someboolean) ? false : truestatt !somebooleanist schrecklich und es wirklich ausführlich.
Mark Amery
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.