Combination of two arrays

Greetings!

Let me start by clarifying that this is just a request for style
suggestions... So I won't be hugely disappointed if the plain old way
works just fine.

I have two arrays. let's call them foo and bar.
foo = [ "a", "b", "c", ... n ]
bar = [ 1 , 2 , 3 , ... m ]

I am looking for a way to combine them such that the result is something like

[ [ "a", 1 ], [ "a", 2 ], ... [ "a", m ], [ "b", 1 ], ... [ n, 1 ],
... [ n, m ] ]

I thought that foo * bar would do it, but upon it failing I checked
with the Pickaxe and found that it was not the case. Now, I could just
do

baz = []
foo.each do |f|
   bar.each do |b|
      baz << [ f, b ]
   end
end

and that would be just fine, but I was wondering if there was any
magical Ruby library/module that already does this. Maybe an extension
for Array that allows Array * Array, or something
that provides Array.combine( Array ). I performed a web search for
array combination/permutation(1) in Ruby and didn't get any relevant
results, which is why I'm asking here.
Again, this is just a matter of style. An answer along the lines of
"the nested loops are as good as it'll get for you" is completely
acceptable.

Many regards...
  -CWS

(1): The order of elements is important. I'm looking for combinations,
not permutations. But I searched for the latter anyway since
"combination" is too much of a common word and combinations and
permutations usually go hand in hand.

* Claus Spitzer <docboobenstein@gmail.com> [2005-07-28 20:57:24 +0900]:

I have two arrays. let's call them foo and bar.
foo = [ "a", "b", "c", ... n ]
bar = [ 1 , 2 , 3 , ... m ]

I am looking for a way to combine them such that the result is something like

[ [ "a", 1 ], [ "a", 2 ], ... [ "a", m ], [ "b", 1 ], ... [ n, 1 ],
... [ n, m ] ]

  irb(main):002:0> foo = [ "a", "b", "c" ]
  => ["a", "b", "c"]
  irb(main):003:0> bar = [ 1 , 2 , 3 ]
  => [1, 2, 3]
  irb(main):004:0> foo.zip(bar)
  => [["a", 1], ["b", 2], ["c", 3]]

···

--
Jim Freeze

harp:~ > cat a.rb
   foo = [ "a", "b", "c", 'n' ]
   bar = [ 1 , 2 , 3 , 'm' ]

   p foo.zip(bar)

   harp:~ > ruby a.rb
   [["a", 1], ["b", 2], ["c", 3], ["n", "m"]]

hth.

-a

···

On Thu, 28 Jul 2005, Claus Spitzer wrote:

Greetings!

Let me start by clarifying that this is just a request for style
suggestions... So I won't be hugely disappointed if the plain old way
works just fine.

I have two arrays. let's call them foo and bar.
foo = [ "a", "b", "c", ... n ]
bar = [ 1 , 2 , 3 , ... m ]

--

email :: ara [dot] t [dot] howard [at] noaa [dot] gov
phone :: 303.497.6469
My religion is very simple. My religion is kindness.
--Tenzin Gyatso

===============================================================================

i felt obiliged to send this after messing it up before :wink:

   harp:~ > cat a.rb
   foo = [ "a", "b", "c", 'n' ]
   bar = [ 1 , 2 , 3 , 'm' ]

   cross = foo.inject(a=){|a,f| bar.inject(a){|a,b| a << [f,b]}}
   p cross

   harp:~ > ruby a.rb
   [["a", 1], ["a", 2], ["a", 3], ["a", "m"], ["b", 1], ["b", 2], ["b", 3], ["b", "m"], ["c", 1], ["c", 2], ["c", 3], ["c", "m"], ["n", 1], ["n", 2], ["n", 3], ["n", "m"]]

i know it's just two loops - but at least it's one line :wink:

kind regards.

-a

···

On Thu, 28 Jul 2005, Claus Spitzer wrote:

Greetings!

Let me start by clarifying that this is just a request for style
suggestions... So I won't be hugely disappointed if the plain old way
works just fine.

I have two arrays. let's call them foo and bar.
foo = [ "a", "b", "c", ... n ]
bar = [ 1 , 2 , 3 , ... m ]

