PHP - Extrahieren einer Eigenschaft aus einem Array von Objekten

128

Ich habe eine Reihe von Katzenobjekten:

$cats = Array
    (
        [0] => stdClass Object
            (
                [id] => 15
            ),
        [1] => stdClass Object
            (
                [id] => 18
            ),
        [2] => stdClass Object
            (
                [id] => 23
            )
)

und ich möchte ein Array von Katzen-IDs in einer Zeile extrahieren (weder eine Funktion noch eine Schleife).

Ich habe darüber nachgedacht, array_walkmit zu verwenden, create_functionaber ich weiß nicht, wie ich es machen soll.

Irgendeine Idee?

abernier
quelle
Ich möchte keine Schleife verwenden, weil ich das Ergebnis in einer Zeile zuweisen möchte: $ cat_id = myfunction ($ cat); / * sollte Array (15, 18, 23) zurückgeben * /
abernier

Antworten:

149

Wenn Sie PHP 5.5 oder höher haben , verwenden Sie am besten die integrierte Funktion array_column():

$idCats = array_column($cats, 'id');

Aber der Sohn muss ein Array sein oder in ein Array konvertiert werden

Josep Alsina
quelle
12
Dies ist eigentlich die beste Antwort
Dmitry Buslaev
1
Beste Lösung, aber nur für PHP 5.5 und höher verfügbar
Ludo - Off the Record
2
niemand sollte mehr mit 5.5 codieren. 3,5 Jahre alte Programmiersprachenversion ...
Toskan
11
Diese Lösung beantwortet die Frage nicht, weil sie array_columnüberhaupt nicht mit einem arrayvon objects funktioniert . Da PHP 7.0.0 möglich ist: stackoverflow.com/a/23335938/655224
Algorithmus
1
Es funktioniert nur, wenn die Objekteigenschaft öffentlich zugänglich ist.
Karol Gasienica
154

Die Warnung create_function() wurde ab PHP 7.2.0 VERRINGERT. Es wird dringend davon abgeraten, sich auf diese Funktion zu verlassen.

Sie können die array_map()Funktion verwenden.
Dies sollte es tun:

$catIds = array_map(create_function('$o', 'return $o->id;'), $objects);
Greg
quelle
54
Dies ist die richtige Lösung und wird dazu führen, dass jeder kommende Betreuer "wtf" 'd: D
Andreas Klinger
4
Das einzige, was ich an dieser Lösung nicht mag, ist die Verwendung von create_function.
Ionuț G. Stan
4
Sie könnten eine Lambda-Funktion verwenden, aber nicht viele Leute werden PHP 5.3
Greg
26
Los geht's: $project_names = array_map(function($project) { return $project->name ;}, $projects );In diesem Blog-Beitrag wird jedoch darauf hingewiesen, dass es auf diese Weise 2,5-mal langsamer / speicherintensiv sein kann.
Relequestual
7
Ich fand heraus, dass jedes Mal, wenn die Funktion mit create_functiondem Speicher erstellt wird, erwachsen wird. Wenn Sie ein Programm mit Endlosschleifen schreiben und das array_mapwith create_functiondarin aufrufen , wird irgendwann immer eine Out of memory...Fehlermeldung angezeigt. Also nicht benutzen create_functionund benutzenarray_map(function($o) { return $o->id; }, $objects);
Algorithmus
87

Die Lösung hängt von der verwendeten PHP-Version ab. Zumindest gibt es 2 Lösungen:

Erstens (neuere PHP-Versionen)

Wie @JosepAlsina zuvor sagte, besteht die beste und auch kürzeste Lösung darin, array_columnFolgendes zu verwenden:

$catIds = array_column($objects, 'id');

Hinweis: Zum Iterieren eines in der Frage verwendeten arrayenthaltenden \stdClassES ist dies nur mit PHP-Versionen möglich >= 7.0. Aber wenn Sie ein arrayenthaltendes arrays verwenden, können Sie dasselbe seit PHP tun >= 5.5.

