PHP: estrazione di una proprietà da un array di oggetti


128

Ho una serie di oggetti gatti:

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

e voglio estrarre un array di ID di gatti in 1 riga (non una funzione né un ciclo).

Stavo pensando di usare array_walkcon create_functionma non so come farlo.

Qualche idea?


Non voglio usare un ciclo perché voglio essere in grado di assegnare il risultato in 1 riga: $ cats_id = myfunction ($ cats); / * dovrebbe restituire Array (15, 18, 23) * /
abernier

Risposte:


150

Se hai PHP 5.5 o successivo , il modo migliore è usare la funzione incorporata array_column():

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

Ma il figlio deve essere un array o convertito in un array


12
Questa è in realtà la risposta migliore
Dmitry Buslaev

1
Soluzione migliore, ma disponibile solo per php 5.5 e versioni successive
Ludo - Off the record

2
nessuno dovrebbe più codificare con 5.5 comunque. Versione del linguaggio di programmazione di 3,5 anni ...
Toskan

11
Questa soluzione non risponde alla domanda perché, array_columnnon funziona affatto con un arrayof objects. Poiché è possibile PHP 7.0.0: stackoverflow.com/a/23335938/655224
algoritmo

1
Funziona solo quando la proprietà dell'oggetto ha accesso pubblico.
Karol Gasienica

154

L'avviso create_function() è stato DEPRECATO a partire da PHP 7.2.0. Affidarsi a questa funzione è altamente sconsigliato.

Puoi usare la array_map()funzione.
Questo dovrebbe farlo:

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

54
questa è la soluzione corretta e porterà al fatto che ogni imminente manutentore sarà "wtf" 'd: D
Andreas Klinger

4
L'unica cosa che non mi piace di questa soluzione è l'uso di create_function.
Ionuț G. Stan

4
Potresti usare una funzione lambda ma non molte persone eseguiranno PHP 5.3
Greg

26
Qui andiamo: $project_names = array_map(function($project) { return $project->name ;}, $projects );Tuttavia, in questo post del blog si nota che potrebbe essere 2,5 volte più lento / ad alta intensità di memoria in questo modo.
Relequestual

7
Ho scoperto che ogni volta che la funzione viene creata con create_functionla memoria cresce. Se scrivi un programma con cicli infiniti e chiami il array_mapcon create_functionin esso, a volte riceverai sempre un Out of memory...errore. Quindi non utilizzare create_functione utilizzarearray_map(function($o) { return $o->id; }, $objects);
algoritmo

87

La soluzione dipende dalla versione PHP che stai utilizzando. Almeno ci sono 2 soluzioni:

Primo (versioni PHP più recenti)

Come ha detto @JosepAlsina prima, la soluzione migliore e anche la più breve è quella di utilizzare array_columncome segue:

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

Avviso: per iterare un arraycontenitore \stdClasscome usato nella domanda è possibile solo con le versioni PHP >= 7.0. Ma quando usi un arraycontenitore arrays puoi fare lo stesso da PHP >= 5.5.

Secondo (versioni PHP precedenti)

@ Greg ha detto che nelle versioni precedenti di PHP è possibile fare quanto segue:

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

Ma attenzione: nelle versioni PHP più recenti >= 5.3.0è meglio usare Closures, come segue:

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


La differenza

La prima soluzione crea una nuova funzione e la inserisce nella RAM. Il garbage collector non elimina l'istanza di funzione già creata e già chiamata fuori dalla memoria per qualche motivo. E questo indipendentemente dal fatto che l'istanza della funzione creata non possa mai essere chiamata di nuovo, perché non abbiamo alcun puntatore per essa. E la prossima volta che viene chiamato questo codice, la stessa funzione verrà creata di nuovo. Questo comportamento riempie lentamente la tua memoria ...

Entrambi gli esempi con output in memoria per confrontarli:

MALE

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

BENE

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

    echo memory_get_usage() . "\n";

    sleep(1);
}

