I have a hash that has an unknown collection and mixture of nested arrays, hashes, arrays of hashes and strings. This is the result of JSON.parse. The structure of the data must be the same as it started with. The end goal is to convert strings to Fixnums that could be Fixnums.
The following works just fine, but I was wondering if it could be shortened. Note how I need the key and the value in the clean method as not all strings that can be Fixnums should be. Any ideas?
def clean_node(node)
if node.class == String
clean(node)
elsif node.class == Array
node.each_with_index do |obj, i|
if obj.class == String
node[i] = clean(node[i], obj)
else
clean_node(obj)
end
end
elsif node.class == Hash
node.each_pair do |key, value|
if value.class == String
node[key] = clean(key, value)
else
clean_node(value)
end
end
end
end
def clean(key, value)
FIXNUM_KEYS.include?(key)? value.to_i : value
end
There is no need to split up the string processing, you can do it all in one recursive routine, if you return the value of node at the end of the routine. This has the side effect of removing some parameters, and you can use in-place .map! for handling arrays.
Using case makes the selection by type slightly easier to read.
Adding the ability to deal with arrays of strings could be done in lots of ways. I've chosen to add a state variable (a common pattern in recursion, for instance to count depth). Then in the case of arrays, the recursion inherits the state from the current level, whilst the hash case determines new state (to convert or not) based on lookup in the list of keys to apply conversions.
def clean_node( node, convert_item = false )
case node
when String
node = node.to_i if convert_item
when Array
node.map! do |obj|
clean_node( obj, convert_item )
end
when Hash
node.each_pair do |key, value|
node[key] = clean_node( value, FIXNUM_KEYS.include?(key) )
end
end
node
end
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With