0

I have an array of hashes:

@operating_systems = [
  {"title"=>"iPhone", "value_percent"=>"42.6"},
  {"title"=>"Windows 7", "value_percent"=>"21.3"},
  {"title"=>"Android", "value_percent"=>"12.8"},
  {"title"=>"Mac OS X", "value_percent"=>"8.5"},
  {"title"=>"Windows 8.1", "value_percent"=>"6.4"},
  {"title"=>"Windows XP", "value_percent"=>"4.3"},
  {"title"=>"Linux", "value_percent"=>"2.1"},
  {"title"=>"Windows Vista", "value_percent"=>"2.1"}
] 

and want to create the following hash:

 {"iphone" => "42.6", "windows 7" => "21.3", ... "windows vista" => "2.1"}

What is the best way to accomplish this?

3
  • 1
    Write the logic behind the conversion. Don't let the reader guess. As of now, none of the given answers got your intention correctly, and that is because you have not made it clear. Commented May 16, 2014 at 23:35
  • 1
    I'm a bit confused by your comment. All three answers correctly solve my question, so it would appear that they indeed understand the intention, no? Commented May 16, 2014 at 23:39
  • 1
    @dmt2989 I don't understand your mind. All the three answers given at the time of my comment above give different result from what you wanted. Commented May 16, 2014 at 23:45

5 Answers 5

6
[
  {"title"=>"iPhone", "value_percent"=>"42.6"},
  {"title"=>"Windows 7", "value_percent"=>"21.3"},
  {"title"=>"Android", "value_percent"=>"12.8"},
  {"title"=>"Mac OS X", "value_percent"=>"8.5"},
  {"title"=>"Windows 8.1", "value_percent"=>"6.4"},
  {"title"=>"Windows XP", "value_percent"=>"4.3"},
  {"title"=>"Linux", "value_percent"=>"2.1"},
  {"title"=>"Windows Vista", "value_percent"=>"2.1"}
]
.map{|h| h.values.map(&:downcase)}.to_h
# =>
# {
#   "iphone"=>"42.6",
#   "windows 7"=>"21.3",
#   "android"=>"12.8",
#   "mac os x"=>"8.5",
#   "windows 8.1"=>"6.4",
#   "windows xp"=>"4.3",
#   "linux"=>"2.1",
#   "windows vista"=>"2.1"
# }
Sign up to request clarification or add additional context in comments.

1 Comment

I don't see how to improve on this. +1.
2

Here's one way

 op_sys =  [{"title"=>"iPhone", "value_percent"=>"42.6"}, {"title"=>"Windows 7", "value_percent"=>"21.3"}, {"title"=>"Android", "value_percent"=>"12.8"}, {"title"=>"Mac OS X", "value_percent"=>"8.5"}, {"title"=>"Windows 8.1", "value_percent"=>"6.4"}, {"title"=>"Windows XP", "value_percent"=>"4.3"}, {"title"=>"Linux", "value_percent"=>"2.1"}, {"title"=>"Windows Vista", "value_percent"=>"2.1"}] 

    new_hash = op_sys.inject({}) {|r,e| r[e['title']] = e['value_percent']; r}

    p new_hash

EDIT

You may have wanted the new hash keys to be in downcase... so an alternative:

new_hash = op_sys.inject({}) {|r,e| r[e['title'].downcase] = e['value_percent']; r}

11 Comments

(shrug) the Ruby style guide disagrees... "Rely on the fact that as of Ruby 1.9 hashes are ordered." However I take your point that explicitly examining for "title" and "value_percent" keys are more robust. I'll rework the answer.
I've been bit by relying on the insertion order before (when dealing with outside data like JSON), it hurt :|
@user2864740 Are you sure this is correct? It doesn't look so to me.
@sawa It is semantically correct (ideone.com/8xukR8), although the variable names are a bit confusing as r is the accumulator hash and e is each item (hash) in the original array ..
each_with_object might be a little nicer than inject in this case, that would let you get rid of the ;r at the end of your block.
|
1

@Sawa came the closest to what I'd do:

operating_systems = [
  {"title"=>"iPhone", "value_percent"=>"42.6"},
  {"title"=>"Windows 7", "value_percent"=>"21.3"},
  {"title"=>"Android", "value_percent"=>"12.8"},
  {"title"=>"Mac OS X", "value_percent"=>"8.5"},
  {"title"=>"Windows 8.1", "value_percent"=>"6.4"},
  {"title"=>"Windows XP", "value_percent"=>"4.3"},
  {"title"=>"Linux", "value_percent"=>"2.1"},
  {"title"=>"Windows Vista", "value_percent"=>"2.1"}
] 
operating_systems.map(&:values).to_h 
# => {"iPhone"=>"42.6",
#     "Windows 7"=>"21.3",
#     "Android"=>"12.8",
#     "Mac OS X"=>"8.5",
#     "Windows 8.1"=>"6.4",
#     "Windows XP"=>"4.3",
#     "Linux"=>"2.1",
#     "Windows Vista"=>"2.1"}

Which works on Ruby 2.1+.

Or, on older versions of Ruby:

Hash[operating_systems.map(&:values)] 
# => {"iPhone"=>"42.6",
#     "Windows 7"=>"21.3",
#     "Android"=>"12.8",
#     "Mac OS X"=>"8.5",
#     "Windows 8.1"=>"6.4",
#     "Windows XP"=>"4.3",
#     "Linux"=>"2.1",
#     "Windows Vista"=>"2.1"}

If folding the keys to lowercase is needed use these to replace the above commands:

operating_systems.map{ |h| k, v = h.values; [k.downcase, v] }.to_h
# => {"iphone"=>"42.6",
#     "windows 7"=>"21.3",
#     "android"=>"12.8",
#     "mac os x"=>"8.5",
#     "windows 8.1"=>"6.4",
#     "windows xp"=>"4.3",
#     "linux"=>"2.1",
#     "windows vista"=>"2.1"}

Hash[operating_systems.map{ |h| k, v = h.values; [k.downcase, v] }]
# => {"iphone"=>"42.6",
#     "windows 7"=>"21.3",
#     "android"=>"12.8",
#     "mac os x"=>"8.5",
#     "windows 8.1"=>"6.4",
#     "windows xp"=>"4.3",
#     "linux"=>"2.1",
#     "windows vista"=>"2.1"}

Comments

0

Combine Enumerable#reduce with Hash#merge

op_sys.reduce({}) { |hsh, itx| hsh.merge({ itx["title"] => itx["value_percent"]}) }
 => {"iPhone"=>"42.6", "Windows 7"=>"21.3", "Android"=>"12.8", "Mac OS X"=>"8.5", "Windows 8.1"=>"6.4", "Windows XP"=>"4.3", "Linux"=>"2.1", "Windows Vista"=>"2.1"}

Comments

0

While there are a number of more clever approaches, a simple loop over the source array with modification of the output hash will suffice; consider

arr = [{"title"=>"iPhone", "value_percent"=>"42.6"}, ..]

hash = {}
arr.each do |item|
   key = item["title"]
   value = item["value_percent"]
   hash[key] = value
end   

and see the ideone demo. This is a very easy task, and I hope that how it was "solved" is made readily apparent above - there is no "best" until there is "working".


Once the basic approach is understood, the same process that was done (a transformation and then building a map from pairs) can be done as so

hash = Hash[arr.map do |v| [v["title"], v["value_percent"]] end]

Is this "better"? shrug, I guess it's "shorter" and still clear.. (and any additional transformation can be trivially added)

7 Comments

@dmt2989 Then the arr variable (or whatever is substituting it in your code) evaluates to nil and it ends up as nil.each do .. which results in the descriptive exception.
yea, that is odd, because I can display @arr in my view, and it's [{"title"=>"iPhone", "value_percent"=>"42.6"}, ..]
@dmt2989 The error message is telling the truth. Either @arr hasn't been set to a non-nil value yet (or has been reset?) or that code is running in a different scope.
My mistake- this also worked, but a different error was confounding the problem. Appreciate the help.
@dmt2989 Glad the problem is solved! Make sure to write/use code that you (and others) can understand later.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.