I am looking for a way to combine them such that the result is something like

[ [ "a", 1 ], [ "a", 2 ], ... [ "a", m ], [ "b", 1 ], ... [ n, 1 ],
... [ n, m ] ]

--

email :: ara [dot] t [dot] howard [at] noaa [dot] gov
phone :: 303.497.6469
My religion is very simple. My religion is kindness.
--Tenzin Gyatso

===============================================================================

Claus,

The order of elements is important. I'm looking for
combinations, not permutations.

Don't you have that backwards?

···

--
Daniel Brockman <daniel@brockman.se>

That is not the same as what the OP was proposing. One could write the
original approach using inject and shorten it one line by that
approach, but that is more obfuscation than neccessary. I don't have
any better proposal for Claus.

regards,

Brian

···

On 28/07/05, Jim Freeze <jim@freeze.org> wrote:

* Claus Spitzer <docboobenstein@gmail.com> [2005-07-28 20:57:24 +0900]:

> I have two arrays. let's call them foo and bar.
> foo = [ "a", "b", "c", ... n ]
> bar = [ 1 , 2 , 3 , ... m ]
>
> I am looking for a way to combine them such that the result is something like
>
> [ [ "a", 1 ], [ "a", 2 ], ... [ "a", m ], [ "b", 1 ], ... [ n, 1 ],
> ... [ n, m ] ]

  irb(main):002:0> foo = [ "a", "b", "c" ]
  => ["a", "b", "c"]
  irb(main):003:0> bar = [ 1 , 2 , 3 ]
  => [1, 2, 3]
  irb(main):004:0> foo.zip(bar)
  => [["a", 1], ["b", 2], ["c", 3]]

--
http://ruby.brian-schroeder.de/

Stringed instrument chords: http://chordlist.brian-schroeder.de/

i'm sorry claus - this obviously does not solve your problem. that's what i
get for answering email before coffee. please excuse noise.

cheers.

-a

···

On Thu, 28 Jul 2005, Ara.T.Howard wrote:

On Thu, 28 Jul 2005, Claus Spitzer wrote:

Greetings!

Let me start by clarifying that this is just a request for style
suggestions... So I won't be hugely disappointed if the plain old way
works just fine.

I have two arrays. let's call them foo and bar.
foo = [ "a", "b", "c", ... n ]
bar = [ 1 , 2 , 3 , ... m ]

harp:~ > cat a.rb
foo = [ "a", "b", "c", 'n' ]
bar = [ 1 , 2 , 3 , 'm' ]

p foo.zip(bar)

harp:~ > ruby a.rb
[["a", 1], ["b", 2], ["c", 3], ["n", "m"]]

--

email :: ara [dot] t [dot] howard [at] noaa [dot] gov
phone :: 303.497.6469
My religion is very simple. My religion is kindness.
--Tenzin Gyatso

===============================================================================

* Ara.T.Howard <Ara.T.Howard@noaa.gov> [2005-07-28 23:11:32 +0900]:

  cross = foo.inject(a=){|a,f| bar.inject(a){|a,b| a << [f,b]}}

Wow, that's great. But my brain hurts.

···

--
Jim Freeze

* Brian Schrder <ruby.brian@gmail.com> [2005-07-28 22:57:18 +0900]:

> * Claus Spitzer <docboobenstein@gmail.com> [2005-07-28 20:57:24 +0900]:
>
> > I have two arrays. let's call them foo and bar.
> > foo = [ "a", "b", "c", ... n ]
> > bar = [ 1 , 2 , 3 , ... m ]
> >
> > I am looking for a way to combine them such that the result is something like
> >
> > [ [ "a", 1 ], [ "a", 2 ], ... [ "a", m ], [ "b", 1 ], ... [ n, 1 ],
> > ... [ n, m ] ]
>
> irb(main):002:0> foo = [ "a", "b", "c" ]
> => ["a", "b", "c"]
> irb(main):003:0> bar = [ 1 , 2 , 3 ]
> => [1, 2, 3]
> irb(main):004:0> foo.zip(bar)
> => [["a", 1], ["b", 2], ["c", 3]]
>

