it-swarm.com.de

Wie zu prüfen, ob PHP Array ist assoziativ oder sequentiell?

PHP behandelt alle Arrays als assoziativ, daher gibt es keine integrierten Funktionen. 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 unterscheiden können:

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

und das:

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

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

  • Erstens, wie Sie feststellen können, ob ein Array nur numerische Schlüssel enthält
  • Zweitens, wie Sie feststellen können, ob ein Array über die numerischen Tasten sequential verfügt, beginnend mit 0

Überlegen Sie, welches dieser Verhalten Sie tatsächlich benötigen. (Es kann sein, dass beide für Ihre Zwecke geeignet sind.)

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

Für die zweite Frage (Überprüfung, ob das Array nullindexiert 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(array('a', 'b', 'c'))); // false
var_dump(isAssoc(array("0" => 'a', "1" => 'b', "2" => 'c'))); // false
var_dump(isAssoc(array("1" => 'a', "0" => 'b', "2" => 'c'))); // true
var_dump(isAssoc(array("a" => 'a', "b" => 'b', "c" => 'c'))); // true
538
Greg

So prüfen Sie lediglich, ob das Array über nicht ganzzahlige Schlüssel verfügt (nicht ob das Array sequenziell oder null indiziert ist):

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

Wenn mindestens ein String-Schlüssel vorhanden ist, wird $array als assoziatives Array betrachtet.

409
Captain kurO

Dies ist sicherlich eine bessere Alternative.

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

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

Ein Schlüssel kann entweder eine ganze Zahl oder eine Zeichenfolge sein. Wenn ein Schlüssel die Standarddarstellung einer ganzen Zahl ist, wird sie als solche interpretiert (d. H. "8" wird als 8 interpretiert, während "08" als "08" interpretiert wird). Floats im Schlüssel werden auf Integer abgeschnitten. Die indizierten und assoziativen Array-Typen sind in PHP vom gleichen Typ, die sowohl Ganzzahl- als auch Zeichenfolgenindizes enthalten können.

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

Wenn Sie ein Array auf nicht ganzzahlige Schlüssel prüfen möchten, ohne eine Kopie eines Teils des Arrays zu erstellen (wie array_keys () dies tut) oder alles (wie foreach):

function keyedNext( &$arr, &$k){
    $k = key($arr);
    return next($arr);
}

for ($k = key(reset($my_array)); is_int($k); keyedNext($my_array,$k))
    $onlyIntKeys = is_null($k);

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

73
squirrel

As 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 assoziativist. Also das erste zuerst: was ist ein Schlüssel in einem PHP -Array ?:

Die Taste kann entweder eine Ganzzahl oder eine Zeichenfolge sein.

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

  • Fall 1. Alle Tasten sind numerisch/Ganzzahlen.
  • Fall 2. Alle Schlüssel sind Strings.
  • Fall 3. Einige Tasten sind Strings, einige Tasten sind Numerisch/Ganzzahlen.

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

Fall 1: Alle Tasten sind numerisch/Ganzzahlen.

Note: Diese Funktion gibt true auch für leere Arrays 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 Strings.

Note: Diese Funktion gibt true auch für leere Arrays 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 Tasten sind Strings, einige Tasten sind Numerisch/Ganzzahlen.

Note: Diese Funktion gibt true auch für leere Arrays 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" Arrayist, an das wir alle gewöhnt sind, was bedeutet:

  • Seine Schlüssel sind alle numerisch/ganze Zahlen.
  • Seine Tasten sind sequentiell (d. H. Um Schritt 1 erhöht).
  • Seine Tasten beginnen bei Null.

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

Fall 3a. Die Tasten sind numerisch/Ganzzahlen, sequentiell und nullbasiert.

Note: Diese Funktion gibt true auch für leere Arrays 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ßregeln/Fallstricke (oder noch speziellere Fakten zu Array-Schlüsseln in PHP)

Ganzzahlige Tasten

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

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!

Ganzzahlige Tasten, die wie Zeichenfolgen aussehen

Wenn Sie denken, die Taste in array("13" => "b") ist eine Zeichenfolge, Sie liegen falsch. Aus dem doc hier :

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

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

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

Aber der Schlüssel für diese Arrays ist Strings:

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, nach dem doc ,

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

