我试图比较两个哈希表的结构。我想看看任何给定的哈希表是否是主表的子集。
有效子表的示例:
master_table = {a: String, b: Object, c: { nested_a: Integer, nested_b: Integer} }
my_table = {a: 'cool value', c: {nested_b: 540}
无效子表的示例
my_table = {a: WrongType.new}
或
my_table = {a: 'cool value', new_key: "I don't belong here!"}
编辑:如果它看起来有点像鸭子和嘎嘎声有点像鸭子,那么我会接受它作为鸭子。
我有一个数据驱动的应用程序,用户必须提供定义应用程序行为的配置文件。我想确保用户的配置文件与主配置文件中定义的结构和类型相匹配。
关于Sergio Tulentsev的评论,上面的问题是在无效子表的第一个例子中:a的类型根据主表无效。在第二种情况下,存在主表中不存在的密钥,因此不正确。
答案 0 :(得分:2)
<强>代码强>
def valid?(master_table, my_table)
my_table.all? do |k,v|
case master_table.key?(k)
when true
mv = master_table[k]
case v
when Hash then mv.is_a?(Hash) && valid?(mv, v)
else mv.is_a?(Class) && v.is_a?(mv)
end
else false
end
end
end
<强>实施例强>
master_table = {a: String, b: Object, c: {nested_a: Integer, nested_b: Integer}}
my_table = {a: 'cool value', c: {nested_b: 540}}
valid?(master_table, my_table)
#=> true
my_table = {a: 'cool value', new_key: "I don't belong here!"}
valid?(master_table, my_table)
#=> false
my_table = {a: 'cool value', new_key: "I don't belong here!"}
valid?(master_table, my_table)
#=> false
my_table = {a: 'cool value', c: 42}
valid?(master_table, my_table)
#=> false
master_table = {a: String, b: Object, c: {nested_a: {nested_b:
{nested_c: Integer}}}}
my_table = {a: 'cool value', b: [1,2,3], c: {nested_a: {nested_b:
{nested_c: 42}}}}
valid?(master_table, my_table)
#=> true
my_table = {a: 'cool value', b: [1,2,3], c: {nested_a: {nested_b:
{nested_c: 'cat'}}}}
valid?(master_table, my_table)
#=> false
my_table = {a: 'cool value', b: [1,2,3], c: {nested_a: {nested_b: 42}}}
valid?(master_table, my_table)
#=> false
答案 1 :(得分:1)
好的,我很无聊,所以你走了。猴子修补(从标准库中定义Hash
类的方法)是可选的,你的新作业就是摆脱它。
class Hash
def structural_subset_of?(master)
each_pair do |key, value|
expected_type = master[key]
return false unless expected_type
if value.is_a?(Hash)
return false unless value.structural_subset_of?(master[key])
else
return false unless value.is_a?(expected_type)
end
end
true
end
end
master = {a: String, b: Object, c: { nested_a: Integer, nested_b: Integer} }
valid = {a: 'cool value', c: {nested_b: 540} }
invalid1 = {a: Object.new}
invalid2 = {a: 'cool value', new_key: "I don't belong here!"}
valid.structural_subset_of?(master) # => true
invalid1.structural_subset_of?(master) # => false
invalid2.structural_subset_of?(master) # => false
答案 2 :(得分:1)
除了比较哈希结构之外,您还做了很多其他事情。您以非常具体的方式(子集)比较结构。您还要检查实际值的有效性。要卸载到Hash
,需要付出很多努力。如果您对master_table
有任何影响力,我会提取一些实际对象来为您完成工作。
以下是包含对象的示例解决方案。
我首先创建了一些验证对象:
class KlassValidation
attr_reader :klass
def initialize(klass)
@klass = klass
end
def valid?(hash, key)
return true unless hash.keys.include?(key)
hash[key].is_a? klass
end
end
string_validation = KlassValidation.new(String)
object_validation = KlassValidation.new(Object)
integer_validation = KlassValidation.new(Integer)
class HashValidation
attr_reader :validations
def initialize(validations)
@validations = validations
end
def valid?(hash, key=nil)
hash_to_validate = key ? hash[key] : hash
return true unless hash_to_validate
return false if invalid_keys?(hash_to_validate)
validations.all? { |key, validation| validation.valid?(hash_to_validate, key) }
end
def invalid_keys?(hash)
(hash.keys - validations.keys).any?
end
end
然后master_table
使用这些对象:
master_table = HashValidation.new(a: string_validation, b: object_validation, c: HashValidation.new(nested_a: integer_validation, nested_b: integer_validation))
检查哈希的有效性只需将其传递给valid?
的{{1}}方法。
master_table
这些测试的结果是:
test_cases = [
{ valid: true, value: {a: 'cool value' } },
{ valid: false, value: {bogus: 'cool value' } },
{ valid: false, value: {a: :symbol } },
{ valid: true, value: {a: 'cool value', c: {nested_b: 540} } },
{ valid: false, value: {a: 'cool value', c: {nested_b: :symbol} } },
{ valid: false, value: {a: 'cool value', c: {bogus: :symbol} } }
]
test_cases.each do |test_case|
if test_case[:valid] == master_table.valid?(test_case[:value])
puts "Good!"
else
puts ">>>#{test_case[:value]} was not #{test_case[:valid]}"
end
end
现在,如果您必须以Good!
Good!
Good!
Good!
Good!
Good!
形式在您的问题中以哈希开头,我仍然会使用my_table
来执行验证。在这种情况下,您的问题是将HashValidation
转换为my_table
对象 - 这是一个比您尝试解决的问题更为简单的问题。
HashValidation
使用验证类的一个主要优点是您现在可以轻松扩展您的解决方案。例如,添加一个&#34; required&#34;验证选项,如master_table_orig = {a: String, b: Object, c: { nested_a: Integer, nested_b: Integer} }
def create_hash_validation(hash)
hash.inject({}) do |acc, (key, value)|
acc[key] = if value.is_a?(Hash)
HashValidation.new(create_hash_validation(hash[key]))
else
KlassValidation.new(value)
end
acc
end
end
master_table = HashValidation.new(create_hash_validation(master_table_orig))
。