Zweitens (ältere PHP-Versionen)

@ Greg sagte, in älteren PHP-Versionen sei Folgendes möglich:

$catIds = array_map(create_function('$o', 'return $o->id;'), $objects);

Aber Vorsicht: In neueren PHP-Versionen ist >= 5.3.0es besser, Closures zu verwenden , wie folgt:

$catIds = array_map(function($o) { return $o->id; }, $objects);


Der Unterschied

Die erste Lösung erstellt eine neue Funktion und legt sie in Ihrem RAM ab. Der Garbage Collector löscht die bereits erstellte und bereits aufgerufene Funktionsinstanz aus irgendeinem Grund nicht aus dem Speicher. Und das unabhängig davon, dass die erstellte Funktionsinstanz nie wieder aufgerufen werden kann, weil wir keinen Zeiger dafür haben. Und wenn dieser Code das nächste Mal aufgerufen wird, wird dieselbe Funktion erneut erstellt. Dieses Verhalten füllt langsam Ihr Gedächtnis ...

Beide Beispiele mit Speicherausgabe zum Vergleich:

SCHLECHT

while (true)
{
    $objects = array_map(create_function('$o', 'return $o->id;'), $objects);

    echo memory_get_usage() . "\n";

    sleep(1);
}

// the output
4235616
4236600
4237560
4238520
...

GUT

while (true)
{
    $objects = array_map(function($o) { return $o->id; }, $objects);

    echo memory_get_usage() . "\n";

    sleep(1);
}

// the output
4235136
4235168
4235168
4235168
...


Dies kann auch hier diskutiert werden

Speicherleck?! Funktioniert Garbage Collector richtig, wenn 'create_function' in 'array_map' verwendet wird?

Algorithmus
quelle
Ich verwende zwar PHP 7.2, aber es scheint, dass die Lösung nicht funktioniert, wenn Sie Namespace-Klassenobjekte verwenden und ein leeres Array zurückgeben.
Muhammad Omer Aslam
Kannst du einen Exploit posten? Ein kurzer Ausschnitt, um Ihren Kontext besser zu verstehen?
Algorithmus
In diesem Snippet finden Sie beispielsweise eine Liste der vom Dropbox-API-SDK zurückgegebenen Modellobjekte. Wenn wir versuchen, array_columndieses Array zu verwenden, wird immer ein Leerzeichen zurückgegeben, während DIESES Snippet ordnungsgemäß funktioniert. Beide haben die geschützte Eigenschaft, auf die im zweiten Snippet zugegriffen werden kann nur. Ich konnte keinen anderen Grund als den Namespace finden.
Muhammad Omer Aslam
Wenn ich objectin meine Lösung schreibe, meine ich objectkeine Object. Das Kleinbuchstaben objectbeschreibt das einfache Objekt \stdClass. Vielleicht ist das das Problem. Hat du Objecteine toArrayMethode? Benutze das. Ein einfaches objectund ein arrayist fast das gleiche. Folgende Invariante muss gültig sein : ((object)(array)$o) === $o. Bei der Implementierung der __getMethode machen Sie Ihre Klasseneigenschaften für alle zugänglich. Dies kann das erwartete Verhalten sein. Sie können aber auch über Ihr array<Object>zB mit array_mapund Anruf toArray(falls vorhanden) iterieren und dann funktioniert auch IT
Algorithmus
Das zweite Snippet, das ich bereitgestellt habe, stammt von PHP, Net und verwendet dasselbe Klassenobjekt wie im ersten Snippet. Vielleicht haben Sie Recht. Vielleicht ist es etwas anderes, das ich versuchen werde, indem ich es in ein Array konvertiere. Danke.
Muhammad Omer Aslam
4
function extract_ids($cats){
    $res = array();
    foreach($cats as $k=>$v) {
        $res[]= $v->id;
    }
    return $res
}

und benutze es in einer Zeile :