Also ist der Schlüssel für dieses Array kann oder kann nicht ​​eine Ganzzahl- es 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, dass PHP in der Regel fehlerhaft ​​ist, wenn die Ganzzahl in der Nähe der 2 liegt31 = 2.147.483.648 Grenze (siehe Fehler 514 , Fehler 52899 ). In meiner lokalen Umgebung (PHP 5.3.8 unter XAMPP 1.7.7 unter Windows 7) gibt var_dump(array("2147483647" => "b")) aus

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

aber auf diese Live-Demo auf Codepad (PHP 5.2.5) gibt der gleiche Ausdruck

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

Der Schlüssel ist also eine Ganzzahlin einer Umgebung, aber eine Zeichenfolgein einer anderen, obwohl 2147483647 ein gültiges Vorzeichen ist. bit integer.

38
Pang

Geschwindigkeit:

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

Gedächtnismäßig:

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

Der effizienteste Weg ist 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. sein werden) verglichen werden.

19
function checkAssoc($array){
    return  ctype_digit( implode('', array_keys($array) ) );
}
18
dsims

Ich habe sowohl array_keys($obj) !== range(0, count($obj) - 1) als auch array_values($arr) !== $arr verwendet (beide sind Duale, obwohl das zweite billiger ist als das erste), aber beide fallen bei sehr großen Arrays aus.

Dies liegt daran, dass array_keys und array_values beide sehr kostspielige Operationen sind (da sie ein ganz neues Array von Größe bilden, das ungefähr der des Originals entspricht).

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

Wenn Sie keine spärlichen Arrays von assoziativen Arrays unterscheiden möchten, können Sie einfach 'assoc' aus beiden if-Blöcken zurückgeben.

Obwohl dies auf dieser Seite weniger "elegant" erscheint als viele "Lösungen", ist es in der Praxis jedoch wesentlich 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, sie duplizieren sie auch.

17
podperson

Ich denke, die folgenden zwei Funktionen sind der beste Weg, um zu überprüfen, ob ein Array assoziativ oder numerisch ist. Da "numerisch" nur numerische Tasten oder nur fortlaufende numerische Tasten bedeuten kann, werden nachfolgend zwei Funktionen aufgeführt, die eine der beiden Bedingungen prü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 sequenziell 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 hinaus bewegt wird. Dies beendet die for-Schleife und gibt die Anweisung nach der for-Schleife zurück, wenn alle Schlüssel eine Ganzzahl sind. Wenn nicht, wird die Schleife vorzeitig beendet, weil ein Schlüssel vom Typ string ist und die Anweisung nach der for-Schleife false zurückgibt. Die letztere Funktion fügt außerdem nach jedem Vergleich eine $ base hinzu, um überprüfen zu können, ob der nächste Schlüssel den richtigen Wert hat. Beim strengen Vergleich wird auch geprüft, ob der Schlüssel vom Typ Ganzzahl 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 bin, habe ich es gelassen. Die Aussage wird sowieso nur einmal ausgeführt. Ich denke, das sind die effizientesten Lösungen:

  • Speichermäßig: Kein Kopieren von Daten oder Schlüsselbereichen. Das Ausführen von array_values ​​oder array_keys kann kürzer erscheinen (weniger Code). Denken Sie jedoch daran, was im Hintergrund passiert, wenn Sie diesen Aufruf tätigen. Ja, es gibt mehr (sichtbare) Aussagen als in anderen Lösungen, aber das zählt nicht, oder?
  • Zeitlich sinnvoll: Abgesehen davon, dass das Kopieren/Extrahieren von Daten und/oder Schlüsseln Zeit erfordert, ist diese Lösung effizienter als ein Foreach. Wieder scheint ein Foreach für einige effizienter zu sein, weil es in der Notation kürzer ist, aber im Hintergrund ruft Foreach auch reset, key und nebenher das Looping auf. Zusätzlich ruft es auch valide auf, um die Endebedingung zu prüfen, was hier durch die Kombination mit der Ganzzahlprüfung vermieden wird.

Denken Sie daran, dass ein Array-Schlüssel nur eine Ganzzahl oder eine Zeichenfolge sein kann. Eine streng numerische Zeichenfolge wie "1" (aber nicht "01") wird in eine Ganzzahl übersetzt. Das macht das Prüfen auf einen Integer-Schlüssel zur einzigen erforderlichen Operation neben dem Zählen, wenn das Array sequenziell sein soll. Wenn is_indexed_array den Wert false zurückgibt, kann das Array natürlich als assoziativ betrachtet werden. Ich sage "gesehen", weil sie eigentlich alle sind.

