PHP - Extraire une propriété d'un tableau d'objets

128

J'ai un tableau d'objets chats:

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

et je veux extraire un tableau d'identifiants de chats en 1 ligne (pas une fonction ni une boucle).

Je pensais utiliser array_walkavec create_functionmais je ne sais pas comment faire.

Une idée?

abernier
la source
Je ne veux pas utiliser de boucle car je veux pouvoir attribuer le résultat en 1 ligne: $ cats_id = mafonction ($ cats); / * devrait retourner Array (15, 18, 23) * /
abernier

Réponses:

150

Si vous avez PHP 5.5 ou version ultérieure , le meilleur moyen est d'utiliser la fonction intégrée array_column():

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

Mais le fils doit être un tableau ou converti en un tableau

Josep Alsina
la source
12
C'est en fait la meilleure réponse
Dmitry Buslaev
1
Meilleure solution, mais uniquement disponible pour php 5.5 et plus
Ludo - Off the record
2
personne ne devrait plus coder avec 5.5 de toute façon. Version du langage de programmation de 3,5 ans ...
Toskan
11
Cette solution ne répond pas à la question car, array_columnne fonctionne pas du tout avec un arrayde objects. Depuis PHP 7.0.0 est possible: stackoverflow.com/a/23335938/655224
algorhythm
1
Cela ne fonctionne que lorsque la propriété de l'objet a un accès public.
Karol Gasienica
154

L'avertissement create_function() est obsolète depuis PHP 7.2.0. Il est fortement déconseillé de se fier à cette fonction.

Vous pouvez utiliser la array_map()fonction.
Cela devrait le faire:

$catIds = array_map(create_function('$o', 'return $o->id;'), $objects);
Greg
la source
54
c'est la bonne solution et mènera au fait que chaque prochain responsable sera "wtf" 'd: D
Andreas Klinger
4
La seule chose que je n'aime pas dans cette solution est l'utilisation de create_function.
Ionuț G. Stan
4
Vous pouvez utiliser une fonction lambda mais peu de gens exécuteront PHP 5.3
Greg
26
On y va: $project_names = array_map(function($project) { return $project->name ;}, $projects );Cependant, il est noté dans ce billet de blog que cela pourrait être 2,5 fois plus lent / gourmand en mémoire de cette façon.
Relequestual
7
J'ai découvert qu'à chaque fois que la fonction est créée avec create_functionla mémoire, elle grandit. Si vous écrivez un programme avec des boucles infinies et appelez le array_mapavec create_function, vous obtiendrez toujours une Out of memory...erreur parfois. Alors n'utilisez pas create_functionet n'utilisez pasarray_map(function($o) { return $o->id; }, $objects);
algorhythm
87

La solution dépend de la version PHP que vous utilisez. Au moins il y a 2 solutions:

First (versions PHP plus récentes)

Comme @JosepAlsina l'a dit avant, la solution la meilleure et la plus courte consiste à utiliser array_columncomme suit:

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

Remarque: Pour itérer un arraycontenant \stdClasses tel qu'utilisé dans la question, cela n'est possible qu'avec les versions PHP >= 7.0. Mais lorsque vous utilisez un arraycontenant arrays, vous pouvez faire la même chose depuis PHP >= 5.5.

Deuxième (anciennes versions de PHP)

@Greg a déclaré que dans les anciennes versions de PHP, il est possible de faire ce qui suit:

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

Mais attention: dans les nouvelles versions de PHP, >= 5.3.0il est préférable d'utiliser Closures, comme suit:

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


La différence

La première solution crée une nouvelle fonction et la met dans votre RAM. Le garbage collector ne supprime pas l'instance de fonction déjà créée et déjà appelée de la mémoire pour une raison quelconque. Et cela indépendamment du fait, que l'instance de fonction créée ne peut plus jamais être appelée, car nous n'avons pas de pointeur pour elle. Et la prochaine fois que ce code sera appelé, la même fonction sera à nouveau créée. Ce comportement remplit lentement votre mémoire ...

Les deux exemples avec sortie mémoire pour les comparer:

MAUVAIS

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

BIEN

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

    echo memory_get_usage() . "\n";

    sleep(1);
}

// the output
4235136
4235168
4235168
4235168
...


Cela peut également être discuté ici