That is not the same as what the OP was proposing. One could write the
original approach using inject and shorten it one line by that
approach, but that is more obfuscation than neccessary. I don't have
any better proposal for Claus.

Oops, read too fast. My apologies.
I wrote something just the other day that let me do the following:

  5 * [1,2,3] #=> [[5,1], [5,2], [5,3]]
  "fred" * [1,2,3] #=> [["fred",1], ["fred",2], ["fred",3]]
  :a * [1,2,3] #=> [[:a,1], [:a,2], [:a,3]]

So, I suppost I could take that and do:

  irb(main):001:0> foo = %w(a b c)
  => ["a", "b", "c"]
  irb(main):002:0> bar = [1,2,3]
  => [1, 2, 3]
  irb(main):003:0> foo.collect { |f| f * bar }
  => [[["a", 1], ["a", 2], ["a", 3]], [["b", 1], ["b", 2], ["b", 3]], [["c",
  1], ["c", 2], ["c", 3]]]

Is that more what you were looking to do?

Do you want the code?

···

On 28/07/05, Jim Freeze <jim@freeze.org> wrote:

--
Jim Freeze

* Ara.T.Howard <Ara.T.Howard@noaa.gov> [2005-07-28 23:11:32 +0900]:

> cross = foo.inject(a=){|a,f| bar.inject(a){|a,b| a << [f,b]}}

Wow, that's great. But my brain hurts.

I like the expanded version better though. Seems somehow less obfuscated.
And the a= is superflous:

$ irb

foo = [1,2,3]
bar = %w(a b c)
foo.inject(){ |result,a| bar.inject(result){ |result,b| result << [a,b] }}

=> [[1, "a"], [1, "b"], [1, "c"], [2, "a"], [2, "b"], [2, "c"], [3,
"a"], [3, "b"], [3, "c"]]

regards,

Brian

···

On 28/07/05, Jim Freeze <jim@freeze.org> wrote:

--
http://ruby.brian-schroeder.de/

Stringed instrument chords: http://chordlist.brian-schroeder.de/

Ara.T.Howard wrote:

harp:~ > cat a.rb
foo = [ "a", "b", "c", 'n' ]
bar = [ 1 , 2 , 3 , 'm' ]

p foo.zip(bar)

harp:~ > ruby a.rb
[["a", 1], ["b", 2], ["c", 3], ["n", "m"]]

I don't want Ara to fail!

>> (foo * bar.length).sort.zip(bar * bar.length)
=> [["a", 1], ["a", 2], ["a", 3], ["a", "m"], ["b", 1], ["b", 2], ["b", 3], ["b", "m"], ["c", 1], ["c", 2], ["c", 3], ["c", "m"], ["n", 1], ["n", 2], ["n", 3], ["n", "m"]]

_why

* Jim Freeze <jim@freeze.org> [2005-07-28 23:05:12 +0900]:

> > > [ [ "a", 1 ], [ "a", 2 ], ... [ "a", m ], [ "b", 1 ], ... [ n, 1 ],
> > > ... [ n, m ] ]

  irb(main):003:0> foo.collect { |f| f * bar }
  => [[["a", 1], ["a", 2], ["a", 3]], [["b", 1], ["b", 2], ["b", 3]], [["c",
  1], ["c", 2], ["c", 3]]]

To answer my own email, that is still not exactly what you
want, is it.

We need to flatten the first level.

  irb(main):004:0> foo.collect { |f| f * bar }.inject() { |a,e| a.concat e }
  => [["a", 1], ["a", 2], ["a", 3], ["b", 1], ["b", 2], ["b", 3], ["c", 1],
  ["c", 2], ["c", 3]]

There, that's better, but ugly. Anyone got a cleaner way to flatten?

···

--
Jim Freeze

* Brian Schrder <ruby.brian@gmail.com> [2005-07-28 23:21:29 +0900]:

···

