Extraindo uma propriedade de uma matriz de objetos

128

Eu tenho uma variedade de objetos de gatos:

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

e quero extrair uma matriz de identificações de gatos em 1 linha (não uma função nem um loop).

Eu estava pensando em usar array_walkcom create_functionmas eu não sei como fazê-lo.

Qualquer ideia?

abernier
fonte
Não quero usar um loop, porque quero poder atribuir o resultado em 1 linha: $ cats_id = myfunction ($ cats); / * deve retornar a matriz (15, 18, 23) * /
abernier 13/07/2009

Respostas:

149

Se você possui PHP 5.5 ou posterior , a melhor maneira é usar a função incorporada array_column():

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

Mas o filho tem que ser uma matriz ou convertido em uma matriz

Josep Alsina
fonte
12
Esta é realmente a melhor resposta
Dmitry Buslaev
1
Melhor solução, mas disponível apenas para php 5.5 e superior
Ludo - off the record
2
ninguém deveria estar mais codificando com 5.5 de qualquer maneira. 3,5 anos versão antiga linguagem de programação ...
Toskan
11
Essas soluções não respondem à pergunta porque, array_columnnão funcionam com um arrayde objects. Como o PHP 7.0.0 é possível: stackoverflow.com/a/23335938/655224
algorhythm
1
Está funcionando apenas quando a propriedade do objeto tem acesso público.
Karol Gasienica
154

O aviso create_function() foi descontinuado a partir do PHP 7.2.0. Confiar nessa função é altamente desencorajado.

Você pode usar a array_map()função
Isso deve servir:

$catIds = array_map(create_function('$o', 'return $o->id;'), $objects);
Greg
fonte
54
esta é a solução correta e vai levar para o fato de que cada futuro mantenedor será "wtf" 'd: D
Andreas Klinger
4
A única coisa que eu não gosto nessa solução é o uso da função create_.
Ionuţ G. Stan
4
Você poderia usar uma função lambda, mas muitas pessoas não vão estar executando PHP 5.3
Greg
26
Aqui vamos nós: $project_names = array_map(function($project) { return $project->name ;}, $projects );No entanto, neste post do blog , é possível notar que pode ser 2,5 vezes mais lento / com uso intenso de memória.
Relequestual
7
Eu descobri que toda vez que a função é criada com create_functiona memória está crescendo. Se você escrever um programa com loops infinitos e chamar o array_mapcom create_functionele, sempre haverá um Out of memory...erro em algum momento. Então não use create_functione usearray_map(function($o) { return $o->id; }, $objects);
algoritmo
87

A solução depende da versão do PHP que você está usando. Pelo menos existem 2 soluções:

Primeiro (versões mais recentes do PHP)

Como o @JosepAlsina disse antes, a melhor e mais curta solução é usar da array_columnseguinte maneira:

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

Nota: Para iterar um es arraycontendo, \stdClassconforme usado na pergunta, só é possível com versões do PHP >= 7.0. Mas ao usar um s arraycontendo arrayvocê pode fazer o mesmo desde o PHP >= 5.5.

Segundo (versões mais antigas do PHP)

O @Greg disse que em versões mais antigas do PHP é possível fazer o seguinte:

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

Mas cuidado: nas versões mais recentes do PHP >= 5.3.0é melhor usar Closures, como se segue:

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


A diferença

A primeira solução cria uma nova função e a coloca na sua RAM. O coletor de lixo não exclui a instância da função já criada e chamada de memória por algum motivo. E isso, independentemente do fato, que a instância da função criada nunca poderá ser chamada novamente, porque não temos ponteiros para ela. E na próxima vez em que esse código for chamado, a mesma função será criada novamente. Esse comportamento preenche lentamente sua memória ...

Ambos os exemplos com saída de memória para compará-los:

RUIM

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

BOA

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

    echo memory_get_usage() . "\n";

    sleep(1);
}

// the output
4235136
4235168
4235168
4235168
...


Isso também pode ser discutido aqui

Vazamento de memória?! O Garbage Collector está fazendo certo ao usar 'create_function' em 'array_map'?

algorítmico
fonte
Embora eu esteja usando o php 7.2, mas parece que a solução não funcionará se você estiver usando objetos de classe de espaço para nome e retorna uma matriz em branco.
Muhammad Omer Aslam
Você pode postar uma exploração? Um pequeno trecho para entender melhor seu contexto?
algorritmo
por exemplo, veja este trecho em que tenho uma lista de objetos de modelo retornados pelo SDK da API do dropbox e, quando tentamos usá array_column-lo, ele sempre retorna em branco, enquanto ESTE trecho funciona corretamente, ambos têm a propriedade protegida, acessível no segundo trecho só. Não encontrei outro motivo além do namespace.
Muhammad Omer Aslam
Quando escrevo objectna minha solução, quero dizer que objectnão Object. As letras minúsculas objectdescrevem o objeto simples \stdClass. Talvez esse seja o problema. O seuObject tem um toArraymétodo? Usa isto. Um simples objecte um arrayé quase o mesmo. Após invariante deve ser válido: ((object)(array)$o) === $o. Ao implementar o __getmétodo, você tornará suas propriedades de classe acessíveis a todos. Este pode ser o comportamento esperado. Mas você também pode iterar sobre o seu array<Object>ex com array_mape chamada toArray(se existir) e, em seguida, ele também funciona
algorritmo
o segundo snippet que forneci é do php, net e usa o mesmo objeto Class do primeiro snippet; talvez você esteja certo; talvez seja outra coisa que tentarei convertendo-o em um array obrigado.
Muhammad Omer Aslam
4
function extract_ids($cats){
    $res = array();
    foreach($cats as $k=>$v) {
        $res[]= $v->id;
    }
    return $res
}

e use-o em uma linha :

$ids = extract_ids($cats);
SilentGhost
fonte
1
Obrigado SilentGhost, mas minha pergunta está prestes a obter $ res em apenas um comando, sem usar um loop ...
abernier
2
Que diferença faz? Isso é o mais direto possível. Você provavelmente usará mais linhas usando algo como array_walk.
Deceze
@deceze, uma solução elegante, simples e concisa parece melhor - especialmente para algo tão explicável como esta operação (por exemplo, essa não é uma lógica personalizada que merece muito espaço e comentários). Para mim, isso faz uma grande diferença.
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);

?>

RESULTADO

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

Eu sei que está usando um loop, mas é a maneira mais simples de fazer isso! E usando uma função, ela ainda acaba em uma única linha.

Luke Antins
fonte
Não entendo por que as pessoas não optam apenas por soluções simples como essa. Parece lógico fazê-lo dessa maneira, se você me perguntar.
21816 Tom Dyer
3

O aviso create_function() foi descontinuado a partir do PHP 7.2.0. Confiar nessa função é altamente desencorajado.

Os loops embutidos no PHP são mais rápidos que os loops interpretados, portanto, faz sentido fazer deste um uma linha:

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

Saída : [1, 2]

daHormez
fonte
0

A create_function()função está obsoleta no php v7.2.0 . Você pode usar o array_map()dado,

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

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

Como alternativa, você pode usar a array_column()função que retorna os valores de uma única coluna da entrada, identificada pela column_key. Opcionalmente, uma index_key pode ser fornecida para indexar os valores na matriz retornada pelos valores da coluna index_key da matriz de entrada. Você pode usar o array_column como determinado,

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