2

I'm trying to generate an array of all possible data based on 2 arrays:

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

The result should be something like:

[
    [
        "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
    ]
    ...
]

This is what I've done so far:

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]);

And I'm getting correct results but the array is not formatted as I proposed, I tried different ways to do it, but had no luck to get correct results. I can't get my head around it.

EDIT: if is there a better way to do the loop please let me know.

Any help would be appreciated.

2 Answers 2

2

It seems you're looking for all permutations with repetition of all elements of $arr2 with length of the number of elements in $arr1.

If so, the following should work:

<?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";
}

To avoid memory issues you can use yield, but if an array is absolutely required use iterator_to_array.

Obviously this grows extremely quickly (\count($arr2) ** \count($arr1)), so be careful when using iterator_to_array.

demo: https://3v4l.org/l5PRo

Sign up to request clarification or add additional context in comments.

2 Comments

thanks for the solution, it seems very quick compared to my solution. Although I couldn't get it to work to build an array something like the one in the OP.
@SolidSnake Would you mind elaborating, from what I saw, I thought it was the correct solution? Maybe there's a way to change it.
0

I finally managed to do it. it's not the best solution but it works. Also if we have more than 10 items the process starts to slow down because it gets multiplied every time we add new item:

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;
}

called like this:

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.