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

时间:2016-08-05 13:02:36

标签: php arrays

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

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包含一个数组($getHouses),最多可达100万条结果,但它永远不需要迭代所有这些结果,行$houses = array_values(array_filter($houses))是由于数组的大小,所以需要很长时间(每次运行此行时最多3秒)。

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

我希望unset($array[$i])在键中取消设置元素之后移位数组元素,所以如果我去unset($array[5]),那么$array[6]将成为$array[5]但是它看起来不像这样。

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

是否有最佳的,资源较少的方法来实现这一目标?

我现在不能真的重组这个,因为它是在单元测试中我需要尽快完成,方法不是很好,但是呃。

1 个答案:

答案 0 :(得分:0)

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

当你循环遍历房屋阵列时,你需要在数组中取消设置,你可以欺骗循环本身。

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循环开始之前为中断条件设置变量。

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

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