数组过滤器和数组值在大型PHP对象数组上花费了很长时间


Array Filter and Array Values taking a long time on large PHP array of objects

我有一段代码在运行模拟。

public function cleanUpHouses('DeadStreet'ValueObject'House'Collection $collection)
{
    $houses = $collection->getHouses();
    $housesLength = count($houses);
    $filterValues = false;
    for($i = 0; $i < $housesLength; $i++) {
        if(!$this->houseModel->hasBeenAttacked($houses[$i])) {
            break;
        }
        $houses[$i]->setCurrentAttackers(0);
        if($this->houseModel->requiresDestroying($houses[$i])) {
            $houses[$i] = null;
            $filterValues = true;
        }
    }
    if($filterValues) {
        $houses = array_values(array_filter($houses));
    }
    $collection->setHouses($houses);
    return $collection;
}

然而,$collection包含一个最多超过100万个结果的数组($getHouses(,尽管它永远不需要对所有这些结果进行迭代,但由于数组的巨大规模,行$houses = array_values(array_filter($houses))花费了很长时间(每次运行该行最多3秒(。

我必须保持数组索引为数字,并且这个数组中不能有空值。

我希望unset($array[$i])会在元素在键中被取消"向下"设置后移动数组元素,所以如果我是unset($array[5]),那么$array[6]就会变成$array[5],但它似乎不是这样工作的。

break条件之所以存在,是因为在迭代中,如果迭代中的房屋没有受到攻击,那么可以安全地假设阵列中的任何其他房屋也没有受到攻击。

有没有一种最佳的、资源较少的方式来实现这一点?

我现在不能真的重组它,因为它正在单元测试中,我需要它尽快完成,这种方法不太好,但嗯。

我认为实现这一点最无痛的方法是这样的:

当你在房子的数组中循环时,你需要在数组中取消设置一些东西,你可以欺骗循环本身。

if($this->houseModel->requiresDestroying($houses[$i])) {
    // $houses[$i] = null;
    // when you unset the $i house in the array,
    // you can simply switch it with the last one in the array, keeping in mind,
    // that this may break your logic with the break condition, so will want to change that as well.
    $lastHouse = $houses[$housesLength - 1];
    $houses[$i] = $lastHouse;
    unset($houses[$housesLength - 1]);
    $i--;
    $housesLength--; // by doing the top two lines we would make the loop check the last house again.
    $shouldBreak = false; // this will keep your logic with the break if later.
    // $filterValues = true; // you can remove this line here.
}

您可能希望在for循环开始之前为break条件设置一个变量。

$shouldBreak = true;
for($i = 0; $i < $housesLength; $i++) {
    ...

现在对于条件本身

if(!$this->houseModel->hasBeenAttacked($houses[$i]) && true === $shouldBreak) {
    break;
} else {
    $shouldBreak = true; // we set $shouldBreak = false when we unset the last house,
    // so we would want to keep checking the houses not to break the logic.
}

我们只会删除数组中的最后一个元素,因此它将保持为数字。