Finding cartesian product with PHP associative arrays

前端 未结 10 1865
醉梦人生
醉梦人生 2020-11-22 04:17

Say that I have an array like the following:

Array
(
    [arm] => Array
        (
            [0] => A
            [1] => B
            [2] => C
         


        
10条回答
  •  孤独总比滥情好
    2020-11-22 04:57

    Here's what I could come up with:

    function inject($elem, $array) {
        return array_map(function ($n) use ($elem) { return array_merge((array)$elem, (array)$n); }, $array);
    }
    
    function zip($array1, $array2) {
        return array_reduce($array1, function ($v, $n) use ($array2) { return array_merge($v, inject($n, $array2));  }, array());
    }
    
    function cartesian_product($array) {
        $keys = array_keys($array);
        $prod = array_shift($array);
        $prod = array_reduce($array, 'zip', $prod);
        return array_map(function ($n) use ($keys) { return array_combine($keys, $n); }, $prod);
    }
    

    (Using pseudo array/list/dictionary notation below since PHP is simply too verbose for such things.)

    The inject function transforms a, [b] into [(a,b)], i.e. it injects a single value into each value of an array, returning an array of arrays. It doesn't matter whether a or b already is an array, it'll always return a two dimensional array.

    inject('a', ['foo', 'bar'])
        =>  [('a', 'foo'), ('b', 'bar')]
    

    The zip function applies the inject function to each element in an array.

    zip(['a', 'b'], ['foo', 'bar'])
        =>  [('a', 'foo'), ('a', 'bar'), ('b', 'foo'), ('b', 'bar')]
    

    Note that this actually produces a cartesian product, so zip is a slight misnomer. Simply applying this function to all elements in a data set in succession gives you the cartesian product for an array of any length.

    zip(zip(['a', 'b'], ['foo', 'bar']), ['42', '76'])
        =>  [('a', 'foo', '42'), ('a', 'foo', '76'), ('a', 'bar', '42'), …]
    

    This does not contain the keys, but since the elements are all in order within the result set, you can simply re-inject the keys into the result.

    array_combine(['key1', 'key2', 'key3'], ['a', 'foo', '42'])
        =>  [ key1 : 'a', key2 : 'foo', key3 : '42' ]
    

    Applying this to all elements in the product gives the desired result.

    You can collapse the above three functions into a single long statement if you wish (which would also clear up the misnomers).


    An "unrolled" version without anonymous functions for PHP <= 5.2 would look like this:

    function inject($elem, $array) {
        $elem = (array)$elem;
        foreach ($array as &$a) {
            $a = array_merge($elem, (array)$a);
        }
        return $array;
    }
    
    function zip($array1, $array2) {
        $prod = array();
        foreach ($array1 as $a) {
            $prod = array_merge($prod, inject($a, $array2));
        }
        return $prod;
    }
    
    function cartesian_product($array) {
        $keys = array_keys($array);
        $prod = array_shift($array);
        $prod = array_reduce($array, 'zip', $prod);
    
        foreach ($prod as &$a) {
            $a = array_combine($keys, $a);
        }
        return $prod;
    }
    

提交回复
热议问题