13
Niels Ockeloen

Ich habe zwei beliebte Ansätze für diese Frage bemerkt: einen mit array_values() und einen anderen 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 auf CentOS lautet wie folgt:

Zeitaufwand bei Methode # 1 = 10.745ms
Zeitaufwand bei Methode # 2 = 18.239ms

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

7
Manu M.

Diese Funktion kann Folgendes verarbeiten:

  • array mit Löchern im Index (z. B. 1,2,4,5,8,10) 
  • array mit "0x" -Tasten: z. Taste '08' ist assoziativ, während Taste '8' sequentiell ist.

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

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

Es gibt bereits viele Antworten, aber hier ist die Methode, auf die sich Laravel in der Arr-Klasse verlässt: 

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

6
Ben
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 Kopieren von Arrays.

5
Jesse

Eine Möglichkeit, dies zu erreichen, besteht darin, auf json_encode zurückzugreifen, das bereits über eine eigene interne Methode zur Unterscheidung zwischen einem assoziativen Array und einem indizierten Array verfügt, um den korrekten JSON-Code auszugeben.

Sie können dies tun, indem Sie prü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] === '{';
}
5
Loading

Verwenden Sie die Erweiterung xarray PHP

Sie können dies sehr schnell tun (ca. 30+ mal schneller in PHP 5.6): 

if (array_is_indexed($array)) {  }

Oder:

if (array_is_assoc($array)) {  }
4
c9s

Meine Lösung:

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

array_merge in einem einzelnen Array wird alle integer-Schlüssel neu indizieren, nicht jedoch andere. 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 unset($a[1]), dann wird array_merge aufgerufen, und die Liste wird ab 0 neu indiziert.

3
ByScripts

Hier ist die Methode, die ich verwende:

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 besonderen Fälle berücksichtigt, wie:

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

unset($a[1]);

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

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

2
AL the X

Ich weiß, dass es ein bisschen sinnlos ist, dieser riesigen Warteschlange eine Antwort hinzuzufügen, aber hier ist eine lesbare O(n) - Lösung, die keine Werte dupliziert:

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

Anstatt die Tasten zu überprüfen, um zu sehen, ob sie alle numerisch sind, iterieren Sie über die Tasten, die wäre für ein numerisches Array vorhanden wären, und stellen Sie sicher, dass sie vorhanden sind.

2
cloudfeet

Ich denke, die Definition eines skalaren Arrays variiert je nach Anwendung. Das heißt, einige Anwendungen erfordern ein strengeres Verständnis dessen, was als Skalararray geeignet ist, und einige Anwendungen erfordern ein lockereres Gefühl.

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 <[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;
}
2
David Farrell

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

Beachten Sie bitte, dass der Hauptpunkt dieser Antwort darin besteht, Sie über das Vorhandensein von SplFixedArray zu informieren und Sie nicht zu ermutigen, Ausnahmen für diese Art von Tests zu verwenden.

2
Gordon

Könnte das die Lösung sein?

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

Der Nachteil ist natürlich, dass der Array-Cursor zurückgesetzt wird, aber ich würde sagen, die Funktion wird wahrscheinlich verwendet, bevor das Array überhaupt durchlaufen wird.

2
Kat Lim Ruiz
<?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);
}

?>

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

2
KillEveryBody

Nach einigem lokalem Benchmarking, Debuggen, Prüfen des Compilers, Erstellen von Profilen und dem Missbrauch von 3v4l.org, um weitere Versionen zu testen (ja, ich habe eine Warnung erhalten, zu stoppen) und.

Ich gebe Ihnen eine organisch abgeleitete Best-Average-Worst-Case-Szenario Assoziative Array-Testfunktion, die bei Worst ungefähr so ​​gut wie oder besser ist als alle anderen Szenarien des Durchschnittsfalls.

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

Noch schneller von source . Passende Kodierung von json_encode (und bson_encode) So hat Javascript Array-Konformität.

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
lazycommit

antworten sind bereits gegeben, aber es gibt zu viele Desinformation ü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
nonsensei

Meiner Meinung nach sollte ein Array als assoziativ akzeptiert werden, wenn einer seiner Schlüssel nicht ganzzahlig ist, z. Float-Nummern und leere Zeichenfolge ''.

