PHP - 在对象数组中通过键查找对象,更新其值

6
我有一个对象数组,并希望更新其中一个对象的属性。
$objs = [ 
    ['value' => 2, 'key' => 'a'], 
    ['value' => 3, 'key' => 'b'] ,
];

假设我想将键为'a'的对象的“值”设置为5。

除了迭代数组搜索键之外,是否还有更快/更有效的方法来完成此操作?

谢谢。

编辑: 为什么我不能使用关联数组存在争议。因为这个数组是从JSON值获取的。

如果我的JSON对象是这样的:

"obj": {
    "a": {
        "key": "a", 
        "value": 2
    }, 
    "b": {
        "key": "b", 
        "value": 3
    }
}

不能保证对象的顺序被保留,这是必需的。

因此,我需要在每个对象中添加一个索引,以便使用usort()进行排序。所以我的JSON应该是:

"obj": {
    "a": {
        "key": "a", 
        "value": 2, 
        "index": 1
    }, 
    "b": {
        "key": "b", 
        "value": 3, 
        "index": 2
    }
}

但是我无法在一个对象上使用usort(),只能在数组上使用。因此我的JSON需要

"obj": [
    {
        "key": "a", 
        "value": 2, 
        "index": 1
    }, {
        "key": "b", 
        "value": 3, 
        "index":2
    }
]

这让我们回到了最初的问题。

如果数组的形式不同,例如 ['a' => $obj],那么你只需要检查它是否被设置并设置值:if(isset($objs['a']))$objs['a']['value'] = $value; - user3647971
1
我之前是这么做的,但由于这个数组是由JSON形成的,对象的顺序并没有得到尊重。因此,我不得不为每个对象添加一个索引,并使用该值和键。 - fractal5
你的 JSON 格式不正确。请发布有效的内容。 - Andreas
1
在我看来,你的问题已经有很多解决方案了;选择任何一种,并将你的应用逻辑调整到你选择的解决方案上。比如说,你可以改变它,给对象添加一个“order”字段。不清楚你想从社区得到什么帮助。 - Николай Лубышев
@Andreas,它为什么无效?除非你是在谈论第一个案例中缺少'value'周围的引号,这只是一个很容易修复的打字错误。但是你的答案帮了我,所以我会接受它作为正确的方法。谢谢。 - fractal5
4个回答

10
通过使用array_column(),您可以提取数组中索引为key的所有值。然后,您可以使用array_search()查找值a的第一次出现。这只会返回它找到值的第一个索引。接下来,您可以简单地替换该值,因为您现在已经拥有该值的索引。
$keys = array_column($objs, 'key');
$index = array_search('a', $keys);

if ($index !== false) {
    $objs[$index]['value'] = 5;
}

请看这个实时演示

1
请注意,如果未找到元素,array_search将返回false,这会破坏数组。https://3v4l.org/Lc2Dd 顺便说一句,需要更新的是值而不是键。 - Andreas
关于array_search()返回false,您是完全正确的。话虽如此,这将更新其值,而不是键(正如您在演示中所看到的)。 - Qirel
它更新了“键”,而不是值。正如您在我的演示中所看到的,它更新了错误的项目。在我的示例中,它不应该更新任何内容,但它更新了“键a”。 - Andreas
1
糟糕,我混淆了索引名称“key”/“value”,实际上它们是键和值。我会进行更新。谢谢! - Qirel
和 for 循环的成本一样,不是吗? - Weltschmerz
通常情况下,这种方法比循环更快一些 - 但它取决于数组的大小以及要进行多少次替换。 - Qirel

2

我建议您重新组织数组,使其如下:

$objs = [ 
'a' => ['value'=>2, 'key'=>'a'], 
'b' => ['value'=>3, 'key'=>'b'] 
];

现在

if( array_key_exists( 'a', $objs )) {
  $objs ['a'] ['value'] = 5;
}

我最初是这样的。但是我需要让对象中有一个索引值,这样我就可以在主数组上运行usort()。这是因为该数组来自JSON,原始顺序不被尊重。
然后创建一个index数组:
// When fill `$objs` array
$objs = [];
$arrIndex = [];
$idx = 0;
foreach( $json as $item ) {
  $arrIndex [ $item ['key']] = $idx;
  $objs [$idx ++] = $item;
}

// And your task:

if( array_key_exists( 'a', $arrIndex )) {
  $objs [ $arrIndex ['a']] ['value'] = 5;
}

我最初也是这样做的。但我需要让对象内部有一个索引值,这样我就可以在主数组上运行usort()函数了。这是因为该数组来自JSON,原始顺序不被尊重。 - fractal5
@fractal5,我不明白你在说什么。所有的数组都有索引或关联键。看起来你是在试图在这里得到另一个问题的答案。 - Andreas
@Andreas 不,他的意思是不能将 key 字段用作数组的索引。 - Николай Лубышев
1
但是没有解释为什么。我没有看到任何好的理由,为什么他不能使用那个。 - Andreas
我修改了上面的代码。那怎么样?当然@Andreas是对的:数据不够)))) - Николай Лубышев
显示剩余3条评论

1
你可以使用array_column函数将数组变为关联数组。这样你就可以直接赋值了。
$objs = [ ['value'=>2, 'key'=>'a'], ['value'=>3, 'key'=>'b'] ];
$objs = array_column($objs, null, "key");
$objs['a']['value'] = 5;

https://3v4l.org/7tJl0


这种方法相比遍历数组并在遇到对象时更新,有何更高效的地方? - Weltschmerz

0
除了遍历数组搜索键之外,还有更快/更有效的方法吗?
无论如何,您都必须付出迭代的代价。
您可以搜索您的集合以查找感兴趣的对象(需要线性时间),或者形成某种字典数据结构,例如哈希表(需要线性时间),然后在常数时间内查找感兴趣的对象。
这里没有免费的午餐。

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