# Manipulate Hash to typecast true/false for certain values

is there a better why how can I refactor this code, making sure that values in a Hash are typecasted to true/false if their value is '1' or '0' while leaving unaltered the rest?

I'm using Ruby 2.0.0 if that matters, and I'd like to improve this code.

  def transform
hsh = {}
preferences.each do |k, v|
v = case v
when '1'
true
when '0'
false
else
v
end
hsh[k.to_sym] = v
end
hsh
end


updated with benchmarks

All right, here's the performance test based on the replies so far:

class Test
HSH = {"xxx"=>"xxx-rrr", "yyy"=>"0", "rrr"=>"1", "nnn"=>"0", "kkk"=>"1", "iii"=>"1", "lll"=>"default", "mmm"=>"76", "www"=>"1"}

def self.transform_case
hsh = {}
HSH.each do |k, v|
v = case v
when '1'
true
when '0'
false
else
v
end
hsh[k.to_sym] = v
end
hsh
end

def self.transform_ternary
Hash[ HSH.map { |k, v| [k.to_sym, v == '1' ? true : v == '2' ? false : v ] } ]
end

def self.transform_fetch
special_values = {"1" => true, "0" => false}
Hash[HSH.map { |k, v| [k.to_sym, special_values.fetch(v, v)] }]
end

def self.transform_negation
Hash[ preferences.map {|k,v| [k.to_sym, !!v]} ]
end
end

Benchmark.bm(20) do|b|

b.report('case') do
1500.times { Test.transform_case }
end

b.report('ternary') do
1500.times { Test.transform_ternary }
end

b.report('fetch') do
1500.times { Test.transform_fetch }
end

b.report('negation') do
1500.times { Test.transform_negation }
end
end

user     system      total        real
case                   0.010000   0.000000   0.010000 (  0.009282)
ternary                0.010000   0.000000   0.010000 (  0.013794)
fetch                  0.010000   0.000000   0.010000 (  0.012804)
negation               0.010000   0.000000   0.010000 (  0.010760)


It appears that my original implementation is faster. Or is the BM test wrong?

• don't select an answer so soon! leave time for others to answer. – tokland Jun 3 '13 at 14:27
• Oh ok, thanks :) I'm going to do some benchmarks between the replies before selecting then. Thanks! – John Smith Jun 3 '13 at 14:38
• I agree with @tokland that this kind of benchmark is not quite relevant : as your current code works, the question you asked was implicitly about expressiveness. And then : 1) your HSH should be a lot longer for your benchmark to be representative (possible differences in big'O) 2) in transform_fetch you create a special_values hash every time the method is called, which is not necessary and hurts performance – m_x Jun 4 '13 at 7:25

## 2 Answers

Code should be as declarative as possible (usually by using functional style):

def transform
special_values = {"1" => true, "0" => false}
Hash[preferences.map { |k, v| [k.to_sym, special_values.fetch(v, v)] }]
end


However, Hash[...] is very ugly and I prefer a more OOP approach with Enumerable#mash, so I'd really write preferences.mash { |k, v| [k.to_sym, special_values.fetch(v, v)] }.

• Added benchmarks to the original post – John Smith Jun 3 '13 at 14:45
• @John: A benchmark of this kind of code is not relevant. Go for the most declarative code. – tokland Jun 3 '13 at 15:17

You can do this:

Hash[ preferences.map {|k,v| [k.to_sym, v.nonzero? or false]} ]


This preserves the value if it is nonzero, otherwise makes it false. It does not change a 1 to true though. I like @tokland's answer best, because it not only does the exact transformation you want, but makes it easy to extend and use as a general purpose transformer (if you ever need to change other values as well), which one might assume is your purpose for this method, based on the name.

Code review

Note that when you initialize a return variable and then loop over another variable, e.g.

hsh = {}
preferences.each do
#something that updates hsh
end


A more idiomatic Ruby way to do this is with a map, as the answers show. This will in general result in more concise, expressive code.