Tim
Tim

Reputation: 137

Array to Hash with Ruby

I am trying to convert this array:

["dog", 5 , "big house"]

to hash:

{"dog" => 3 , 5 => 25, "big house" => 9}

The value will be the number of characters of the string (key). If it's an integer (key), then the value will be to the power of 2.

This is what I got so far, but it only converts the string (key):

h = {}
arr.each do |x,y|
  y = x.length
  h[x] = y
end

Upvotes: 2

Views: 156

Answers (7)

Arvind Kumar
Arvind Kumar

Reputation: 247

arr= ["dog", 5, "big house"]
two_dimensional_array =  arr.zip(arr.map do |e| 
  case e
  when String then e.length  
  when Integer then e ** 2  
  else raise 'Neither string nor numeric given'  
  end 
end)

my_hash = Hash[*two_dimensional_array.flatten]

Upvotes: 0

Mark Reed
Mark Reed

Reputation: 95385

You can use the Hash[...] constructor to convert an array of [key, value] pairs to a Hash. So here's another option:

arr = ["dog", 5, "big house"]
result = Hash[ arr.map { |e| [e, e.to_i == e ? e**2 : e.length] } ]
# => {"dog"=>3, 5=>25, "big house"=>9}

Since Ruby 2, you can use Array#to_h to do the same thing:

result = arr.map { |e| [e, e.to_i == e ? e**2 : e.length] }.to_h
# => {"dog"=>3, 5=>25, "big house"=>9}

Upvotes: 1

pangpang
pangpang

Reputation: 8831

You can use inject method.

is_a? method can judge the element is String or Integer.

["dog", 2 , "big house"].inject({}) do |sum, e|
  if e.is_a? Integer
    sum.merge({e => e*e})
  elsif e.is_a? String
    sum.merge({e => e.length})
  end
end
=> {"dog"=>3, 2=>4, "big house"=>9}

as @Myst said, h[:key] = value has better performance than merge, so you also can do like this:

["dog", 2 , "big house"].inject({}) do |sum, e|
  if e.is_a? Integer
    sum[e] = e*e
  elsif e.is_a? String
    sum[e] = e.length
  end
  sum
end

Upvotes: 0

Jorge Lopez Jr
Jorge Lopez Jr

Reputation: 257

You could use the Enumberable#each_with_object method like so:

array = ["dog",5,"big house"]
array.each_with_object({}) {|x,hash| x.is_a?(String) ? hash[x] = x.length : hash[x] = x**2}
# => {"dog"=>3,5=>25,"big house"=>9}

The each_with_object method is very similar to the inject method, so it'll iterate through the array, then once completed it'll return the newly given object. The difference between each_with_object and inject is that each_with_object is immutable, so you can't do something like:

(1..5).each_with_object(0) {|num,sum| sum += num}

It'll only return a zero.

Upvotes: 1

Aleksei Matiushkin
Aleksei Matiushkin

Reputation: 121010

▶ arr = ["dog", 5, "big house"]
#⇒ [ "dog", 5, "big house" ]
▶ arr.zip(arr.map do |e| 
▷     case e
▷     when String then e.length  
▷     when Integer then e ** 2  
▷     else raise 'Neither string nor numeric given'  
▷     end  
▷ end).to_h    
#⇒ { "dog" => 3, 5 => 25, "big house" => 9 }

Upvotes: 2

etdev
etdev

Reputation: 534

If you have the following array:

arr = ["dog", 5, "big house"]

First you can create a method to convert elements into your desired format based on the element's class:

def convert_to_hash_val(element)
  case element
  when Fixnum
    element**2
  else
    element.size
  end
end

Then create an array of [element, converted_element] pairs and use the Hash#[] function to convert this to key, value pairs:

Hash[ arr.map {|element| [element, convert_to_hash_val(element)]} ]
# => { "dog" => 3, 5 => 25, "big house" => 9 }

Upvotes: 0

Hyung Cho
Hyung Cho

Reputation: 127

result_hash = {}
arr = ["dog", 2, "big house"]

arr.each do |el|
  if el.is_a?(String)
    result_hash[el] = el.length
  elsif el.is_a?(Fixnum)
    result_hash[el] = el ** 2
  end
end

Remember that the each block called on an array only throws one argument into the block, which is the currently iterated element.

Upvotes: -1

Related Questions