183

I've got an array of cats objects:

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

and I want to extract an array of cats' IDs in 1 line (not a function nor a loop).

I was thinking about using array_walk with create_function but I don't know how to do it.

Any idea?

SilentGhost
  • 307,395
  • 66
  • 306
  • 293
abernier
  • 27,030
  • 20
  • 83
  • 114
  • I don't want to use a loop because I want to be able to assign the result in 1 line: $cats_id = myfunction($cats); /* should return Array(15, 18, 23) */ – abernier Jul 13 '09 at 11:43

10 Answers10

228

If you have PHP 5.5 or later, the best way is to use the built in function array_column():

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

But the son has to be an array or converted to an array

MikeSchinkel
  • 4,947
  • 4
  • 38
  • 46
Josep Alsina
  • 2,762
  • 1
  • 16
  • 12
  • 19
    This solutions doesn't answer the question because, `array_column` doesn't work with an `array` of `object`s at all. Since PHP 7.0.0 is is possible: https://stackoverflow.com/a/23335938/655224 – algorhythm May 23 '17 at 06:36
  • 4
    It's working only when object property has public access. – Karol Gasienica Dec 31 '18 at 11:44
194

Warning create_function() has been DEPRECATED as of PHP 7.2.0. Relying on this function is highly discouraged.

You can use the array_map() function.
This should do it:

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

As @Relequestual writes below, the function is now integrated directly in the array_map. The new version of the solution looks like this:

$catIds = array_map(function($o) { return $o->id;}, $objects);
Sarah Trees
  • 822
  • 12
  • 28
Greg
  • 316,276
  • 54
  • 369
  • 333
  • 61
    this is the correct solution and will lead to the fact that every upcoming maintainer will be "wtf"'d :D – Andreas Klinger Jul 13 '09 at 11:56
  • it is also not scalable. I've no idea why would OP want this approach. – SilentGhost Jul 13 '09 at 12:02
  • 6
    The only thing that I dislike about this solution is the use of create_function. – Ionuț G. Stan Jul 13 '09 at 12:16
  • 4
    You could use a lambda function but not many people will be running PHP 5.3 – Greg Jul 13 '09 at 12:22
  • @SilentGhost - I'd be interested to know what you suggest is a more scalable approach? We have a similar situation, where an existing function (we can't modify as it's maintained elsewhere) returns an array.We want to extract the ID property only into another array, to then pass to another (existing) function (we also can't modify). Is Greg's answer correct in this case? – Chris May 07 '12 at 19:51
  • 1
    I think it's time that this answer gets refreshed to include closure :) – Ja͢ck Jan 18 '13 at 14:31
  • 29
    Here we go: `$project_names = array_map(function($project) { return $project->name ;}, $projects );` However, it is noted [in this blog post](http://willem.stuursma.name/2010/11/22/a-detailed-look-into-array_map-and-foreach/) that it could be 2.5 times slower / memory intensive this way. – Relequestual Jul 03 '13 at 15:30
  • 8
    I found out, that everytime when the function is created with `create_function` the memory is growing up. If you write a programm with infinite loops and call the `array_map` with `create_function` in it you will always get an `Out of memory...` error sometime. So don't use `create_function` and use `array_map(function($o) { return $o->id; }, $objects);` – algorhythm Sep 12 '14 at 11:47
  • This may also be discussed here: http://stackoverflow.com/questions/25808390/memory-leak-is-garbage-collector-doing-right-when-using-create-function-with – algorhythm Sep 12 '14 at 13:28
  • 4
    `create_function` is deprecated since php 7.2. – Fernando Basso Jun 21 '17 at 13:39
  • It's even more simple with an arrow function `$catIds = array_map(fn($o) => $o->id, $objects);` – Kamil P. Jul 20 '22 at 07:36
100

The solution depends on the PHP version you are using. At least there are 2 solutions:

First (Newer PHP versions)

As @JosepAlsina said before the best and also shortest solution is to use array_column as following:

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

Notice: For iterating an array containing \stdClasses as used in the question it is only possible with PHP versions >= 7.0. But when using an array containing arrays you can do the same since PHP >= 5.5.

Second (Older PHP versions)

@Greg said in older PHP versions it is possible to do following:

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

But beware: In newer PHP versions >= 5.3.0 it is better to use Closures, like followed:

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

The difference

The solution using create_function() creates a new function and puts it into your RAM. The garbage collector does not delete the already created and already called function instance out of memory for some reason. And that regardless of the fact, that the created function instance can never be called again, because we have no pointer for it. And the next time when this code is called, the same function will be created again. This behavior slowly fills your memory...

Both examples with memory output to compare them:

BAD

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

GOOD

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

    echo memory_get_usage() . "\n";

    sleep(1);
}

