PHP - wyodrębnianie właściwości z tablicy obiektów

128

Mam szereg obiektów dla kotów:

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

i chcę wyodrębnić tablicę identyfikatorów kotów w 1 linii (nie jako funkcja ani pętla).

Myślałem o użyciu array_walkz, create_functionale nie wiem, jak to zrobić.

Dowolny pomysł?

abernier
źródło
Nie chcę używać pętli, ponieważ chcę mieć możliwość przypisania wyniku w 1 linii: $ cats_id = moja funkcja ($ cats); / * powinno zwrócić Array (15, 18, 23) * /
abernier

Odpowiedzi:

150

Jeśli masz PHP 5.5 lub nowszy , najlepszym sposobem jest użycie wbudowanej funkcji array_column():

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

Ale syn musi być tablicą lub przekonwertowany na tablicę

Josep Alsina
źródło
12
To jest właściwie najlepsza odpowiedź
Dmitry Buslaev
1
Najlepszym rozwiązaniem, ale dostępny tylko dla PHP 5.5 i up
Ludo - Nieoficjalnie
2
i tak nikt nie powinien już kodować przy pomocy 5.5. 3.5 letnia wersja języka programowania ...
Toskan
11
Rozwiązanie to nie jest odpowiedź na to pytanie, ponieważ array_columnnie działa ze związkiem arrayo objects w ogóle. Ponieważ PHP 7.0.0 jest możliwe: stackoverflow.com/a/23335938/655224
algorhythm
1
Działa tylko wtedy, gdy właściwość obiektu ma dostęp publiczny.
Karol Gasienica
154

Ostrzeżenie create_function() zostało WYCOFANE z PHP 7.2.0. Poleganie na tej funkcji jest wysoce odradzane.

Możesz użyć array_map()funkcji.
To powinno wystarczyć:

$catIds = array_map(create_function('$o', 'return $o->id;'), $objects);
Greg
źródło
54
to jest poprawne rozwiązanie i doprowadzi do tego, że każdy przyszły opiekun będzie "wtf" 'd: D
Andreas Klinger
4
Jedyne, co mi się nie podoba w tym rozwiązaniu, to użycie funkcji create_function.
Ionuț G. Stan
4
Możesz użyć funkcji lambda, ale niewiele osób będzie korzystało z PHP 5.3
Greg,
26
Zaczynamy: $project_names = array_map(function($project) { return $project->name ;}, $projects );Jednak jest to odnotowane w tym wpisie na blogu w ten sposób może być 2,5 razy wolniejsze / intensywnie wykorzystujące pamięć.
Relequestual
7
Dowiedziałem się, że za każdym razem, gdy tworzona jest funkcja z create_functionpamięcią, rośnie. Jeśli napiszesz program z nieskończonymi pętlami i wywołasz w nim array_mapwith create_function, zawsze pojawi się Out of memory...błąd. Więc nie używajcreate_function i nie używajarray_map(function($o) { return $o->id; }, $objects);
algorhythm
87

Rozwiązanie zależy od używanej wersji PHP. Istnieją przynajmniej 2 rozwiązania:

Pierwsza (nowsze wersje PHP)

Jak powiedział wcześniej @JosepAlsina, najlepszym i zarazem najkrótszym rozwiązaniem jest użycie array_column:

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

Uwaga: W celu iteracji arrayzawierającego \stdClasses, jak użyto w pytaniu, jest to możliwe tylko w wersjach PHP >= 7.0. Ale używając arrayzawierającego arrays, możesz zrobić to samo od PHP>= 5.5 .

Po drugie (starsze wersje PHP)

@Greg powiedział, że w starszych wersjach PHP można wykonać następujące czynności:

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

Ale strzeż się: nowszych wersjach PHP >= 5.3.0lepiej jest używać Closures, jak na przykład:

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


Różnica

Pierwsze rozwiązanie tworzy nową funkcję i umieszcza ją w pamięci RAM. Z jakiegoś powodu moduł odśmiecania pamięci nie usuwa już utworzonej i wywołanej instancji funkcji z pamięci. I to niezależnie od tego, że utworzonej instancji funkcji nie da się już nigdy ponownie wywołać, bo nie mamy na nią wskaźnika. Następnym razem, gdy ten kod zostanie wywołany, ta sama funkcja zostanie ponownie utworzona. To zachowanie powoli wypełnia twoją pamięć ...

