Search code examples
gopermutation

Golang print all permutations of an array of arrays


Looking for a method that will print all combinations of an array of arrays.

Data type would look something like this:

// print all combos
var data [][]string

Example

Input: [[Lorem, Ipsum], [Alpha, Beta, Theta]]

Expected Output: Lorem Alpha, Lorem Beta, Lorem Theta, Ipsum Alpha, Ipsum Beta, Ipsum Theta

The arrays can be of arbitrary length. What's the best way to accomplish this?


Solution

  • I'd iterate the vector of indexes of the slices.

    Single index iterator:

    // Iterator of a slice index. `len` equals to the length of the slice
    type IdxIter struct {
        idx uint
        len uint
    }
    
    // Returns true is the iteration is over.
    func (i IdxIter) Done() bool {
        return i.idx >= i.len
    }
    
    // Builds the next iteration value. If called for the last index,
    // the next value's `Done` returns `true`.
    func (i *IdxIter) Next() {
        i.idx++
    }
    
    // Resets the iterator
    func (i *IdxIter) Reset() {
        i.idx = 0
    }
    
    // The index value
    func (i IdxIter) Idx() uint {
        return i.idx
    }
    

    Iterator of a vector of indexes:

    // Index iterator for a slice of slices
    type IdxVectorIter []IdxIter
    
    // Returns true is the iteration is over.
    func (ii IdxVectorIter) Done() bool {
        last := len(ii) - 1
        return ii[last].Done()
    }
    
    // Builds the next iteration value. If called for the last index vector,
    // the next value's `Done` returns `true`.
    func (ii IdxVectorIter) Next() {
        if len(ii) == 0 {
            return
        }
        last := len(ii) - 1
        for pos := range ii[:last] {
            ii[pos].Next()
            if ii[pos].Done() {
                ii[pos].Reset()
            } else {
                return
            }
        }
        ii[last].Next()
    }
    

    With that the iteration over the slice of slices is simple:

    func main() {
        words := [][]string{
            {"lorem", "ipsum"},
            {},
            {"Alpha", "Beta", "Gamma"},
            {"X", "Y", "Z"},
        }
        // Fixed buffer for the combinations of words
        dst := make([]string, len(words))
        // Iteration loop
        for ii := NewIdxVectorFromSlices(words); !ii.Done(); ii.Next() {
            GetTo(words, dst, ii)
            fmt.Printf("%v\n", dst)
        }
    }
    

    Full code https://go.dev/play/p/ecjjcAEexZO

    Output

    [lorem  Alpha X]
    [ipsum  Alpha X]
    [lorem  Beta X]
    [ipsum  Beta X]
    [lorem  Gamma X]
    [ipsum  Gamma X]
    [lorem  Alpha Y]
    [ipsum  Alpha Y]
    [lorem  Beta Y]
    [ipsum  Beta Y]
    [lorem  Gamma Y]
    [ipsum  Gamma Y]
    [lorem  Alpha Z]
    [ipsum  Alpha Z]
    [lorem  Beta Z]
    [ipsum  Beta Z]
    [lorem  Gamma Z]
    [ipsum  Gamma Z]