Auch nicht-sequenzierte Ganzzahlen müssen als assoziativ wie (0,2,4,6) betrachtet werden, da diese Art von Arrays auf diese Weise nicht für for-Schleifen verwendet werden kann:

$n =count($arr);
for($i=0,$i<$n;$i++) 

Der zweite Teil der Funktion prüft, ob die Schlüssel indiziert sind oder nicht. Sie funktioniert auch für Schlüssel mit negativen Werten. Zum Beispiel (-1,0,1,2,3,4,5)

count() = 7 , max = 5, min=-1



if( 7 == (5-(-1)+1 ) // true
    return false; // array not associative


/** 
 * isAssoc Checks if an array is associative
 * @param $arr reference to the array to be checked
 * @return bool 
 */     
function IsAssoc(&$arr){
    $keys= array_keys($arr);
    foreach($keys as $key){
        if (!is_integer($key))
            return true;
    }
    // if all keys are integer then check if they are indexed
    if(count($arr) == (max($keys)-min($keys)+1))
        return false;
    else
        return true;
}
0
Selim Acar

Viele der hier angebotenen Lösungen sind elegant und hübsch, lassen sich jedoch nicht gut skalieren und sind speicherintensiv oder rechenintensiv. Die meisten erstellen mit dieser Lösung auf beiden Seiten des Vergleichs zwei neue Datenpunkte im Speicher. Je größer das Array, desto härter und länger sind der verwendete Prozess und Speicher, und Sie verlieren den Vorteil der Kurzschlussbewertung. Ich habe einige Tests mit ein paar verschiedenen Ideen durchgeführt. Es wird versucht, array_key_exists zu vermeiden, da dies teuer ist, und es wird auch vermieden, neue große zu vergleichende Datensätze zu erstellen. Ich halte dies für eine einfache Methode, 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 überprüfen die genaue Übereinstimmung, während Sie den Zähler durchlaufen. Sie sollten von 1 haben, um zu zählen. 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 ($ arr [$ i]) gesucht, aber dies erkennt keine Nullschlüssel, für die array_key_exists erforderlich sind, und wie wir wissen, ist dies die schlechteste Funktion für die Geschwindigkeit.

Ständige Aktualisierung der Variablen über foreach, um zu überprüfen, ob der Iterator immer größer wird. Lassen Sie uns PHP die integrierte Speicheroptimierung, Zwischenspeicherung und Speicherbereinigung verwenden, um den Ressourcenverbrauch möglichst 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.

0
geilt

Ich habe mir die nächste Methode ausgedacht:

function isSequential(array $list): bool
{
    $i = 0;
    $count = count($list);
    while (array_key_exists($i, $list)) {
        $i += 1;
        if ($i === $count) {
            return true;
        }
    }

    return false;
}


var_dump(isSequential(array())); // false
var_dump(isSequential(array('a', 'b', 'c'))); // true
var_dump(isSequential(array("0" => 'a', "1" => 'b', "2" => 'c'))); // true
var_dump(isSequential(array("1" => 'a', "0" => 'b', "2" => 'c'))); // true
var_dump(isSequential(array("1a" => 'a', "0b" => 'b', "2c" => 'c'))); // false
var_dump(isSequential(array("a" => 'a', "b" => 'b', "c" => 'c'))); // false

* Beachten Sie, dass ein leeres Array nicht als sequenzielles Array betrachtet wird, aber ich denke, dass es in Ordnung ist, da leere Arrays wie 0 sind - egal, ob Plus oder Minus, es ist leer.

Hier sind die Vorteile dieser Methode im Vergleich zu einigen oben aufgeführten:

  • Es beinhaltet kein Kopieren von Arrays (jemand, der in diesem Gist erwähnt wird https://Gist.github.com/Thinkscape/1965669 dass array_values Kein Kopieren beinhaltet - was! ?? Auf jeden Fall - wie weiter unten zu sehen sein wird )
  • Es ist schneller für größere Arrays und gleichzeitig speicherfreundlicher

Ich habe Benchmark verwendet, freundlicherweise zur Verfügung gestellt von Artur Bodera , wobei ich eines der Arrays in 1M-Elemente (array_fill(0, 1000000, uniqid()), // big numeric array) geändert habe.

Hier sind die Ergebnisse für 100 Iterationen:

PHP 7.1.16 (cli) (built: Mar 31 2018 02:59:59) ( NTS )

Initial memory: 32.42 MB
Testing my_method (isset check) - 100 iterations
  Total time: 2.57942 s
  Total memory: 32.48 MB

Testing method3 (array_filter of keys) - 100 iterations
  Total time: 5.10964 s
  Total memory: 64.42 MB

Testing method1 (array_values check) - 100 iterations
  Total time: 3.07591 s
  Total memory: 64.42 MB

Testing method2 (array_keys comparison) - 100 iterations
  Total time: 5.62937 s
  Total memory: 96.43 MB

* Methoden werden basierend auf ihrem Speicherverbrauch sortiert

** Ich habe echo " Total memory: " . number_format(memory_get_peak_usage()/1024/1024, 2) . " MB\n"; verwendet, um die Speichernutzung anzuzeigen

0
Slayer Birden
function is_array_assoc($foo) {
    if (is_array($foo)) {
        return (count(array_filter(array_keys($foo), 'is_string')) > 0);
    }
    return false;
}
0
macki

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 gleich 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;
}
0
philroy

Wenn PHP dafür keine integrierte Funktion hat, können Sie dies nicht in weniger als O(n) tun - es werden alle Schlüssel aufgelistet und der Integer-Typ geprüft. Tatsächlich möchten Sie auch sicherstellen, dass keine Löcher vorhanden sind. Ihr Algorithmus könnte also folgendermaßen aussehen:

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 die Mühe machen? Nehmen Sie einfach an, dass das Array dem Typ entspricht, den Sie erwarten. Wenn dies nicht der Fall ist, wird es einfach in Ihr Gesicht explodieren - das ist dynamische Programmierung für Sie! Testen Sie Ihren Code und alles wird gut ...

0
Daren Thomas
function is_associative($arr) {
  return (array_merge($arr) !== $arr || count(array_filter($arr, 'is_string', ARRAY_FILTER_USE_KEY)) > 0);
}
0
scronide

Noch eine andere Möglichkeit, dies zu tun.

function array_isassociative($array)
{
    // Create new Array,  Make it the same size as the input array
    $compareArray = array_pad(array(), count($array), 0);

    // Compare the two array_keys
    return (count(array_diff_key($array, $compareArray))) ? true : false;

}
0
Mez
/*
iszba - Is Zero Based Array

Detects if an array is zero based or not.

PARAMS:
    $chkvfnc
        Callback in the loop allows to check the values of each element.
        Signature:
            bool function chkvfnc($v);
            return:
                true    continue looping
                false   stop looping; iszba returns false too.

NOTES:
○ assert: $array is an array.
○ May be memory efficient;
  it doesn't get extra arrays via array_keys() or ranges() into the function.
○ Is pretty fast without a callback.
○ With callback it's ~2.4 times slower.
*/
function iszba($array, $chkvfnc=null){

    $ncb = !$chkvfnc;
    $i = 0;

    foreach($array as $k => $v){
        if($k === $i++)
            if($ncb || $chkvfnc($v))
                continue;

        return false;
    }

    return true;
}

• Ohne Rückruf ist es ~ 30% schneller als die derzeit führende Antwort.

• Negieren Sie einfach die Antwort, um zu wissen, ob das Array als assoziativ betrachtet werden soll.

0
rbgo

Oder Sie können dies einfach verwenden:

Arr::isAssoc($array)

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

Arr:isAssoc($array, true)

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

verwenden von this library.

0
Minwork

Änderung der beliebtesten Antwort.
Dies erfordert etwas mehr Verarbeitung, ist aber genauer. 

<?php
//$a is a subset of $b
function isSubset($a, $b)
{
    foreach($a =>$v)
        if(array_search($v, $b) === false)
            return false;

    return true;

    //less effecient, clearer implementation. (uses === for comparison)
    //return array_intersect($a, $b) === $a;
}

function isAssoc($arr)
{
    return !isSubset(array_keys($arr), range(0, count($arr) - 1));
}

var_dump(isAssoc(array('a', 'b', 'c'))); // false
var_dump(isAssoc(array(1 => 'a', 0 => 'b', 2 => 'c'))); // false
var_dump(isAssoc(array("0" => 'a', "1" => 'b', "2" => 'c'))); // false 
//(use === in isSubset to get 'true' for above statement)
var_dump(isAssoc(array("a" => 'a', "b" => 'b', "c" => 'c'))); // true
?>
0
Jason McCarrell