2

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
6
  • "the power of 2" --Do you mean the second power? Commented Sep 25, 2015 at 1:47
  • 1
    What is your question? Commented Sep 25, 2015 at 1:48
  • 2
    How do you get from 2 to 5 => 25? Commented Sep 25, 2015 at 1:51
  • 1
    @pangpang Don't change the question. Commented Sep 25, 2015 at 1:54
  • 1
    @pangpang You may, but you shouldn't do it. Only the OP should do that. Commented Sep 25, 2015 at 2:06

7 Answers 7

2
â–¶ 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 }
Sign up to request clarification or add additional context in comments.

Comments

1

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.

Comments

1

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}

Comments

0

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 }

Comments

0

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

8 Comments

Thanks for the input. is_a? is what i was looking for
This code will result in parsing error. Consider changing else to elsif.
@mudasobwa I tried it with Ruby v2.2.2 and it worked fine. Something must've changed, as I'd expect it to error as well.
@iain I was wrong about an error. Just else condition won’t be treated as condition. Try if false ⏎ puts '' ⏎ else puts 'inside else' ⏎ puts 'another inside' ⏎ end.
You are creating too many objects with this code. i.e. sum.merge({e => e.length}) creates a Hash only to merge it with sum. This means that you are creating atlas one unnecessary Hash object with every iteration. You should use sum[e] = e.length. I'm sorry for downgrading your answer for such a reason, but this answer might propagate bad practices.
|
0
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]

Comments

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

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.