Iterate over a cartesian product

I have an array of N arrays A_i: [A_1,A_2,...,A_N].

I want to iterate over the cartesian product A_1xA_2...xA_N.

Is there a simple way of doing this without specifying N?

This thread seems to be a pretty thorough treatment of the idea:
http://www.ruby-forum.com/topic/95519 . It was the first result of a google
search on "ruby cross_product".

···

On Tue, Sep 14, 2010 at 9:55 AM, Handy Gandy <handigandy-es@yahoo.com>wrote:

I have an array of N arrays A_i: [A_1,A_2,...,A_N].

I want to iterate over the cartesian product A_1xA_2...xA_N.

Is there a simple way of doing this without specifying N?

Handy Gandy wrote:

I have an array of N arrays A_i: [A_1,A_2,...,A_N].

I want to iterate over the cartesian product A_1xA_2...xA_N.

Is there a simple way of doing this without specifying N?

    ary = [[1, 2], [3, 4, 5], [6, 7, 8, 9]]
    ary.first.product(*ary.drop(1))
    # => [
    # => [1, 3, 6], [1, 3, 7], [1, 3, 8], [1, 3, 9],
    # => [1, 4, 6], [1, 4, 7], [1, 4, 8], [1, 4, 9],
    # => [1, 5, 6], [1, 5, 7], [1, 5, 8], [1, 5, 9],
    # => [2, 3, 6], [2, 3, 7], [2, 3, 8], [2, 3, 9],
    # => [2, 4, 6], [2, 4, 7], [2, 4, 8], [2, 4, 9],
    # => [2, 5, 6], [2, 5, 7], [2, 5, 8], [2, 5, 9]
    # => ]

jwm

That looks so cool I wish I knew what it does.

···

On Tue, 14 Sep 20 10 17:13:11 +0200, Jörg W Mittag wrote:

Handy Gandy wrote:

I have an array of N arrays A_i: [A_1,A_2,...,A_N].

I want to iterate over the cartesian product A_1xA_2...xA_N.

Is there a simple way of doing this without specifying N?

    ary = [[1, 2], [3, 4, 5], [6, 7, 8, 9]]
    ary.first.product(*ary.drop(1))
    # => [
    # => [1, 3, 6], [1, 3, 7], [1, 3, 8], [1, 3, 9], # => [1, 4, 6],
    [1, 4, 7], [1, 4, 8], [1, 4, 9], # => [1, 5, 6], [1, 5, 7], [1, 5,
    8], [1, 5, 9], # => [2, 3, 6], [2, 3, 7], [2, 3, 8], [2, 3, 9], #
    => [2, 4, 6], [2, 4, 7], [2, 4, 8], [2, 4, 9], # => [2, 5, 6],
    [2, 5, 7], [2, 5, 8], [2, 5, 9] # => ]

jwm

This looks nice. How did you do the formatting?

If destruction is allowed, we can do:

irb(main):013:0> ary = [[1, 2], [3, 4, 5], [6, 7, 8, 9]]
=> [[1, 2], [3, 4, 5], [6, 7, 8, 9]]
irb(main):014:0> pp ary.shift.product(*ary)
[[1, 3, 6],
[1, 3, 7],
[1, 3, 8],
[1, 3, 9],
[1, 4, 6],
[1, 4, 7],
[1, 4, 8],
[1, 4, 9],
[1, 5, 6],
[1, 5, 7],
[1, 5, 8],
[1, 5, 9],
[2, 3, 6],
[2, 3, 7],
[2, 3, 8],
[2, 3, 9],
[2, 4, 6],
[2, 4, 7],
[2, 4, 8],
[2, 4, 9],
[2, 5, 6],
[2, 5, 7],
[2, 5, 8],
[2, 5, 9]]
=> nil

Kind regards

robert

···

2010/9/14 Jörg W Mittag <JoergWMittag+Ruby@googlemail.com>:

Handy Gandy wrote:

I have an array of N arrays A_i: [A_1,A_2,...,A_N].

I want to iterate over the cartesian product A_1xA_2...xA_N.

Is there a simple way of doing this without specifying N?

ary = [[1, 2], [3, 4, 5], [6, 7, 8, 9]]
ary.first.product(*ary.drop(1))
# => [
# => [1, 3, 6], [1, 3, 7], [1, 3, 8], [1, 3, 9],
# => [1, 4, 6], [1, 4, 7], [1, 4, 8], [1, 4, 9],
# => [1, 5, 6], [1, 5, 7], [1, 5, 8], [1, 5, 9],
# => [2, 3, 6], [2, 3, 7], [2, 3, 8], [2, 3, 9],
# => [2, 4, 6], [2, 4, 7], [2, 4, 8], [2, 4, 9],
# => [2, 5, 6], [2, 5, 7], [2, 5, 8], [2, 5, 9]
# => ]

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

ary.first is the first element of ary.
ary.product(one, two, ...) is the cartesian product of ary with its arguments.
ary.drop(1) is ary[1..-1], all the elements of ary but the first.
ary.product(*ary.drop(1)) passes each element of ary[1..-1] to
product() as an argument, making it equivalent to

ary.product(ary[1], ary[2], ..., ary[-1])

···

That looks so cool I wish I knew what it does.

Robert Klemme wrote:

Handy Gandy wrote:

I have an array of N arrays A_i: [A_1,A_2,...,A_N].

I want to iterate over the cartesian product A_1xA_2...xA_N.

Is there a simple way of doing this without specifying N?

