PHP - извлечение свойства из массива объектов


128

У меня есть массив объектов кошек:

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

и я хочу извлечь массив идентификаторов кошек в 1 строку (не функцию и не цикл).

Я думал об использовании array_walkс, create_functionно не знаю, как это сделать.

Любая идея?


Я не хочу использовать цикл, потому что хочу, чтобы результат был записан в одну строку: $ cats_id = myfunction ($ cats); / * должен вернуть Array (15, 18, 23) * /
abernier

Ответы:


150

Если у вас PHP 5.5 или новее , лучше всего использовать встроенную функцию array_column():

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

Но сын должен быть массивом или преобразован в массив


12
На самом деле это лучший ответ
Дмитрий Буслаев

1
Лучшее решение, но доступно только для php 5.5 и выше
Ludo - Не для записи

2
в любом случае никто не должен больше кодировать 5.5. Версия языка программирования, которой 3,5 года ...
Toskan

11
Это решение не отвечает на вопрос, потому что array_columnвообще не работает с одним arrayиз object. Поскольку возможен PHP 7.0.0: stackoverflow.com/a/23335938/655224
algorhythm

1
Он работает только тогда, когда свойство объекта имеет открытый доступ.
Кароль Гасеница

154

Предупреждение create_function() УСТАРЕЛО с PHP 7.2.0. Полагаться на эту функцию крайне не рекомендуется.

Вы можете использовать array_map()функцию.
Это должно сделать это:

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

54
это правильное решение, и оно приведет к тому, что каждый следующий сопровождающий будет "wtf" d: D
Андреас Клингер

4
Единственное, что мне не нравится в этом решении, - это использование create_function.
Ionuț G. Stan

4
Вы можете использовать лямбда-функцию, но не многие люди будут использовать PHP 5.3
Грег

26
Начнем: $project_names = array_map(function($project) { return $project->name ;}, $projects );однако в этом сообщении в блоге отмечается, что в этом случае он может быть в 2,5 раза медленнее / потребляет много памяти.
Relequestual

7
Я выяснил, что каждый раз, когда функция создается с create_functionпамятью, она растет. Если вы напишете программу с бесконечными циклами и вызовете в ней array_mapwith, create_functionвы всегда будете когда-нибудь получать Out of memory...ошибку. Так что не используйте create_functionи не используйтеarray_map(function($o) { return $o->id; }, $objects);
algorhythm

87

Решение зависит от версии PHP, которую вы используете. По крайней мере, есть 2 решения:

Первый (более новые версии PHP)

Как сказал @JosepAlsina, лучшее и самое короткое решение - использовать array_columnследующее:

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

Примечание: повторять итерацию arrayсодержащего \stdClasses, используемого в вопросе, можно только с версиями PHP >= 7.0. Но при использовании arrayсодержащего arrays вы можете сделать то же самое, начиная с PHP >= 5.5.

Второй (более старые версии PHP)

@Greg сказал, что в более старых версиях PHP можно сделать следующее:

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

Но будьте осторожны: в новых версиях PHP >= 5.3.0лучше использовать Closures, например:

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


Различия

Первое решение создает новую функцию и помещает ее в вашу оперативную память. Сборщик мусора по какой-то причине не удаляет уже созданный и уже вызванный экземпляр функции из памяти. И это независимо от того, что созданный экземпляр функции никогда не может быть вызван снова, потому что у нас нет указателя на него. И при следующем вызове этого кода та же функция будет создана снова. Такое поведение медленно заполняет вашу память ...

Оба примера с выводом в память для сравнения:

ПЛОХОЙ

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

ХОРОШО

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

    echo memory_get_usage() . "\n";

    sleep(1);
}

// the output
4235136
4235168
4235168
4235168
...


Это также можно обсудить здесь

Утечка памяти?! Правильно ли работает сборщик мусора при использовании create_function внутри array_map?


хотя я использую php 7.2, но похоже, что решение не будет работать, если вы используете объекты класса пространства имен и возвращает пустой массив.
Мухаммад Омер Аслам

Вы можете опубликовать эксплойт? Короткий фрагмент, чтобы лучше понять ваш контекст?
алгоритм 04

например, посмотрите этот фрагмент, где у меня есть список объектов модели, возвращаемых SDK API Dropbox, и когда мы пытаемся использовать array_columnэтот массив, он всегда возвращает пустое значение, тогда как ЭТО фрагмент работает правильно, оба имеют защищенное свойство, которое доступно во втором фрагменте только. Я не мог найти никакой другой причины, кроме пространства имен.
Мухаммад Омер Аслам

Когда я пишу objectв своем решении, я имею в виду objectне файл Object. В нижнем регистре objectописывается простой объект \stdClass. Может, в этом проблема. У вас Objectесть toArrayметод? Использовать это. Простое objectи arrayпочти то же самое. После инвариант должен быть действительным: ((object)(array)$o) === $o. При реализации __getметода вы сделаете свойства своего класса доступными для всех. Это может быть ожидаемым поведением. Но вы также можете выполнить итерацию, array<Object>например, с помощью array_mapи позвонить toArray(если существует), и тогда ЭТО также работает
алгоритм

второй предоставленный мной фрагмент взят из php, net и использует тот же объект класса, что и в первом фрагменте, возможно, вы правы, возможно, это что-то еще, что я попробую, преобразовав его в массив, спасибо.
Мухаммад Омер Аслам

4
function extract_ids($cats){
    $res = array();
    foreach($cats as $k=>$v) {
        $res[]= $v->id;
    }
    return $res
}

и используйте его в одну строку :

$ids = extract_ids($cats);

1
Спасибо, SilentGhost, но мой вопрос заключается в том, чтобы получить $ res только в 1 команде, без использования цикла ...
Абернье

2
Что это меняет? Это настолько просто, насколько это возможно. Вы, вероятно, будете использовать больше строк, используя что-то вроде array_walk.
deceze

@deceze, элегантное, простое и лаконичное решение выглядит лучше - особенно для чего-то столь объяснимого, как эта операция (например, это не настраиваемая логика, которая заслуживает много места и комментариев). Для меня это имеет большое значение.
Чарли Далсасс

3

КОД

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

?>

ВЫВОД

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

Я знаю, что он использует цикл, но это самый простой способ сделать это! И при использовании функции он все равно попадает в одну строку.


Я не понимаю, почему люди просто не выбирают такие простые решения. Если вы спросите меня, то это кажется логичным.
Том Дайер

3

Вы можете легко сделать это с вкусностями узо

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

или с массивами (из вкусностей узо)

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

Проверьте: http://ouzo.readthedocs.org/en/latest/utils/functions.html#extract

См. Также функциональное программирование с помощью узо (я не могу разместить ссылку).


3

Предупреждение create_function() УСТАРЕЛО с PHP 7.2.0. Полагаться на эту функцию крайне не рекомендуется.

Встроенные циклы в PHP быстрее, чем интерпретируемые циклы, поэтому на самом деле имеет смысл сделать этот цикл однострочным:

$result = array();
array_walk($cats, create_function('$value, $key, &$result', '$result[] = $value->id;'), $result)


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

Вывод : [1, 2]


0

create_function()Функция устаревшим PHP v7.2.0 . Вы можете использовать array_map()как дано,

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

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

В качестве альтернативы вы можете использовать array_column()функцию, которая возвращает значения из одного столбца ввода, идентифицированного с помощью column_key. По желанию, index_key может быть предоставлен для индексации значений в возвращаемом массиве по значениям из столбца index_key входного массива. Вы можете использовать array_column как указано,

$IDs = array_column($array_of_object , 'id');
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.