JP Silvashy
JP Silvashy

Reputation: 48535

ruby for each key/value pair in a hash

I have a bit of a problem with MongoDB in that it returns hashes with the keys in double quotes and integers as floats all the time, has this been a problem for anyone else?

for examples after a map reducing or grouping, say I have a bunch of hashes which look like this:

{"unknown"=>54.0, "pedestrians"=>572.0, "vehicles"=>1045.0}

But what I really want is:

{ unknown: 54, pedestrians: 572, vehicles: 1045 }

Any ideas on how I can easily convert it?

Upvotes: 18

Views: 66391

Answers (2)

DigitalRoss
DigitalRoss

Reputation: 146151

Trust Integral FP Values

In order to handle all possible key types correctly, if you are going to convert it I would suggest something like:

h = {:a => 54.0, :b => 572.0, :c => 1045.0, :d => 'test', :e => 1.23 }
p(h.merge(h) do |k, v|
  v = v.to_i if v.is_a?(Float) && v.to_i == v
  v
end)

The above code will convert Float values in a hash that are actually integral to Integer.

But you actually don't need to do this at all. While it's common to distrust the floating point formats, it turns out that they do represent integral values exactly.

You can trust that any value that was an integer in the database will compare exactly with integer constants (including 0) and that you will not see any rounding artifacts.

You will notice a difference, of course, if you divide a float by something other than a factor.

Upvotes: 3

Russell
Russell

Reputation: 12439

You could do:

original = {"unknown"=>54.0, "pedestrians"=>572.0, "vehicles"=>1045.0}
converted = Hash[ original.map { |key, value| [key.to_sym, value.to_i] } ]

Or if you're using Rails, you could make it a HashWithIndifferentAccess and just convert the values:

original = HashWithIndifferentAccess.new(original)
original.each { |key, value| original[key] = value.to_i }

Upvotes: 36

Related Questions