// the output
4235136
4235168
4235168
4235168
...


Questo può anche essere discusso qui

Perdita di memoria?! Garbage Collector funziona correttamente quando si utilizza "create_function" all'interno di "array_map"?


anche se sto usando php 7.2 ma sembra che la soluzione non funzionerà se stai usando oggetti di classe dello spazio dei nomi e restituisce un array vuoto.
Muhammad Omer Aslam

Puoi pubblicare un exploit? Un breve snippet per capire meglio il tuo contesto?
algoritmo

ad esempio, vedi questo frammento in cui ho un elenco di oggetti modello restituiti dall'SDK API di Dropbox e quando proviamo a usarlo array_columnsu questo array restituisce sempre vuoto mentre QUESTO frammento funziona correttamente, entrambi hanno la proprietà protetta che è accessibile nel secondo frammento solo. Non ho trovato nessun altro motivo oltre allo spazio dei nomi.
Muhammad Omer Aslam

Quando scrivo objectnella mia soluzione intendo objectnon un file Object. Il minuscolo objectdescrive l'oggetto semplice \stdClass. Forse è questo il problema. La vostra Objecthanno un toArraymetodo? Usa questo. Un semplice objecte un arrayè quasi la stessa cosa. A seguito di invariante deve essere valido: ((object)(array)$o) === $o. Quando si implementa il __getmetodo, le proprietà della classe saranno accessibili a tutti. Questo può essere il comportamento previsto. Ma puoi anche iterare sul tuo array<Object>eg con array_mape chiamare toArray(se esiste) e poi funziona anche IT
algoritmo

il secondo frammento che ho fornito è da php, net e utilizza lo stesso oggetto Class del primo frammento, forse hai ragione, forse è qualcos'altro che proverò convertendolo in un array grazie.
Muhammad Omer Aslam il

4
function extract_ids($cats){
    $res = array();
    foreach($cats as $k=>$v) {
        $res[]= $v->id;
    }
    return $res
}

e usalo in una riga :

$ids = extract_ids($cats);

1
Grazie SilentGhost, ma la mia domanda sta per ottenere $ res in un solo comando, non utilizzando un ciclo ...
abernier

2
Che differenza fa? Questo è il più semplice possibile. Probabilmente userete più righe usando qualcosa di simile array_walk.
inganno

@deceze, una soluzione elegante, semplice e concisa sembra semplicemente migliore, specialmente per qualcosa di così spiegabile come questa operazione (ad esempio, questa non è una logica personalizzata che merita molto spazio e commenti). Per me questo fa una grande differenza.
Charlie Dalsass

3

CODICE

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

?>

PRODUZIONE

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

So che sta usando un loop, ma è il modo più semplice per farlo! E usando una funzione finisce comunque su una singola riga.


Non capisco perché le persone non optano solo per soluzioni semplici come questa. Sembra logico farlo in questo modo se me lo chiedi.
Tom Dyer


3

L'avviso create_function() è stato DEPRECATO a partire da PHP 7.2.0. Affidarsi a questa funzione è altamente sconsigliato.

I loop incorporati in PHP sono più veloci dei loop interpretati, quindi ha davvero senso renderlo un one-liner:

$result = array();
array_walk($cats, create_function('$value, $key, &$result', '$result[] = $value->id;'), $result)

1
// $array that contain records and id is what we want to fetch a
$ids = array_column($array, 'id');

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

Uscita : [1, 2]


0

La create_function()funzione è deprecata a partire da php v7.2.0 . Puoi usare array_map()come dato,

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

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

In alternativa, è possibile utilizzare la array_column()funzione che restituisce i valori da una singola colonna dell'input, identificata da column_key. Facoltativamente, può essere fornita una chiave_indice per indicizzare i valori nell'array restituito dai valori dalla colonna index_key dell'array di input. Puoi usare array_column come indicato,

$IDs = array_column($array_of_object , 'id');
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.