On 28/07/05, Jim Freeze <jim@freeze.org> wrote:
> * Ara.T.Howard <Ara.T.Howard@noaa.gov> [2005-07-28 23:11:32 +0900]:
>
> > cross = foo.inject(a=){|a,f| bar.inject(a){|a,b| a << [f,b]}}
>
> Wow, that's great. But my brain hurts.

> foo.inject(){ |result,a| bar.inject(result){ |result,b| result << [a,b] }}
=> [[1, "a"], [1, "b"], [1, "c"], [2, "a"], [2, "b"], [2, "c"], [3,
"a"], [3, "b"], [3, "c"]]

Nice. Brain feel better.

--
Jim Freeze

you win _why! nice.

-a

···

On Thu, 28 Jul 2005, why the lucky stiff wrote:

Ara.T.Howard wrote:

harp:~ > cat a.rb
foo = [ "a", "b", "c", 'n' ]
bar = [ 1 , 2 , 3 , 'm' ]

p foo.zip(bar)

harp:~ > ruby a.rb
[["a", 1], ["b", 2], ["c", 3], ["n", "m"]]

I don't want Ara to fail!

(foo * bar.length).sort.zip(bar * bar.length)

=> [["a", 1], ["a", 2], ["a", 3], ["a", "m"], ["b", 1], ["b", 2], ["b", 3], ["b", "m"], ["c", 1], ["c", 2], ["c", 3], ["c", "m"], ["n", 1], ["n", 2], ["n", 3], ["n", "m"]]

_why

--

email :: ara [dot] t [dot] howard [at] noaa [dot] gov
phone :: 303.497.6469
My religion is very simple. My religion is kindness.
--Tenzin Gyatso

===============================================================================

Wow, what a plethora of responses! Thanks to everyone who responded.
I didn't really expect such a lively response. I really appreciate
all the support. Many regards...
-CWS

···

On 7/28/05, why the lucky stiff <ruby-talk@whytheluckystiff.net> wrote:

Ara.T.Howard wrote:

>>
>> harp:~ > cat a.rb
>> foo = [ "a", "b", "c", 'n' ]
>> bar = [ 1 , 2 , 3 , 'm' ]
>>
>> p foo.zip(bar)
>>
>> harp:~ > ruby a.rb
>> [["a", 1], ["b", 2], ["c", 3], ["n", "m"]]
>
I don't want Ara to fail!

>> (foo * bar.length).sort.zip(bar * bar.length)
=> [["a", 1], ["a", 2], ["a", 3], ["a", "m"], ["b", 1], ["b", 2], ["b",
3], ["b", "m"], ["c", 1], ["c", 2], ["c", 3], ["c", "m"], ["n", 1],
["n", 2], ["n", 3], ["n", "m"]]

_why

why the lucky stiff wrote:

>> (foo * bar.length).sort.zip(bar * bar.length)

With

foo = %w(one two three four)
bar = [6, 7, 8]

the result is

[["four", 6], ["four", 7], ["four", 8], ["one", 6], ["one", 7],
["one", 8], ["three", 6], ["three", 7], ["three", 8], ["two", nil],
["two", nil], ["two", nil]]

so it should be

(foo * bar.length).sort.zip(bar * foo.length)

However, if foo isn't already sorted, the order of its elements is
changed.

This is one of the prettiest pieces of ruby code I've seen! Also, the
symmetry reminds me of the classic perl[1]
        $max = [ $x => $y ] -> [ $x <= $y ]

martin

http://groups-beta.google.com/group/comp.lang.perl.misc/msg/d6306b3f92f16a6f

···

William James <w_a_x_man@yahoo.com> wrote:

so it should be

(foo * bar.length).sort.zip(bar * foo.length)

require 'flattenx'
foo.collect { |f| f * bar }.flatten_once

(Given http://www.wobblini.net/ruby/flattenx.html )

···

On Jul 28, 2005, at 8:09 AM, Jim Freeze wrote:

  irb(main):004:0> foo.collect { |f| f * bar }.inject() { |a,e| a.concat e }
  => [["a", 1], ["a", 2], ["a", 3], ["b", 1], ["b", 2], ["b", 3], ["c", 1],
  ["c", 2], ["c", 3]]

There, that's better, but ugly. Anyone got a cleaner way to flatten?