// the output
4235136
4235168
4235168
4235168
...

This may also be discussed here

Memory leak?! Is Garbage Collector doing right when using 'create_function' within 'array_map'?

algorhythm
  • 8,530
  • 3
  • 35
  • 47
  • although i am using php 7.2 but it seems like the solution wont work if you are using namespace class objects, and returns blank array. – Muhammad Omer Aslam Oct 04 '19 at 12:05
  • Can you post an exploit? A short snippet to understand your context better? – algorhythm Oct 04 '19 at 12:45
  • for instance, see this [snippet](https://pastebin.com/pAjYZS3q) where I have a list of model objects returned by dropbox API SDK and when we try to use `array_column` on this array it always returns blank whereas [THIS](https://pastebin.com/44G6UDvx) snippet works correctly, both have the protected property which is accessible in the second snippet only. I couldn't find any other reason than namespace. – Muhammad Omer Aslam Oct 04 '19 at 14:04
  • When I write `object` in my solution i mean `object` not an `Object`. The lowercased `object` describes the simple object `\stdClass`. Maybe that is the problem. Does your `Object` have a `toArray` method? Use this. A simple `object` and an `array` is nearly the same. Following invariant must be valid: `((object)(array)$o) === $o`. When implementing `__get` method you'll make your class properties accessable for everyone. This can be the expected behavior. But you also can iterate over your `array` e.g. with `array_map` and call `toArray` (if exists) and then IT also works – algorhythm Oct 04 '19 at 16:00
  • the second snippet i provided is from php,net and uses the same Class object as in the first snippet, maybe you are right maybe its something else i will try by converting it to an array thankyou. – Muhammad Omer Aslam Oct 04 '19 at 17:12
  • Because the second snippet has the magic method `__get()` and behaves like a simple `object` – algorhythm Oct 04 '19 at 18:07
4
function extract_ids($cats){
    $res = array();
    foreach($cats as $k=>$v) {
        $res[]= $v->id;
    }
    return $res
}

and use it in one line:

$ids = extract_ids($cats);
SilentGhost
  • 307,395
  • 66
  • 306
  • 293
  • 1
    Thanks SilentGhost, but my question is about to get $res in only 1 command, not using a loop... – abernier Jul 13 '09 at 11:50
  • 2
    What difference does it make? This is about as straight-forward as it gets. You'll probably use more lines using something like `array_walk`. – deceze Jul 13 '09 at 11:51
  • @deceze, an elegant, simple and terse solution just looks better - especially for something so explainable as this operation (e.g. this isn't custom logic that merits lots of space and comments). To me, that makes a big difference. – Charlie Dalsass Jun 27 '18 at 11:57
3

Warning create_function() has been DEPRECATED as of PHP 7.2.0. Relying on this function is highly discouraged.

Builtin loops in PHP are faster then interpreted loops, so it actually makes sense to make this one a one-liner:

$result = array();
array_walk($cats, create_function('$value, $key, &$result', '$result[] = $value->id;'), $result)
Dharman
  • 30,962
  • 25
  • 85
  • 135
soulmerge
  • 73,842
  • 19
  • 118
  • 155
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);

?>

OUTPUT

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

I know its using a loop, but it's the simplest way to do it! And using a function it still ends up on a single line.

Luke Antins
  • 2,010
  • 1
  • 19
  • 15
  • I don't understand why people don't just opt for simple solutions like this. Seems logical to do it this way if you ask me. – Tom Dyer Sep 19 '16 at 16:55
3

You can do it easily with ouzo goodies

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

or with Arrays (from ouzo goodies)

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

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

See also functional programming with ouzo (I cannot post a link).

woru
  • 1,420
  • 9
  • 17
1
    $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);

Output: [1, 2]

Draken
  • 3,134
  • 13
  • 34
  • 54
daHormez
  • 21
  • 2
1
// $array that contain records and id is what we want to fetch a
$ids = array_column($array, 'id');
Abhishek Gurjar
  • 7,426
  • 10
  • 37
  • 45
0

The create_function() function is deprecated as of php v7.2.0. You can use the array_map() as given,

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

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

Alternatively, you can use array_column() function which returns the values from a single column of the input, identified by the column_key. Optionally, an index_key may be provided to index the values in the returned array by the values from the index_key column of the input array. You can use the array_column as given,

$IDs = array_column($array_of_object , 'id');
Kiran Maniya
  • 8,453
  • 9
  • 58
  • 81