Search code examples
rubysplat

Meaning of * in argument position


Assume arr is an array [[1,3],[2,5],[3,8]]. I was wondering what * means. What does it mean?

hash = Hash[*arr.flatten] # => {1=>3, 2=>5, 3=>8}

I tried the following

arr.flatten # => [1, 3, 2, 5, 3, 8]
Hash[arr.flatten] # => {}
Hash[*a.flatten] # => {1=>3, 2=>5, 3=>8}

Solution

  • Here is the explanation

    When you do array.flatten it will give you one flatten array where all inner array slatted. Now You are putting this flatten array inside the Hash::[] method. But Hash::[] supports the below constructs :

    Hash[ key, value, ... ] → new_hash 
    # or
    Hash[ [ [key, value], ... ] ] → new_hash
    

    Now array.flatten gives you [1, 3, 2, 5, 3, 8]. Now you are putting this array inside Hash[] like Hash[[1, 3, 2, 5, 3, 8]]. Now compare with the above 2 structures. Does either of them match ? NO. So you need to splat the inner array again, thus it need (splatted operator)* to splat the inner array.

    Now you did Hash[*[1, 3, 2, 5, 3, 8]], which gives Hash[1, 3, 2, 5, 3, 8]. Now again check from the above 2 constructs. Does it match with either of the 2 ? This time YES, first one. So you got the desired hash {1=>3, 2=>5, 3=>8}.

    BTW, you don't need to splat, as the second construt exactly matched when you put array inside Hash::[] directly.

    array = [[1,3],[2,5],[3,8]]
    Hash[array] # => {1=>3, 2=>5, 3=>8}
    

    The above worked, because Hash[array] means Hash[[[1,3],[2,5],[3,8]]], which exactly the second structure as documentation suggested.

    Read some examples to see how splatting work in Ruby.

    There is another construct :-

     Hash[ object ] → new_hash
    

    This I think is also important to tell you why you got {}. Hash[[1, 3, 2, 5, 3, 8]] same as the last type of construct as per the doc. The doc is saying -

    The second and third form take a single argument which is either an array of key-value pairs or an object convertible to a hash.

    So. [1,3,2,5,3,8] it is an Array object not convertible to Hash. Currently it is giving you an empty hash, if an object as per the third construct. But it will throw error in future version of release. See Below warnings.

    [1] pry(main)> Hash[[1,2]]
    (pry):1: warning: wrong element type Fixnum at 0 (expected array)
    (pry):1: warning: ignoring wrong elements is deprecated, remove them explicitly
    (pry):1: warning: this causes ArgumentError in the next release
    (pry):1: warning: wrong element type Fixnum at 1 (expected array)
    (pry):1: warning: ignoring wrong elements is deprecated, remove them explicitly
    (pry):1: warning: this causes ArgumentError in the next release
    => {}
    

    My Ruby version is :

    ruby 2.0.0p451 (2014-02-24 revision 45167) [i686-linux]