Reputation: 387
I need the next hash:
x = {
params: {
user_params1: { name: "stephen", dir: "2001", dir2: nil },
user_params2: { name: "josh", dir: "jhon", dir2: nil }
}
to return a new hash of arrays like this:
x = {
params: {
user_params1: ["stephen","201", ""],
user_params2: ["josh","jhon",""]
}
Upvotes: 2
Views: 1362
Reputation: 20263
Given:
x = {
params: {
user_params1: { name: "stephen", dir: "2001", dir2: nil },
user_params2: { name: "josh", dir: "jhon", dir2: nil }
}
}
Try:
x[:params] = x[:params].each_with_object({}) do |(k,v), returning|
returning[k] = v.map{|k,v| v}
end
Which will yield:
{:params=>{:user_params1=>["stephen", "2001", nil], :user_params2=>["josh", "jhon", nil]}}
If you want empty strings instead of nils (as in your example), do:
x[:params] = x[:params].each_with_object({}) do |(k,v), returning|
returning[k] = v.map{|k,v| v.to_s}
end
If you don't want to modify x
, then just create a new hash
and do the same:
y ={}
y[:params] = x[:params].each_with_object({}) do |(k,v), returning|
returning[k] = v.map{|k,v| v.to_s}
end
Since you're not doing anything with that k
in v.map
, you could just do v.values.map(&:to_s)
(stolen shamelessly from Gerry's answer) - which is cleaner, IMO, but costs you one extra character(!) - and end up with:
y ={}
y[:params] = x[:params].each_with_object({}) do |(k,v), returning|
returning[k] = v.values.map(&:to_s)
end
As Sebastian points out, there is syntactic sugar for this:
y[:params] = x[:params].transform_values do |value|
# Then use one of:
# hash.values.map { |value| value.nil? ? '' : value }
# hash.values.map { |value| value ? value : '' }
# hash.values.map { |value| value || '' }
# hash.values.map(&:to_s)
end
Interestingly, if you look at the source code,
you'll see that the each_with_object
and tranform_values
mechanics are quite similar:
def transform_values
return enum_for(:transform_values) unless block_given?
result = self.class.new
each do |key, value|
result[key] = yield(value)
end
result
end
You could imagine this re-written as:
def transform_values
return enum_for(:transform_values) unless block_given?
each_with_object(self.class.new) do |(key, value), result|
result[key] = yield(value)
end
end
Which, at its root (IMO), is pretty much what Gerry and I came up with.
Seems to me this cat is well-skinned.
Upvotes: 1
Reputation: 10497
You use each_with_object
(twice in case you have more thane one key on the top level); for example:
x.each_with_object({}) do |(k, v), result|
result[k] = v.each_with_object({}) do |(k1, v1), result1|
result1[k1] = v1.values.map(&:to_s)
end
end
#=> {:params=>{:user_params1=>["stephen", "2001", ""], :user_params2=>["josh", "jhon", ""]}}
Upvotes: 0