0

I came across a weird behaviour in Ruby that I can't explain.

If we have an array of arrays and wanted to iterated over it with map. I tried to use map with two block variables expecting the second one to be the index, but it instead takes the values of the inner array. Why?

persons = [["john", 28], ["mary", 25],["emma", 30]]
persons.map do |param1, param2|
  puts param1
  puts param2
end

The output is

john
28

So how come that it takes the values of the iterators it should iterate over?

5
  • It works that way. It yields the element and assigns two local variables corresponding to each element within the array. If you need to work with the element index, consider using with_index. Commented Aug 1, 2019 at 16:17
  • 1
    a,b =["john", 28] . Now b == 28 Commented Aug 1, 2019 at 16:19
  • 2
    Referring to @steenslag's comment, it's explained here. Commented Aug 1, 2019 at 17:03
  • 2
    Can you point to the documentation where you found that map yields two values, the second one of which is the index? That is completely wrong, and it would be interesting to know where you found such documentation, so that we can warn future programmers about its in-accuracies and/or contact the responsible person and ask them to correct it. Commented Aug 1, 2019 at 18:32
  • @Jörg I didn't see such documentation, I guess I just assumed it because each_with_index has the second one as the index. I new the destructuring assignment but just forgot about it. It makes total sense however I was in a state of mind where I believed the second argument should be the index Commented Aug 2, 2019 at 17:34

2 Answers 2

3

This is what you are looking for:

persons.map.with_index do |array, index|
  ...
end

You can pass an offset if you want the index to start in 1 instead of 0: with_index(1)

Sign up to request clarification or add additional context in comments.

Comments

1

You're using map but you don't seem to care about the result, so each is more appropriate. Unlike in JavaScript where you may be expecting an index to appear by default, in Ruby you have to ask for it.

If you wanted to display the values you'd do something like this:

persons.each_with_index do |(name, age), index|
  puts '%d. %s (%s)' % [ index + 1, name, age ]
end

Note the use of (name, age) to unpack what is otherwise a pair of values that would be received as an array. This is because each-type methods treat arrays as singular objects. In the default case it'll auto-unpack for you.

If you wanted to transform the values then you'd use map:

persons.map.with_index do |(name, age), index|
  '%d. %s (%s)' % [ index + 1, name, age ]
end

Remember, when using map you must capture as a variable, return it, or somehow use the result or it will get thrown away.

1 Comment

Thanks for the answer, I know the difference between map and each. I just was confused as why map with two block parameters is behaving as such. Reading up on destructuring it makes sense now

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.