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?


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


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

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'?


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

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.


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)

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

Saída : [1, 2]


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');
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.