PHP - 从一个对象数组中获取属性

189

我有一个猫的对象数组:

$cats = Array
    (
        [0] => stdClass Object
            (
                [id] => 15
            ),
        [1] => stdClass Object
            (
                [id] => 18
            ),
        [2] => stdClass Object
            (
                [id] => 23
            )
)
我想要从一行中提取出猫的ID数组(不使用函数或循环)。
我在考虑使用array_walkcreate_function但是我不知道怎么做。
有什么想法吗?

我不想使用循环,因为我希望能够在一行中分配结果: $cats_id = myfunction($cats); /* 应返回Array(15, 18, 23) */ - abernier
10个回答

231

如果您使用的是PHP 5.5或更高版本,最好的方法是使用内置函数array_column()

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

但是子元素必须是一个数组或被转换为数组


19
这个解决方案并没有回答这个问题,因为array_column根本无法处理一个由对象组成的数组。自从PHP 7.0.0之后,就变成了可能:https://dev59.com/bWAg5IYBdhLWcg3wq8aU#23335938 - algorhythm
4
仅当对象属性具有公共访问权限时,它才能正常工作。 - Karol Gasienica

196

警告 create_function() 函数已在 PHP 7.2.0 版本中被弃用。依赖此函数是不推荐的。

你可以使用 array_map() 函数。
以下代码应该可以实现相同的效果:

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

正如@Relequestual在下面所写的那样,该函数现在直接集成到array_map中。 解决方案的新版本看起来像这样:

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

61
这是正确的解决方案,将导致每个即将接手维护工作的人都会感到困惑。 :D - Andreas Klinger
6
我对这个解决方案唯一不喜欢的是使用了create_function函数。 - Ionuț G. Stan
29
这里是代码:$project_names = array_map(function($project) { return $project->name ;}, $projects ); 但是,这篇博客文章指出,这种方法可能会慢2.5倍/内存密集型。 - Relequestual
8
我发现,每次使用create_function创建函数时,内存都会增加。如果您编写一个带有无限循环的程序,并在其中使用create_function调用array_map,那么您始终会在某个时候收到“内存不足...”的错误提示。因此,请勿使用create_function,而是使用array_map(function($o) { return $o->id; }, $objects); - algorhythm
4
自 PHP 7.2 起,create_function 已被弃用。 - Fernando Basso
显示剩余6条评论

101

解决方案取决于您使用的PHP版本。至少有2种解决方案:

第一种(较新的PHP版本)

正如@JosepAlsina之前所说,最好且最简短的解决方案是使用array_column,具体如下:

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

注意: 对于在问题中使用的包含stdClass的数组进行迭代,只有在PHP版本>= 7.0时才可能。但是,当使用包含数组的数组时,自PHP版本>= 5.5以来也可以做同样的事情。
第二部分(较旧的PHP版本):
@Greg说,在较旧的PHP版本中,可以按照以下方式操作:
$catIds = array_map(create_function('$o', 'return $o->id;'), $objects);

但要小心: 在较新的PHP版本中>= 5.3.0最好使用Closure,如下所示:

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

区别

使用create_function()的解决方案会创建一个新函数并将其放入RAM中。出于某种原因,垃圾收集器不会从内存中删除已经创建和调用过的函数实例。而且,无论如何,由于没有指针可以调用已创建的函数实例,下次调用此代码时,将再次创建相同的函数。这种行为会逐渐填满您的内存...

将两个示例的内存输出进行比较:

糟糕的

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,但似乎如果您正在使用命名空间类对象,则该解决方案将无法正常工作,并返回空数组。 - Muhammad Omer Aslam
你能发布一个漏洞利用吗?为了更好地理解你的上下文,可以提供一个简短的代码片段吗? - algorhythm
例如,查看此代码片段,其中我有一个由Dropbox API SDK返回的模型对象列表,当我们尝试在此数组上使用array_column时,它总是返回空白,而代码片段可以正常工作,两者都具有受保护的属性,但只有第二个代码片段中可访问。我找不到除名称空间之外的任何其他原因。 - Muhammad Omer Aslam
当我在我的解决方案中写object时,我指的是object而不是Object。小写的object描述了简单的对象\stdClass。也许这就是问题所在。你的Object是否有一个toArray方法?使用它。一个简单的object和一个array几乎是一样的。以下不变量必须有效:((object)(array)$o) === $o。当实现__get方法时,您将使您的类属性对每个人都可以访问。这可能是期望的行为。但是,您还可以遍历您的array<Object>,例如使用array_map并调用toArray(如果存在),然后它也可以工作。 - algorhythm
我提供的第二个代码片段来自php.net,并使用与第一个代码片段相同的Class对象,也许你是对的,也许是其他问题,我将尝试将其转换为数组,谢谢。 - Muhammad Omer Aslam
因为第二个代码片段具有魔术方法__get()并且表现得像一个简单的object - algorhythm

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

并在一行中使用它:

$ids = extract_ids($cats);

1
谢谢SilentGhost,但我的问题是如何在仅使用一个命令中获取$res,而不是使用循环... - abernier
2
有什么区别吗?这是最直接的方法。您可能会使用更多行,例如“array_walk”。 - deceze
@deceze,一种优雅、简洁和简明的解决方案看起来更好——尤其是对于像这样可以解释的操作(例如,这不是需要大量空间和注释的自定义逻辑)。对我来说,这会产生很大的影响。 - Charlie Dalsass

3

警告:自PHP 7.2.0版本起,create_function()已被弃用。强烈不建议使用此功能。

PHP内置循环比解释性循环更快,因此将其变成一行代码是有意义的:

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

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

我知道它使用了循环,但这是最简单的方法!即使使用函数,它仍然会在一行上结束。


我不明白为什么人们不选择像这样简单的解决方案。如果你问我,这样做似乎很合理。 - Tom Dyer

3

1
// $array that contain records and id is what we want to fetch a
$ids = array_column($array, 'id');

https://www.php.net/manual/en/function.array-column.php - JJS

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

输出:[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');

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接