Search code examples
phpalgorithmsymfonytemplatescombinations

Populate an array with filepath strings based on ordered elements in a flat input array


I'm trying to develop an algorithm to create a Symfony template service. I want to check if a template exists in a subset of paths, ordered.

Given an array of parameter like this (already ordered like I want):

$params = ['O', 'U', 'W', 'P']

How can I output this array?

$urls = [
    'O/U/W/P/template',
    'O/U/W/template',
    'O/U/P/template',
    'O/U/template',
    'O/W/P/template',
    'O/W/template',
    'O/P/template',
    'O/template',
    'U/W/P/template',
    'U/W/template',
    'U/P/template',
    'U/template',
    'W/P/template',
    'W/template',
    'P/template',
    'template'
];

I can perform for a little list of parameters (like everyone can do it I suppose) with a code like this :

private function getPaths($template, $params)
{
    $urls           = [];
    $alreadyPerform = [];
    $paramsCounter = count($params);

    for ($i = 0; $i < $paramsCounter; $i++) {
        for ($j = 0; $j < $paramsCounter; $j++) {
            if ($i !== $j && !in_array($params[$j], $alreadyPerform, true)) {
                $urls[] = sprintf(
                    '/%s/%s/%s.html.twig', $params[$i], $params[$j], $template
                );
            }
        }
        $alreadyPerform[] = $params[$i];
        $urls[] = sprintf('/%s/%s.html.twig', $params[$i], $template);
    }
    $urls[] = sprintf('%s.html.twig', $template);

    return $urls;
}

This function work like I wanted until today (max 3 parameters), but I want to add one parameters today, maybe more after.


Solution

  • Using recursion, you can do the following:

    /**
     * @param array $elements
     * @param array $extra
     *
     * @return Generator
     */
    function gen(array $elements, array $extra = []): \Generator {
    
        foreach ($elements as $i => $head) {
            foreach (gen(array_slice($elements, $i + 1), $extra) as $tail) {
                yield array_merge([$head], $tail);
            }
        }
    
        yield $extra;
    }
    

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


    Or without recursion:

    /**
     * @param array $elements
     *
     * @return Generator
     */
    function gen2(array $elements): \Generator {
    
        for ($num = count($elements), $i = pow(2, $num) - 1; $i >= 1; $i -= 2) {
            $r = [];
            for ($j = 0; $j < $num; $j += 1) {
                if ($i & (1 << ($num - $j - 1))) {
                    $r[] = $elements[$j];
                }
            }
    
            yield $r;
        }
    }
    

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