Search code examples
rubymethodsdivmod

What is it that this #divmod method is doing to output this result?


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.)


Solution

  • 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.