代码之家  ›  专栏  ›  技术社区  ›  Desolator

PHP递归调用以生成数组

  •  2
  • Desolator  · 技术社区  · 7 年前

    我正在尝试基于2个数组生成所有可能数据的数组:

    $arr1 = ['a', 'b', 'c'];
    $arr2 = [true, false];
    

    结果应该是:

    [
        [
            "a" => true,
            "b" => true,
            "c" => true
        ],
        [
            "a" => true,
            "b" => false,
            "c" => true
        ],
        [
            "a" => true,
            "b" => false,
            "c" => false
        ],
        [
            "a" => true,
            "b" => true,
            "c" => false
        ],
        [
            "a" => false,
            "b" => true,
            "c" => true
        ]
        ...
    ]
    

    这就是我迄今为止所做的:

    function generateAllCases($arr1, $arr2)
    {
        $resultArr = [];
    
        foreach ($arr1 as $i => $elm)
        {
            array_shift($arr1);
    
            foreach ($arr2 as $vis)
            {
                $resultArr[] =
                [
                    $elm => $vis
                ];
    
                $resultArr[] =  $this->generateAllCases($arr1, $arr2);
            }
        }
        return $resultArr;
    }
    
    generateAllCases(['a', 'b', 'c'], [true, false]);
    

    我得到了正确的结果,但是数组没有按照我的建议进行格式化,我尝试了不同的方法来实现它,但是没有运气得到正确的结果。我没办法绕过去。

    编辑:如果有更好的循环方法,请告诉我。

    任何帮助都将不胜感激。

    2 回复  |  直到 7 年前
        1
  •  2
  •   Yoshi senschen    7 年前

    似乎你在寻找所有 permutations with repetition 所有元素的 $arr2 元素数量的长度 $arr1 .

    如果是这样的话,以下内容应该有效:

    <?php
    declare(strict_types=1);
    
    error_reporting(-1);
    ini_set('display_errors', 'On');
    
    function pwr(array $elements, int $k, int $idx = 0, array &$result = []): \Generator
    {
        foreach ($elements as $element) {
            $result[$idx] = $element;
    
            if ($k - $idx > 1) {
                yield from pwr($elements, $k, $idx + 1, $result);
            }
            else {
                yield $result;
            }
        }
    }
    
    function gen(array $keys, array $values): \Generator
    {
        foreach (pwr($values, \count($keys)) as $set) {
            yield array_combine($keys, $set);
        }
    }
    
    // this is just to test the result in a more *visual* way
    foreach (gen(range('a', 'j'), [true, false]) as $case) {
        foreach ($case as $k => $v) {
            echo $v ? $k : '_';
        }
    
        echo "\n";
    }
    

    为了避免内存问题,您可以使用 yield ,但如果绝对需要数组,请使用 iterator_to_array .

    很明显,它生长得非常快( \count($arr2) ** \count($arr1) )因此使用时要小心 迭代器\到\数组 .

    演示: https://3v4l.org/l5PRo

        2
  •  0
  •   Desolator    7 年前

    我终于做到了。这不是最好的解决方案,但很有效。另外,如果我们有10个以上的项目,那么这个过程就会开始减慢,因为每次我们添加新项目时,它都会成倍增加:

    public function generateAllCases($arr1, $arr2, $resultArr, &$index, $firstCall = false)
    {
        $shifted = false;
    
        foreach ($arr1 as $elm)
        {
            foreach ($arr2 as $i => $vis)
            {
                if(!$shifted)
                {
                    array_shift($arr1);
                    $shifted = true;
                }
    
                if(!isset($resultArr[$index]) || !isset($resultArr[$index][$elm]))
                {
                    $resultArr[$index][$elm] = $vis;
                }
                else 
                {
                    $prevItem = $resultArr[$index];
                    $index++;
                    $resultArr[$index] = $prevItem;
                    $resultArr[$index][$elm] = $vis;
                }
    
                $resultArr = $this->generateAllCases($arr1, $arr2, $resultArr, $index);
            }
            break;
        }
    
        if($firstCall)
        {
            $allResults = [];
            foreach ($resultArr as $k => $v) 
            {
                $allResults[implode('.', $v)] = $v;
            }
    
            $allResults = array_values($allResults);
            return $allResults;
        }
    
        return $resultArr;
    }
    

    这样称呼:

    $index = 0;
    $cases = $rpd->generateAllCases(['a', 'b', 'c'], [true, false], [], $index, true);