PHP: extracción de una propiedad de una matriz de objetos

128

Tengo una gran variedad de objetos de gatos:

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

y quiero extraer una matriz de ID de gatos en 1 línea (no es una función ni un bucle).

Estaba pensando en usar array_walkcon create_functionpero no sé cómo hacerlo.

¿Alguna idea?

abernier
fuente
No quiero usar un bucle porque quiero poder asignar el resultado en 1 línea: $ cats_id = myfunction ($ cats); / * debería devolver Array (15, 18, 23) * /
antes del

Respuestas:

149

Si tiene PHP 5.5 o posterior , la mejor manera es usar la función integrada array_column():

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

Pero el hijo tiene que ser una matriz o convertirse en una matriz

Josep Alsina
fuente
12
Esta es realmente la mejor respuesta
Dmitry Buslaev
1
La mejor solución, pero solo disponible para php 5.5 y versiones posteriores
Ludo - Fuera del récord el
2
nadie debería estar codificando más con 5.5 de todos modos. Versión del lenguaje de programación de 3.5 años ...
Toskan
11
Esta solución no responde a la pregunta, porque, array_columnno funciona con una arrayde objects en absoluto. Como PHP 7.0.0 es posible: stackoverflow.com/a/23335938/655224
algoritmo
1
Funciona solo cuando la propiedad del objeto tiene acceso público.
Karol Gasienica
154

La advertencia create_function() ha sido DEPRECADA a partir de PHP 7.2.0. Confiar en esta función es altamente desaconsejado.

Puedes usar la array_map()función.
Esto debería hacerlo:

$catIds = array_map(create_function('$o', 'return $o->id;'), $objects);
Greg
fuente
54
esta es la solución correcta y conducirá al hecho de que cada próximo mantenedor será "wtf" 'd: D
Andreas Klinger el
44
Lo único que no me gusta de esta solución es el uso de create_function.
Ionuț G. Stan
44
Puede usar una función lambda, pero no muchas personas ejecutarán PHP 5.3
Greg
26
Aquí vamos: $project_names = array_map(function($project) { return $project->name ;}, $projects );Sin embargo, se señala en esta publicación de blog que podría ser 2.5 veces más lento / memoria intensiva de esta manera.
Relequestual
77
Descubrí que cada vez que la función se crea con create_functionla memoria está creciendo. Si escribe un programa con bucles infinitos y llama al array_mapcon create_functionen él, siempre recibirá un Out of memory...error en algún momento. Así que no use create_functiony usearray_map(function($o) { return $o->id; }, $objects);
algoritmo
87

La solución depende de la versión de PHP que esté utilizando. Al menos hay 2 soluciones:

Primero (versiones PHP más recientes)

Como @JosepAlsina dijo antes, la mejor y más corta solución es usar array_columnlo siguiente:

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

Aviso: Para iterar un es que arraycontiene \stdClasscomo se usa en la pregunta, solo es posible con versiones de PHP >= 7.0. Pero cuando usas un s que arraycontiene arraypuedes hacer lo mismo desde PHP >= 5.5.

Segundo (versiones anteriores de PHP)

@ Greg dijo en versiones anteriores de PHP que es posible hacer lo siguiente:

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

Pero cuidado: en las versiones más recientes de PHP >= 5.3.0es mejor usar Closures, como sigue:

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


La diferencia

La primera solución crea una nueva función y la pone en su RAM. El recolector de basura no elimina de la memoria la instancia de función ya creada y llamada por alguna razón. Y eso, independientemente del hecho, que la instancia de función creada nunca se puede volver a llamar, porque no tenemos un puntero para ella. Y la próxima vez que se invoque este código, se volverá a crear la misma función. Este comportamiento llena lentamente tu memoria ...

Ambos ejemplos con salida de memoria para compararlos:

MALO

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

BUENO

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

    echo memory_get_usage() . "\n";

    sleep(1);
}

// the output
4235136
4235168
4235168
4235168
...


