2

I have two arrays:

a = [7, 1, 65, 4, 13, 97]
b = []

and I'm trying to append the #divmod return values of each of a's elements into b, via the following code:

b << a.map(&4.method(:divmod))

I was expecting the results to be:

b = [[1, 3], [0, 1], [16, 1], [1, 0], [3, 1], [24, 1]]

But instead I get:

b = [[[0, 4], [4, 0], [0, 4], [1, 0], [0, 4], [0, 4]]]

And I don't understand why. Can anyone explain what I'm not understanding?

(I'm having to use &: because I need to use this code as a method block argument.)

jbk
  • 1,911
  • 19
  • 36

1 Answers1

5

There are two problems with the result you are getting:

  1. the array you get has an extra layer of nesting
  2. the (inner) values are not as expected

To fix (1) you have to assign the result from map instead of appending the result, i.e.

b = a.map(...)

Regarding (2), your code is equivalent to:

a.map { |n| 4.divmod(n) }
#=> [[0, 4], [4, 0], [0, 4], [1, 0], [0, 4], [0, 4]]

whereas you want: (note that n and 4 are swapped now)

a.map { |n| n.divmod(4) }
#=> [[1, 3], [0, 1], [16, 1], [1, 0], [3, 1], [24, 1]]

I'm having to use &: because I need to use this code as a method argument

I'm not sure what you mean by that, but if you don't want to have an explicit block argument, you can pass a proc or lambda with & instead:

divmod4 = ->(n) { n.divmod(4) }

b = a.map(&divmod4)
#=> [[1, 3], [0, 1], [16, 1], [1, 0], [3, 1], [24, 1]]

However, since &divmod4 will convert the proc to a block argument, the above is equivalent to calling a.map { |n| n.divmod(4) } directly. You would only need this approach if you want to re-use that proc, e.g. for passing it to different methods.

Stefan
  • 109,145
  • 14
  • 143
  • 218
  • Thanks @stefan for your clear and helpful explanation. FYI what I meant to say at the end of my post, now updated accordingly, was that I'm trying to pass this code in as a block argument to another method. To be specific what I'm trying to do is pass this `a.map { |n| n.divmod(4)}` into a binary tree traversal method that I've written which accepts an optional block argument. So this is why I'm trying to use the `&:` strategy to pass in (the #divmod) method, but that method requires an argument, thus the `&4.method(:divmod)` syntax, except that I want to swap the receiver and the 4. – jbk Apr 26 '23 at 11:26
  • 1
    @jbk using `&` to convert a proc or method to a block argument or using the block syntax (`{ .. }` or `do ... end`) directly isn't any different. Both pass a block argument to the method. You can use one or the other. – Stefan Apr 26 '23 at 13:41
  • Thanks @stefan, I'm realising that now. I was just trying to get around the error `(&:method(arg))` gives as it seems that it doesn't allow the passed in method arg to take in its own arg. Courtesy of your help I now create a lambda; `divmod4 = ->(n) { n.divmod(4) }` and pass it in like so `tree.level_order(&divmod4)` which works well , though I realise that don't need to reuse the code, so I could just pass an explicit block anyway . A good exercise though for learning about Procs, method arguments etc. Thanks for the guidance. – jbk Apr 26 '23 at 13:57
  • 1
    @jbk BTW (since you said `&:` twice): note that `&:method` is *not* `&:` + `method`. It’s `&` + `:method` with the latter being a plain symbol. (and you can’t of course pass arguments to symbols) – Stefan Apr 26 '23 at 18:07
  • Of course, that makes sense now @stefan. I think I now see that the `&` syntax just prompts the immediately following `:symbol` to receive the Symbol#to_proc method(?). – jbk Apr 26 '23 at 18:31