我能用数组函数(例如array_map)设置数组的键吗?

6
我非常喜欢函数式编程风格,使用数组映射从另一个对象数组创建对象数组。
$newObjects = array_map(
  function($oldObject) {
    return new NewObject($oldObject);
  },
  $oldObjects
);

所有的功能都很好,但我真的想能够设置数组的索引,使它们成为原始对象的ID,以便更轻松地从数组中进行搜索和检索,但我无法想到如何做到这一点,除非是不那么优雅。

$newObjects = array();
foreach ($oldObjects as $oldObject) {
  $newObjects[$oldObject->getId()] = new NewObject($oldObject);
}

有没有一种方法可以实现这个?

恶魔的辩护:尽管我很喜欢map/reduce/walk等函数,但有时候foreach是最直接、易读的解决方案 :) - Darragh Enright
是的,我同意,因人而异。从下面的建议来看,foreach在风格和可读性方面更具优势。 - Edwin Love
4个回答

3
那就 - array_reduce() 恰好是你需要的函数:
class Bar 
{
        protected $id;

        public function __construct($id)
        {
                $this->id = $id;
        }

        public function getId()
        {
                return $this->id;
        }
}

class Foo
{
        protected $bar;

        public function __construct(Bar $bar)
        {
                $this->bar = $bar;
        }
}

$oldObjects = [new Bar('x'), new Bar('y'), new Bar('z')];

$newObjects = array_reduce($oldObjects, function($current, Bar $obj) {
        $current[$obj->getId()] = new Foo($obj);
        return $current;
}, []);

这将在不需要花费额外的数组内存(如array_combine())的情况下完成所有就地操作。

然而,我建议只在必要时使用这样的结构。仅仅因为它“看起来更好”而使用它可能不是一个好主意——因为普通的循环在大多数情况下更易读。


嗯,有趣。必须再深入研究一下才能真正理解它的工作原理(从未真正使用过array_reduce)。我非常同意这并不一定是最好的方法,总体而言,我认为foreach或array_combine选项提供了最好的可读性和优雅性的平衡。这更多是一个理解的练习,而不是要求找到比foreach更好的功能性方法(尽管这很好)。 - Edwin Love
在底部,所有这些选项归结为循环,因此可以将其隐藏在引擎盖下。 - Alma Do

1
我认为在这种情况下,foreach 循环可能是最易读的解决方案,但你也可以使用 array_map()array_combine() 来实现你想要的效果。类似这样:
// empty array to store the old object ids
$ids = [];

// map over old objects, inheriting $id 
// from parent scope by reference 
$objs = array_map(function($oldObject) use (&$ids) {
    $ids[] = $oldObject->getId();
    return new NewObject($oldObject);
}, $oldObjects);

// combine id and object arrays
$newObjects = array_combine($ids, $objs);

希望这有所帮助 :)

啊,是的,那比我的整洁一些。尽管出于某种原因,我不喜欢外部数组(我对通过引用传递的对象有一种非理性的厌恶!) - Edwin Love
哈哈,说得对!看起来确实有点复杂。更多“功能性”语言可以更透明地完成同样的事情。PHP可能不是最漂亮的语言,但我们仍然喜欢它……对吧? - Darragh Enright

1
如果您使用array_walk和临时数组来处理新的索引,会怎样呢?
    $array = ['A', 'B', 'C', 'D'];
    $reIndexedTemp = [];

    array_walk(
        $array,
        function ($item, $key) use (&$reIndexedTemp) {
            // here you can have your logic to assemble your new index
            $reIndexedTemp[$key + 100] = $item;
        }
    );

    //$array = $reIndexedTemp;

    var_dump($array, $reIndexedTemp);

输出(不包括注释行):

array(4) {
  [0] =>
  string(1) "A"
  [1] =>
  string(1) "B"
  [2] =>
  string(1) "C"
  [3] =>
  string(1) "D"
}
array(4) {
  [100] =>
  string(1) "A"
  [101] =>
  string(1) "B"
  [102] =>
  string(1) "C"
  [103] =>
  string(1) "D"
}

哦,我认为那不够优雅!我不想使用一个临时数组。此外,$key 只是原始键,说实话并不相关。 - Edwin Love
我不是临时数组的粉丝,但我能想到的所有数组函数都依赖于数组索引,这是显而易见的原因。在处理数组本身的中间操作一个似乎不是一个好主意。如果您根本不需要原始数组的键,则可以忽略array_walk方法并保留array_map,但我担心仍然需要临时数组! - Ali
1
它不是一个临时数组。它存储你需要的输出。不幸的是,array_walk() 修改了它作为参数接收到的数组,并没有返回任何有用的值。它不能直接在函数式编程风格中使用 :-( 如果你需要在多个地方使用这个处理流程,请考虑将上面的代码封装在一个函数中,该函数返回“临时”数组(并满足你对函数式编程风格的渴望)。 - axiac

0

四处寻找 - 寻找在关联数组中用于键的array_map等效函数

建议使用array_combine来实现

所以我想应该是这样的

$newObjects = array_combine(
  array_map(
    function($oldObject) {
      return $oldObject->getId();
    },
    $oldObjects
  ),
  array_map(
    function($oldObject) {
      return new NewObject($oldObject);
    },
    $oldObjects
  )
);

嗯,这可能是最好的选择,仅次于夸大其词,但绝对比foreach复杂得多


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