using this Hash object
{"foo" => {"bar" => 1, "baz" => 2}, "bla" => [1,2,3]}
I want to produce this array of Hash objects
[
{"foo" => "*", "bla" => [1,2,3]},
{"foo" => {"bar" => "*", "baz" => 2}, "bla" => [1,2,3]},
{"foo" => {"bar" => "1", "baz" => "*"}, "bla" => [1,2,3]},
{"foo" => {"bar" => "*", "baz" => 2}, "bla" => "*"},
]
Where I basically went over each key and changed its value to "*" while preserving the overall structure of the hash and saved the new hash produced in some array.
I have tried many ideas, but most just wont work as I can guess the Array type before, I only know this hash is produced by JSON.parse and then changed into Hash(String, JSON::Any)
My current try at it
hash = {"bar" => {"and" => "2", "br" => "1"}}
arr = [hash, {"bar" => "1"}]
arr.delete(arr.last)
arr.delete(hash)
def changer(hash, arr, original = nil)
original = hash.dup
hash.each do |k, v|
if v.is_a?(Hash)
changer(v, arr, hash)
elsif v.is_a?(Array)
v.each do |a|
if a.is_a?(Hash)
changer(a, arr, hash)
end
end
elsif v.is_a?(String) && original.is_a?(Hash(String, String))
original[k.to_s] = "*"
arr << original
end
end
end
Crystal v0.25.0 implements JSON::Any and YAML::Any without recursive aliases. With that change:
require "json"
hash = JSON.parse(%(
{"foo": {"bar": 1, "baz": 2}, "bla": [1,2,3]}
))
def changer(any : JSON::Any)
result = [JSON::Any.new("*")]
if (hash = any.as_h?)
hash.each do |key, value|
changer(value).each do |s|
result << JSON::Any.new(hash.merge({key => s}))
end
end
end
result
end
puts changer(hash).join("\n")
*
{"foo" => "*", "bla" => [1_i64, 2_i64, 3_i64]}
{"foo" => {"bar" => "*", "baz" => 2_i64}, "bla" => [1_i64, 2_i64, 3_i64]}
{"foo" => {"bar" => 1_i64, "baz" => "*"}, "bla" => [1_i64, 2_i64, 3_i64]}
{"foo" => {"bar" => 1_i64, "baz" => 2_i64}, "bla" => "*"}