Oba przykłady z wyjściem pamięci, aby je porównać:

ZŁY

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

DOBRY

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

    echo memory_get_usage() . "\n";

    sleep(1);
}

// the output
4235136
4235168
4235168
4235168
...


Można to również omówić tutaj

Wyciek pamięci?! Czy Garbage Collector działa dobrze, gdy używa się funkcji „create_function” w obrębie „array_map”?

algorytm
źródło
chociaż używam php 7.2, ale wydaje się, że rozwiązanie nie zadziała, jeśli używasz obiektów klasy przestrzeni nazw i zwraca pustą tablicę.
Muhammad Omer Aslam
Czy możesz opublikować exploit? Krótki fragment, aby lepiej zrozumieć kontekst?
algorhythm
na przykład zobacz ten fragment, w którym mam listę obiektów modelu zwróconych przez dropbox API SDK i kiedy próbujemy użyć array_columnw tej tablicy, zawsze zwraca to puste, podczas gdy TEN fragment działa poprawnie, oba mają chronioną właściwość, która jest dostępna w drugim fragmencie tylko. Nie mogłem znaleźć innego powodu niż przestrzeń nazw.
Muhammad Omer Aslam
Kiedy piszę objectw moim rozwiązanie mam na myśli objectnie jest Object. Mała litera objectopisuje prosty obiekt \stdClass. Może w tym tkwi problem. Czy Objectmasz toArraymetodę? Użyj tego. Proste objecti arrayprawie to samo. W następstwie niezmienna musi być ważny: ((object)(array)$o) === $o. Podczas implementacji __getmetody sprawisz, że właściwości Twojej klasy będą dostępne dla każdego. Może to być oczekiwane zachowanie. Ale możesz też iterować po swoim array<Object>np. Z array_mapi zadzwonić toArray(jeśli istnieje), a wtedy IT również działa
algorhythm
drugi fragment, który dostarczyłem, pochodzi z php, net i używa tego samego obiektu klasy, co w pierwszym fragmencie, może masz rację, może to coś innego spróbuję, konwertując go na tablicę dziękuję.
Muhammad Omer Aslam
4
function extract_ids($cats){
    $res = array();
    foreach($cats as $k=>$v) {
        $res[]= $v->id;
    }
    return $res
}

i użyj go w jednej linii :

$ids = extract_ids($cats);
SilentGhost
źródło
1
Dzięki SilentGhost, ale moje pytanie ma zamiar uzyskać $ res tylko w 1 poleceniu, bez użycia pętli ...
abernier,
2
Jaką to robi różnicę? To jest tak proste, jak to tylko możliwe. Prawdopodobnie użyjesz więcej linii, używając czegoś takiego jak array_walk.
deceze
@deceze, eleganckie, proste i zwięzłe rozwiązanie wygląda po prostu lepiej - szczególnie w przypadku czegoś tak zrozumiałego jak ta operacja (np. nie jest to niestandardowa logika, która zasługuje na dużo miejsca i komentarzy). Dla mnie to duża różnica.
Charlie Dalsass
3

KOD

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

?>

WYNIK

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

Wiem, że używa pętli, ale to najprostszy sposób! Używając funkcji, nadal kończy się w jednej linii.

Luke Antins
źródło
Nie rozumiem, dlaczego ludzie nie wybierają takich prostych rozwiązań. Wydaje się logiczne, aby zrobić to w ten sposób, jeśli o mnie chodzi.
Tom Dyer,
3

Ostrzeżenie create_function() zostało WYCOFANE z PHP 7.2.0. Poleganie na tej funkcji jest wysoce odradzane.

Wbudowane pętle w PHP są szybsze niż pętle zinterpretowane, więc faktycznie ma sens, aby uczynić tę jedną linijką:

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

Wyjście : [1, 2]

daHormez
źródło
0

Ta create_function()funkcja jest przestarzała od wersji PHP 7.2.0 . Możesz użyć array_map()podanego,

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

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

Alternatywnie możesz użyć array_column()funkcji, która zwraca wartości z pojedynczej kolumny danych wejściowych, identyfikowanej przez klucz_kolumny. Opcjonalnie można podać klucz_indeksu w celu indeksowania wartości w zwracanej tablicy przez wartości z kolumny klucz_indeksu tablicy wejściowej. Możesz użyć tablicy_kolumny podanej,

$IDs = array_column($array_of_object , 'id');
Kiran Maniya
źródło