Forum: Ruby How does Array.map work for 2D arrays?

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
B7a15848864437bab1ce7f240b536c66?d=identicon&s=25 Karthik Nar (pearl8)
on 2007-01-14 11:21
I was reading this example:

pt = [ [ "Check", "check" ], [ "Credit card", "cc" ], [ "Purchase
order", "po" ] ]
pt.map  {|value| value}

Displays the 2D Array elements as expected => [["Check", "check"],
["Credit card", "cc"], ["Purchase order", "po"]]

but a two-parameter block suppled to map like below:

>> pt.map  {|disp, value| value}
=> ["check", "cc", "po"]

is able to give me the second element of the 2D array.

i looked in the ruby doc and on the net for examples, but 2D arrays are
typically not covered.

while, i love this functionality, my question is -

How does this 2 parameter variant really work?

How does the array.map "figure out" and pass disp to param1 and value to
param2?
42292bf8a0acbc98862d9306b501275d?d=identicon&s=25 Daniel Waite (rabbitcreative)
on 2007-01-15 09:32
Karthik Nar wrote:
> ... a two-parameter block suppled to map like below:
>
>>> pt.map  {|disp, value| value}
> => ["check", "cc", "po"]
>
> is able to give me the second element of the 2D array.
>
> while, i love this functionality, my question is -
>
> How does this 2 parameter variant really work?
>
> How does the array.map "figure out" and pass disp to param1 and value to
> param2?

Two points. First, I don't know how it figures it out. Second, your
question can be "answered" by looking at another example...

a = [ [ 'billy', 'goat', 'cheese' ], [ 'silly', 'hats' ] ]

a.map { |x, y, z| z } # => ["cheese", nil]

Observation tells us that map isn't really figuring out that you have a
2D array, it's simply assuming that if you're asking for two arguments
(or three or one-hundred), that your array has them.

Make sense?
B7a15848864437bab1ce7f240b536c66?d=identicon&s=25 Karthik Nar (pearl8)
on 2007-01-15 10:37
yes makes sense!!

here's some experiments i did, and it's more of a ruby thing than a map
thing!

irb(main):001:0>  a = 1, 2
=> [1, 2]

irb(main):002:0> def map(a) yield a end
=> nil

irb(main):003:0> map(a) { | x, y | p y }
2
=> nil

irb(main):004:0> map(a) {| x | p x }
[1, 2]
=> nil


> Two points. First, I don't know how it figures it out. Second, your
> question can be "answered" by looking at another example...
>
> a = [ [ 'billy', 'goat', 'cheese' ], [ 'silly', 'hats' ] ]
>
> a.map { |x, y, z| z } # => ["cheese", nil]
>
> Observation tells us that map isn't really figuring out that you have a
> 2D array, it's simply assuming that if you're asking for two arguments
> (or three or one-hundred), that your array has them.
>
> Make sense?
E0d864d9677f3c1482a20152b7cac0e2?d=identicon&s=25 Robert Klemme (Guest)
on 2007-01-19 16:30
(Received via mailing list)
On 14.01.2007 11:21, Karthik Nar wrote:
>
> How does this 2 parameter variant really work?
>
> How does the array.map "figure out" and pass disp to param1 and value to
> param2?

It doesn't really have to do anything at all:

irb(main):001:0> def foo() yield 1,2 end
=> nil
irb(main):002:0> foo {|a| p a}
(irb):2: warning: multiple values for a block parameter (2 for 1)
         from (irb):1
[1, 2]
=> nil
irb(main):003:0> foo {|*a| p a}
[1, 2]
=> nil
irb(main):004:0> foo {|a,b| p a}
1
=> nil
irb(main):005:0> def foo() yield [1,2] end
=> nil
irb(main):006:0> foo {|a| p a}
[1, 2]
=> nil
irb(main):007:0> foo {|*a| p a}
[[1, 2]]
=> nil
irb(main):008:0> foo {|a,b| p a}
1
=> nil

You see in line 6 and 8 how it works.

However, map could also evaluate the block's arity:

irb(main):009:0> def foo(&b) p b.arity; b[1,2] end
=> nil
irb(main):010:0> foo {|a| p a}
1
(irb):10: warning: multiple values for a block parameter (2 for 1)
         from (irb):9
[1, 2]
=> nil
irb(main):011:0> foo {|a,b| p a}
2
1
=> nil
irb(main):012:0> foo {|*a| p a}
-1
[1, 2]
=> nil
irb(main):013:0> def foo(&b) p b.arity; b[[1,2]] end
=> nil
irb(main):014:0> foo {|a| p a}
1
[1, 2]
=> nil
irb(main):015:0> foo {|a,b| p a}
2
1
=> nil
irb(main):016:0> foo {|*a| p a}
-1
[[1, 2]]
=> nil

HTH

  robert
B7a15848864437bab1ce7f240b536c66?d=identicon&s=25 Karthik Nar (pearl8)
on 2007-09-25 22:33
(Received via mailing list)
wow, robert, that makes a lot of sense Thanks !

now my next question: how would a newbie (like me) know by looking at
the ruby doc that Array.map could actually support 2D arrays in this
fashion?

(or is it that it just becomes "obvious" as one uses Ruby more and
more?!)
35b0b4029fd4387842ec88a8e99d84de?d=identicon&s=25 Jason Mayer (slamboy)
on 2007-09-25 22:38
(Received via mailing list)
On 1/14/07, Robert Klemme <shortcutter@googlemail.com> wrote:
> So, an Array is automatically assigned element wise you do not need to
> pattern matching:
> => [1, [2, 3]]
> irb(main):001:0> h={}
> This is of course a silly example.  I chose these values in order to
> make checking what's going on easier (i.e. summing all keys and values
> equals h.size * 10).
>
> Kind regards
>
>        robert


Just wanted to throw in another set of thanks(late, I know); that
response
very clearly answered a question I had regarding map.
E0d864d9677f3c1482a20152b7cac0e2?d=identicon&s=25 Robert Klemme (Guest)
on 2007-09-25 22:40
(Received via mailing list)
On 14.01.2007 12:49, karthik wrote:
> wow, robert, that makes a lot of sense Thanks !

You're welcome!

> now my next question: how would a newbie (like me) know by looking at
> the ruby doc that Array.map could actually support 2D arrays in this
> fashion?
>
> (or is it that it just becomes "obvious" as one uses Ruby more and
> more?!)

I guess the latter.  What you see is basically the effect of a
combination of features: You're actually not dealing with a 2D Array but
with an Array that contains Arrays, or more generally, an Enumerable
that contains Enumerables.  A true 2D Array would make sure that every
row has the same number of elements and similarly for columns, that's
why it's not a 2D Array.  The other aspect is how yield and assignments
work.  There is some automatic, err, how would you call that, unrolling?

irb(main):001:0> a,b=1,2
=> [1, 2]
irb(main):002:0> a
=> 1
irb(main):003:0> b
=> 2
irb(main):004:0> a,b=[1,2]
=> [1, 2]
irb(main):005:0> a
=> 1
irb(main):006:0> b
=> 2

So, an Array is automatically assigned element wise you do not need to
explicitly provide the splat operator:

irb(main):007:0> a,b=*[1,2]
=> [1, 2]
irb(main):008:0> a
=> 1
irb(main):009:0> b
=> 2

You can do a lot more fancy stuff with this as Ruby actually does
pattern matching:

irb(main):001:0> a,b,c=1,[2,3]
=> [1, [2, 3]]
irb(main):002:0> a
=> 1
irb(main):003:0> b
=> [2, 3]
irb(main):004:0> c
=> nil
irb(main):005:0> a,(b,c)=1,[2,3]
=> [1, [2, 3]]
irb(main):006:0> a
=> 1
irb(main):007:0> b
=> 2
irb(main):008:0> c
=> 3

This is often useful when using #inject on a Hash:

$ irb
irb(main):001:0> h={}
=> {}
irb(main):002:0> 10.times { i=rand(10); h[i]=10-i}
=> 10
irb(main):003:0> h
=> {0=>10, 6=>4, 7=>3, 2=>8, 8=>2, 3=>7, 9=>1, 4=>6}
irb(main):004:0> h.size
=> 8
irb(main):005:0> h.inject(0) {|sum,(key,val)| sum + key + val}
=> 80

This is of course a silly example.  I chose these values in order to
make checking what's going on easier (i.e. summing all keys and values
equals h.size * 10).

Kind regards

  robert
This topic is locked and can not be replied to.