Fuite de mémoire?! Garbage Collector fonctionne-t-il correctement lors de l'utilisation de 'create_function' dans 'array_map'?

algorithme
la source
bien que j'utilise php 7.2, mais il semble que la solution ne fonctionnera pas si vous utilisez des objets de classe d'espace de noms et retourne un tableau vide.
Muhammad Omer Aslam
Pouvez-vous publier un exploit? Un court extrait pour mieux comprendre votre contexte?
algorhythm
par exemple, voir cet extrait de code où j'ai une liste d'objets de modèle retournés par le SDK API Dropbox et lorsque nous essayons d'utiliser array_columnsur ce tableau, il retourne toujours vide alors que CET extrait fonctionne correctement, les deux ont la propriété protégée qui est accessible dans le deuxième extrait seulement. Je n'ai trouvé aucune autre raison que l'espace de noms.
Muhammad Omer Aslam
Quand j'écris objectdans ma solution, je objectne veux pas dire un Object. Le minuscule objectdécrit l'objet simple \stdClass. C'est peut-être là le problème. Avez-vous Objectune toArrayméthode? Utilisez ceci. Un simple objectet un arraysont presque les mêmes. Invariant suivant doit être valide: ((object)(array)$o) === $o. Lors de l'implémentation de la __getméthode, vous rendrez les propriétés de votre classe accessibles à tous. Cela peut être le comportement attendu. Mais vous pouvez également parcourir votre array<Object>exemple avec array_mapet appeler toArray(s'il existe), puis cela fonctionne également
algorhythm
le deuxième extrait que j'ai fourni provient de php, net et utilise le même objet de classe que dans le premier extrait de code, peut-être que vous avez raison, peut-être que c'est quelque chose d'autre que je vais essayer en le convertissant en un tableau merci.
Muhammad Omer Aslam
4
function extract_ids($cats){
    $res = array();
    foreach($cats as $k=>$v) {
        $res[]= $v->id;
    }
    return $res
}

et utilisez-le en une seule ligne :

$ids = extract_ids($cats);
SilentGhost
la source
1
Merci SilentGhost, mais ma question est sur le point d'obtenir $ res en une seule commande, sans utiliser de boucle ...
abernier
2
Quelle différence cela fait? C'est à peu près aussi simple que possible. Vous utiliserez probablement plus de lignes en utilisant quelque chose comme array_walk.
deceze
@deceze, une solution élégante, simple et concise, est tout simplement meilleure - en particulier pour quelque chose d'aussi explicable que cette opération (par exemple, ce n'est pas une logique personnalisée qui mérite beaucoup d'espace et de commentaires). Pour moi, cela fait une grande différence.
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);

?>

PRODUCTION

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

Je sais qu'il utilise une boucle, mais c'est le moyen le plus simple de le faire! Et en utilisant une fonction, il se termine toujours sur une seule ligne.

Luke Antins
la source
Je ne comprends pas pourquoi les gens n'optent pas seulement pour des solutions simples comme celle-ci. Cela semble logique de le faire de cette façon si vous me demandez.
Tom Dyer
3

Vous pouvez le faire facilement avec des goodies ouzo

$result = array_map(Functions::extract()->id, $arr);

ou avec des tableaux (à partir de goodies ouzo)

$result = Arrays::map($arr, Functions::extract()->id);

Consultez: http://ouzo.readthedocs.org/en/latest/utils/functions.html#extract

Voir aussi programmation fonctionnelle avec ouzo (je ne peux pas poster de lien).

woru
la source
3

L'avertissement create_function() est obsolète depuis PHP 7.2.0. Il est fortement déconseillé de se fier à cette fonction.

Les boucles intégrées en PHP sont plus rapides que les boucles interprétées, il est donc logique de faire de celle-ci une ligne unique:

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

Sortie : [1, 2]

daHormez
la source
0

La create_function()fonction est obsolète à partir de php v7.2.0 . Vous pouvez utiliser array_map()comme indiqué,

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

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

Vous pouvez également utiliser une array_column()fonction qui renvoie les valeurs d'une seule colonne de l'entrée, identifiée par la clé_colonne. Facultativement, un index_key peut être fourni pour indexer les valeurs dans le tableau retourné par les valeurs de la colonne index_key du tableau d'entrée. Vous pouvez utiliser le array_column comme indiqué,

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