Search code examples
arraysrubyruby-hash

Creating a hash automatically


I have an array ( array = ["home", "style", "honor", "home" ] and what I want to do is create a hash from this array like this :

hash = { "home" => 1, "style" => 2, "honor" => 3} #Array has two "home" element but I should give one value ("home" => 1) to both.

But I'm wondering is there any easy way to do this? Such as if i = 1 and "home" => 1 and then i will be 2, following "style" => 2. Should I use loops, enmerables ? Or is there better way?


Solution

  • The first thing we need to do is remove the duplicate elements from the Array. For that, we can use the Enumerable#uniq method:

    array.uniq
    #=> ['home', 'style', 'honor']
    

    Next, we need to pair up every element with its index. For that, we can use the Enumerable#each_with_index method, which gives us an Enumerator that yields two-element arrays of the original element and its index:

    array.uniq.each_with_index
    #=> #<Enumerator: ['home', 'style', 'honor']:each_with_index>
    

    We can take a peek at what happens when we use the Enumerator by converting it to an Array:

    array.uniq.each_with_index.to_a
    #=> [['home', 0], ['style', 1], ['honor', 2]]
    

    The indices are off-by-one, but we could fix that using Enumerable#map:

    array.uniq.each_with_index.map {|el, i| [el, i.succ] }
    #=> [['home', 1], ['style', 2], ['honor', 3]]
    

    Alternatively, we could fix it later, after we create the Hash by using the Hash#transform_values method.

    Another possibility is to convert the Array to an Enumerator first by using Array#each and then use the Enumerator#with_index method which takes an optional offset argument:

    array.uniq.each.with_index(1)
    #=> #<Enumerator: #<Enumerator: ['home', 'style', 'honor']:each>:with_index(1)>
    
    array.uniq.each.with_index(1).to_a
    #=> [['home', 1], ['style', 2], ['honor', 3]]
    

    In order to create the Hash, all we need to do, is use the Enumerable#to_h method to convert the two-element "pairs" we already have into key-value pairs. Enumerable#to_h also takes an optional block, which is another possible way for us to transform the indices.

    array.uniq.each.with_index(1).to_h
    array.uniq.each_with_index.to_h {|el, i| [el, i.succ]}
    array.uniq.each_with_index.to_h.transform_values(&:succ)
    #=> { 'home' => 1, 'style' => 2, 'honor' => 3 }