Per Index auf einen assoziativen Array zugreifen
Hä? Was ist das denn für eine Überschrift? Das geht doch garnicht! Richtig, normalerweise geht das nicht, aber mit etwas tricksen schon.
Wir haben das folgende Array und möchten den 4. Wert haben, ohne aber den Key zu kennen. Bei einem 0-basierten Array wäre es also das Element mit dem Index 3:
<?php $array = array( '10.07.2011' => 14, '11.07.2011' => 51, '12.07.2011' => 46, '13.07.2011' => 9, '14.07.2011' => 20, '15.07.2011' => 37, ); echo $array[3];
Wie zu erwarten erhalten wir eine Notice und keinen Wert:
Notice: Undefined offset: 3 in test.php on line 12
Doch wie kommen wir nun an das 4. Array-Element? Ich habe 3 Möglichkeiten für euch, zuerst die unschönen:
$i=0; foreach ($array as $a) { if ($i == 3) { echo $a; break; } $i++; }
Bei einem großen Array ist diese Methode natürlich sehr langsam. Vergleichbar schlecht der folgende Code:
for ($i=0; $i<3; $i++) { next($array); } echo current($array);
Besser ist die folgende Lösung:
echo current(array_slice($array, 3, 1));
Wer kennt noch eine?
Edit:
Zwei weitere Lösungen aus den Kommentaren, wahrscheinlich irgendwo in der Mitte anzusiedeln was die Geschwindigkeit bei sehr großen Arrays angeht:
$values = array_values($array); echo $values[3];
Mit PHP 5.4 geht es sogar noch kürzer ohne das „Zwischenarray“:
echo array_values($array)[3];
Oder aber auch:
$keys = array_keys($array); echo $array[$keys[3]];
PHP 5.4:
echo $array[array_keys($array)[3]];
Moin Michael,
für einzelne Werte macht array_slice() den meisten Sinn. Willst du mehrere Werte haben, kannst du das assoziative Array auch in ein numerisches überführen: php.net/array_values – dann kannst du wieder mit $array[123] darauf zugreifen.
Rodney Rehm
13 Jul 11 at 09:00
Was ist einfach mit dem hier?
$keys = array_keys($array);
echo $array[$keys[3]];
KingCrunch
13 Jul 11 at 09:11
Habe eure beiden Lösungen oben eingetragen.
Michael Kliewe
13 Jul 11 at 09:17
Das ist ungeheuerlich, nur Gangsta würden so etwas tun! Oh, Moment… 😉
Ich weiß zwar keinen Anwendungsfall, aber nette Idee.
Timo Reitz
13 Jul 11 at 09:35
Schöne Lösungen, aber wo ist der Anwendungsfall? Habe mal darüber nachgedacht und keinen gefunden 🙂
Nils
13 Jul 11 at 10:14
Noch eine Lösung für OO- und Java-Fanboys 😉
$array = new ArrayIterator($array);
$array->seek(3);
echo $array->current();
Ich hatte mal einen Anwendungsfall für dieses Problem. Hatte da die array_values() Lösung benutzt.
Florian Heinze
13 Jul 11 at 13:33
Ich ab es mal fix verglichen:
8. Wert von 10
foreach/break: 4.0531158447266E-6
for/next: 4.0531158447266E-6
array_slice: 2.8610229492188E-6
89. Wert von 100
foreach/break: 1.1920928955078E-5
for/next: 2.3841857910156E-5
array_slice: 5.0067901611328E-6
8999. Wert von 100
foreach/break: 0.00072598457336426
for/next: 0.0022101402282715
array_slice: 4.0531158447266E-6
Ziemlich eindeutig für array_slice.
Oliver
13 Jul 11 at 13:50
@Florian Heinze:
Das ist gemein, von einem Anwendungsfall schreiben und den dann nicht erwähnen! 😛
Ich selbst trenne scharf zwischen indizierten Arrays und assoziativen Arrays – dass die in PHP durch die gleiche Datenstruktur realisiert werden, halte ich bis heute für kurios (und eine Monstrosität!).
Timo Reitz
13 Jul 11 at 13:54
Nur bevor sich mal wer Umstände macht – den ersten und den letzten Wert bekommt man auch über reset()/end() geliefert.
nk
13 Jul 11 at 13:55
@Oliver: Bei benchmarks mit derart kleinen Zahlen bin ich immer etwas skeptisch :X Hast auch die beiden zusätzlichen Varianten mit array_keys() und array_values() vergessen 😉
@Timo Reitz:
Indizierte und assoziative Arrays sind nur die Darstellungen. Genau genommen decken Arrays einiges mehr ab: arrays, maps, lists, stacks, queues, tupel, structs und mit Wohlwollen trees (und sicher hab ich noch einiges vergessen). Wenn man es noch strenger sieht, dann entspricht der Anwendungsfall genau den structs, denn die sind formal immer geordnet, also auch immer per Index ansprechbar 😉 Obs Sinn macht, oder nicht, sei mal dahing gestellt.
Ich muss bei diesem Thema die ganze Zeit an die `fetch_array()`-Methoden diverser Datenbanktreiber denken. Die liefern wahlweise auch eine Repräsentation, die sowohl numerische (Spaltennummer), als auch assoziative (Spaltenname) Schlüssel bereit stellt. Da wirkt es auch mich schon schlüssiger, wenn man auf rein-assoziative auch per Index zugreifen könnte.
Als Randbemerkung: Finds gut, dass hier auch gleich noch Werbung für PHP5.4 gemacht wird 🙂 PHP hats echt mal nötig sein Ruf als Dauer-Veraltet los zu werden
KingCrunch
13 Jul 11 at 14:42
array_slice ist die effektive Lösung. Alle anderen Lösungen können da nicht ansatzweise mithalten.
Die Funktion macht im Grunde dasselbe wie eine entsprechende for-Schleife in PHP. Nur macht array_slice das in C-Code, also im wahrsten Sinne des Wortes ein paar Takte schneller.
– http://svn.php.net/viewvc/php/php-src/tags/php_5_3_6/ext/standard/array.c?view=markup#l2143
Das bestätigt wieder die Regel, auf Core-Funktionen zu setzen, wo es nur geht.
Benchmark: http://pastie.org/2210713
Selbst die Lösung mit array_keys ist (auf meinem Rechner) unsignifikant schneller als diverse Schleifenansätze, verbraucht aber durch das Kopieren der Schlüssel wesentlich mehr Speicher.
Die next/current-Lösung ist die langsamste von allen. Das schiebe ich darauf, dass viele Funktionsaufrufe getätigt werden müssen.
Noch eine interessante Beobachtung zu Referenzen und Copy-On-Write-Verhalten:
Wird $array in der Funktionssignatur als Referenz ausgewiesen, verringert sich der Speicherverbauch bei den Varianten next/current und foreach, während er bei array_slice und array_keys anwächst.
Das liegt vermutlich daran, dass sowohl array_slice als auch array_keys einen Array-Zeiger-Reset durchführen (zend_hash_internal_pointer_reset_ex), was bei Arrays, die als Referenz vorliegen, nicht geduldet wird und deshalb Copy-On-Write auslöst.
Bei der foreach-Variante löst zudem…
foreach ($array as $key => $value) {
…kein Copy-On-Write von $array aus…
foreach ($array as $key => &$value) {
…dagegen schon.
mermshaus
14 Jul 11 at 06:48
PS: Das Kopieren bremst array_slice und array_keys dann wohl verständlicherweise ganz schön aus.
mermshaus
14 Jul 11 at 06:52
Es gibt noch weitere Lösungen,
$array = array(
‚10.07.2011‘ => 14,
‚11.07.2011‘ => 51,
‚12.07.2011‘ => 46,
‚13.07.2011‘ => 9,
‚14.07.2011‘ => 20,
‚15.07.2011‘ => 37,
);
$num = count($array);
$ar = array_fill(0, $num, 1);
$ar = array_keys($ar);
$ar = array_combine($ar, $array);
Wäre zum Beispiel noch eine, die im Prinzip aber genau dasselbe macht wie array_values, nur umständlicher.
Mit wäre jetzt auch kein Anwendungsfall bekannt, bei dem man wirklich per numerischem Index auf einen assoziativen Array zugreifen muss. Eigentlich ist das ja auch nicht der Sinn eines solchen Arrays, der eigentlich eine HashMap ist.
Patrick
14 Jul 11 at 15:27