Esto también se puede discutir aquí

¡¿Pérdida de memoria?! ¿Garbage Collector funciona correctamente cuando usa 'create_function' dentro de 'array_map'?

algoritmo
fuente
aunque estoy usando php 7.2 pero parece que la solución no funcionará si está usando objetos de clase de espacio de nombres y devuelve una matriz en blanco.
Muhammad Omer Aslam
¿Puedes publicar un exploit? ¿Un pequeño fragmento para comprender mejor tu contexto?
algoritmo el
por ejemplo, vea este fragmento donde tengo una lista de objetos modelo devueltos por el SDK de la API de Dropbox y cuando intentamos usar array_columnen esta matriz siempre vuelve en blanco, mientras que ESTE fragmento funciona correctamente, ambos tienen la propiedad protegida que es accesible en el segundo fragmento solamente. No pude encontrar otra razón que el espacio de nombres.
Muhammad Omer Aslam
Cuando escribo objecten mi solución me refiero a objectno un Object. Las minúsculas objectdescriben el objeto simple \stdClass. Tal vez ese sea el problema. ¿ ObjectTienes un toArraymétodo? Utilizar este. Un simple objecty un arrayes casi lo mismo. Siguiendo invariante debe ser válido: ((object)(array)$o) === $o. Al implementar el __getmétodo, hará que sus propiedades de clase sean accesibles para todos. Este puede ser el comportamiento esperado. Pero también puede iterar sobre su array<Object>eg con array_mapy llamar toArray(si existe) y luego también funciona
algoritmo el
el segundo fragmento que proporcioné es de php, net y usa el mismo objeto de clase que en el primer fragmento, tal vez tengas razón, tal vez sea otra cosa que intentaré convirtiéndolo en una matriz gracias.
Muhammad Omer Aslam
4
function extract_ids($cats){
    $res = array();
    foreach($cats as $k=>$v) {
        $res[]= $v->id;
    }
    return $res
}

y úsalo en una línea :

$ids = extract_ids($cats);
SilentGhost
fuente
1
Gracias SilentGhost, pero mi pregunta es acerca de conseguir $ res sólo en 1 de comando, sin utilizar un bucle ...
abernier
2
¿Qué diferencia hace? Esto es lo más sencillo posible. Probablemente usará más líneas usando algo como array_walk.
deceze
@deceze, una solución elegante, simple y concisa simplemente se ve mejor, especialmente para algo tan explicable como esta operación (por ejemplo, esta no es una lógica personalizada que merece mucho espacio y comentarios). Para mí, eso hace una gran diferencia.
Charlie Dalsass
3

CÓDIGO

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

?>

SALIDA

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

Sé que está usando un bucle, ¡pero es la forma más sencilla de hacerlo! Y usando una función todavía termina en una sola línea.

Luke Antins
fuente
No entiendo por qué las personas no solo optan por soluciones simples como esta. Parece lógico hacerlo de esta manera si me preguntas.
Tom Dyer
3

La advertencia create_function() ha sido DEPRECADA a partir de PHP 7.2.0. Confiar en esta función es altamente desaconsejado.

Los bucles incorporados en PHP son más rápidos que los bucles interpretados, por lo que en realidad tiene sentido hacer que este sea una línea:

$result = array();
array_walk($cats, create_function('$value, $key, &$result', '$result[] = $value->id;'), $result)
soulmerge
fuente
1
// $array that contain records and id is what we want to fetch a
$ids = array_column($array, 'id');
Rai Ahmad Fraz
fuente
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);

Salida : [1, 2]

daHormez
fuente
0

La create_function()función está en desuso a partir de php v7.2.0 . Puede usar el array_map()como se indica,

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

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

Alternativamente, puede usar la array_column()función que devuelve los valores de una sola columna de la entrada, identificada por column_key. Opcionalmente, se puede proporcionar una index_key para indexar los valores en la matriz devuelta por los valores de la columna index_key de la matriz de entrada. Puede usar el array_column como se indica,

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