ary = [[1, 2], [3, 4, 5], [6, 7, 8, 9]]
ary.first.product(*ary.drop(1))
# => [
# => [1, 3, 6], [1, 3, 7], [1, 3, 8], [1, 3, 9],
# => [1, 4, 6], [1, 4, 7], [1, 4, 8], [1, 4, 9],
# => [1, 5, 6], [1, 5, 7], [1, 5, 8], [1, 5, 9],
# => [2, 3, 6], [2, 3, 7], [2, 3, 8], [2, 3, 9],
# => [2, 4, 6], [2, 4, 7], [2, 4, 8], [2, 4, 9],
# => [2, 5, 6], [2, 5, 7], [2, 5, 8], [2, 5, 9]
# => ]

This looks nice. How did you do the formatting?

By hand :slight_smile:

Though I assume Hirb or awesome_print could probably do it
automatically.

If destruction is allowed, we can do:

irb(main):013:0> ary = [[1, 2], [3, 4, 5], [6, 7, 8, 9]]
=> [[1, 2], [3, 4, 5], [6, 7, 8, 9]]
irb(main):014:0> pp ary.shift.product(*ary)
[[1, 3, 6],
...
=> nil

That's nice. Having toyed with Scala and Haskell, mutation just feels
*so* dirty to me. I guess I need some re-education to get back into
the idiomatic Ruby mindset.

jwm

···

2010/9/14 Jörg W Mittag <JoergWMittag+Ruby@googlemail.com>:

Whoops, that should be ary.first.product, of course.

···

On Tue, Sep 14, 2010 at 9:40 PM, Adam Prescott <mentionuse@gmail.com> wrote:

ary.first is the first element of ary.
ary.product(one, two, ...) is the cartesian product of ary with its arguments.
ary.drop(1) is ary[1..-1], all the elements of ary but the first.
ary.product(*ary.drop(1)) passes each element of ary[1..-1] to
product() as an argument, making it equivalent to

ary.product(ary[1], ary[2], ..., ary[-1])

That looks so cool I wish I knew what it does.

Robert Klemme wrote:

Handy Gandy wrote:

I have an array of N arrays A_i: [A_1,A_2,...,A_N].

I want to iterate over the cartesian product A_1xA_2...xA_N.

Is there a simple way of doing this without specifying N?

ary = [[1, 2], [3, 4, 5], [6, 7, 8, 9]]
ary.first.product(*ary.drop(1))
# => [
# => [1, 3, 6], [1, 3, 7], [1, 3, 8], [1, 3, 9],
# => [1, 4, 6], [1, 4, 7], [1, 4, 8], [1, 4, 9],
# => [1, 5, 6], [1, 5, 7], [1, 5, 8], [1, 5, 9],
# => [2, 3, 6], [2, 3, 7], [2, 3, 8], [2, 3, 9],
# => [2, 4, 6], [2, 4, 7], [2, 4, 8], [2, 4, 9],
# => [2, 5, 6], [2, 5, 7], [2, 5, 8], [2, 5, 9]
# => ]

This looks nice. How did you do the formatting?

By hand :slight_smile:

14:09:29 ~$ gem19 install 'By hand'
ERROR: could not find gem By hand locally or in a repository
14:09:58 ~$

Hm...

Though I assume Hirb or awesome_print could probably do it
automatically.

If destruction is allowed, we can do:

irb(main):013:0> ary = [[1, 2], [3, 4, 5], [6, 7, 8, 9]]
=> [[1, 2], [3, 4, 5], [6, 7, 8, 9]]
irb(main):014:0> pp ary.shift.product(*ary)
[[1, 3, 6],
...
=> nil

That's nice. Having toyed with Scala and Haskell, mutation just feels
*so* dirty to me. I guess I need some re-education to get back into
the idiomatic Ruby mindset.

Unfortunately there is no #slice which has two return values. If
there was we could do

a,b = arr.slice 0,1

But we can do this to achieve the same (i.e. non destructively partitioning):

irb(main):016:0> a=(1..5).to_a
=> [1, 2, 3, 4, 5]
irb(main):017:0> y=(x=a.dup).slice! 0,1
=> [1]
irb(main):018:0> x
=> [2, 3, 4, 5]
irb(main):019:0> a
=> [1, 2, 3, 4, 5]

Well, I guess the simplest approach is still

irb(main):001:0> a=(1..5).to_a
=> [1, 2, 3, 4, 5]
irb(main):002:0> x,*y = a
=> [1, 2, 3, 4, 5]
irb(main):003:0> x
=> 1
irb(main):004:0> y
=> [2, 3, 4, 5]

So we can do

irb(main):026:0> ary = [[1, 2], [3, 4, 5], [6, 7, 8, 9]]
=> [[1, 2], [3, 4, 5], [6, 7, 8, 9]]
irb(main):027:0> x, *y = ary
=> [[1, 2], [3, 4, 5], [6, 7, 8, 9]]
irb(main):028:0> pp x.product(*y)
[[1, 3, 6],
[1, 3, 7],
[1, 3, 8],
[1, 3, 9],
[1, 4, 6],
[1, 4, 7],
[1, 4, 8],
[1, 4, 9],
[1, 5, 6],
[1, 5, 7],
[1, 5, 8],
[1, 5, 9],
[2, 3, 6],
[2, 3, 7],
[2, 3, 8],
[2, 3, 9],
[2, 4, 6],
[2, 4, 7],
[2, 4, 8],
[2, 4, 9],
[2, 5, 6],
[2, 5, 7],
[2, 5, 8],
[2, 5, 9]]
=> nil

:slight_smile:

Kind regards

robert

···

2010/9/15 Jörg W Mittag <JoergWMittag+Ruby@googlemail.com>:

2010/9/14 Jörg W Mittag <JoergWMittag+Ruby@googlemail.com>:

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/