$ids = extract_ids($cats);
SilentGhost
quelle
1
Danke SilentGhost, aber meine Frage ist im Begriff, $ res in nur 1 Befehl zu erhalten, ohne eine Schleife zu verwenden ...
abernier
2
Welchen Unterschied macht es? Dies ist so einfach wie es nur geht. Sie werden wahrscheinlich mehr Zeilen mit so etwas wie verwenden array_walk.
Täuschung
@deceze, eine elegante, einfache und knappe Lösung, sieht einfach besser aus - insbesondere für etwas, das so erklärbar ist wie diese Operation (z. B. ist dies keine benutzerdefinierte Logik, die viel Platz und Kommentare verdient). Für mich macht das einen großen Unterschied.
Charlie Dalsass
3

CODE

<?php

# setup test array.
$cats = array();
$cats[] = (object) array('id' => 15);
$cats[] = (object) array('id' => 18);
$cats[] = (object) array('id' => 23);

function extract_ids($array = array())
{
    $ids = array();
    foreach ($array as $object) {
        $ids[] = $object->id;
    }
    return $ids;
}

$cat_ids = extract_ids($cats);
var_dump($cats);
var_dump($cat_ids);

?>

AUSGABE

# var_dump($cats);
array(3) {
  [0]=>
  object(stdClass)#1 (1) {
    ["id"]=>
    int(15)
  }
  [1]=>
  object(stdClass)#2 (1) {
    ["id"]=>
    int(18)
  }
  [2]=>
  object(stdClass)#3 (1) {
    ["id"]=>
    int(23)
  }
}

# var_dump($cat_ids);
array(3) {
  [0]=>
  int(15)
  [1]=>
  int(18)
  [2]=>
  int(23)
}

Ich weiß, dass es eine Schleife verwendet, aber es ist der einfachste Weg, dies zu tun! Und mit einer Funktion endet es immer noch in einer einzelnen Zeile.

Luke Antins
quelle
Ich verstehe nicht, warum sich die Leute nicht einfach für solche einfachen Lösungen entscheiden. Scheint logisch, es so zu machen, wenn Sie mich fragen.
Tom Dyer
3

Die Warnung create_function() wurde ab PHP 7.2.0 VERRINGERT. Es wird dringend davon abgeraten, sich auf diese Funktion zu verlassen.

Eingebaute Schleifen in PHP sind schneller als interpretierte Schleifen, daher ist es tatsächlich sinnvoll, diese zu einem Einzeiler zu machen:

$result = array();
array_walk($cats, create_function('$value, $key, &$result', '$result[] = $value->id;'), $result)
Soulmerge
quelle
1
// $array that contain records and id is what we want to fetch a
$ids = array_column($array, 'id');
Rai Ahmad Fraz
quelle
0
    $object = new stdClass();
    $object->id = 1;

    $object2 = new stdClass();
    $object2->id = 2;

    $objects = [
        $object,
        $object2
    ];

    $ids = array_map(function ($object) {
        /** @var YourEntity $object */
        return $object->id;
        // Or even if you have public methods
        // return $object->getId()
    }, $objects);

Ausgabe : [1, 2]

daHormez
quelle
0

Die create_function()Funktion ist ab PHP v7.2.0 veraltet . Sie können das array_map()wie angegeben verwenden,

function getObjectID($obj){
    return $obj->id;
}

$IDs = array_map('getObjectID' , $array_of_object);

Alternativ können Sie eine array_column()Funktion verwenden, die die Werte aus einer einzelnen Spalte der Eingabe zurückgibt, die durch den Spaltenschlüssel identifiziert wird. Optional kann ein Indexschlüssel bereitgestellt werden, um die Werte im zurückgegebenen Array durch die Werte aus der Spalte index_key des Eingabearrays zu indizieren. Sie können die array_column wie angegeben verwenden:

$IDs = array_column($array_of_object , 'id');
Kiran Maniya
quelle