Ruby 如何比较散列中的许多值
我有一个散列,看起来像下面的一个。我想将所有“之前”值与其对应的“之后”值进行比较。当所有代码都相同时,我执行一些代码。。目前我用IF语句来做,这很难看。我通常怎么做Ruby 如何比较散列中的许多值,ruby,Ruby,我有一个散列,看起来像下面的一个。我想将所有“之前”值与其对应的“之后”值进行比较。当所有代码都相同时,我执行一些代码。。目前我用IF语句来做,这很难看。我通常怎么做 hash = { "id_BEFORE"=>10825, "name_BEFORE"=>"management", "prefix_BEFORE"=>"10.70.230.0/24", "created_at_BEFORE"=>Thu, 10 Oct 2019 12:07:23 UTC +
hash = {
"id_BEFORE"=>10825,
"name_BEFORE"=>"management",
"prefix_BEFORE"=>"10.70.230.0/24",
"created_at_BEFORE"=>Thu, 10 Oct 2019 12:07:23 UTC +00:00,
"updated_at_BEFORE"=>Thu, 10 Oct 2019 12:07:23 UTC +00:00,
"snapshot_id_BEFORE"=>18,
:entry1_ipnexthop_BEFORE=>"10.70.230.72",
:entry1_ifname_BEFORE=>"mgmt0",
:entry1_pref_BEFORE=>0,
:entry1_metric_BEFORE=>0,
:entry1_clientname_BEFORE=>"direct",
:entry1_route_type_BEFORE=>"",
"id_AFTER"=>10828,
"name_AFTER"=>"management",
"prefix_AFTER"=>"10.70.230.0/24",
"created_at_AFTER"=>Thu, 10 Oct 2019 12:07:35 UTC +00:00,
"updated_at_AFTER"=>Thu, 10 Oct 2019 12:07:35 UTC +00:00,
"snapshot_id_AFTER"=>19,
:entry1_ipnexthop_AFTER=>"10.70.230.72",
:entry1_ifname_AFTER=>"mgmt0",
:entry1_pref_AFTER=>0,
:entry1_metric_AFTER=>0,
:entry1_clientname_AFTER=>"direct",
:entry1_route_type_AFTER=>""
}
如果你把所有的键都做成字符串,你可以做如下的事情
hash.select { |k, _v| k.end_with?("BEFORE") }.all? { |k, v| v == hash["#{k.gsub("_BEFORE", "")}_AFTER"] }
试着这样做:
diff = hash.select do |k, v|
next unless k.to_s.include?('AFTER')
attr = k.to_s.split('AFTER').first
v != (hash["#{attr}BEFORE"] || hash["#{attr}BEFORE".to_sym])
end
p diff
#=> { "id_AFTER" => 10828, "created_at_AFTER" => "Thu, 10 Oct 2019 12:07:35 UTC +00:00","updated_at_AFTER" => "Thu, 10 Oct 2019 12:07:35 UTC +00:00", "snapshot_id_AFTER" => 19 }
# You can use it in IF statement
do_something if diff.empty?
代码
def pairs?(h, suffix1, suffix2)
h1 = select(h, suffix1).transform_keys do |k|
case k
when Symbol
k.to_s.sub(/#{suffix1}\z/, suffix2).to_sym
else # String
k.sub(/#{suffix1}\z/, suffix2)
end
end
h2 = select(h, suffix2)
h1.all? { |k,v| h2[k] == v }
end
def select(h, suffix)
h.select { |k,_| k.to_s.end_with?(suffix) }
end
示例
h = {
"id_BEFORE"=>10825,
"id_AFTER" =>10828,
:entry1_ipnexthop_BEFORE => "10.70.230.72",
:entry1_ipnexthop_AFTER => "10.70.230.72",
"name_BEFORE" => "management",
"name_AFTER" => "management",
"prefix_BEFORE" => "10.70.230.0/24",
"prefix_AFTER" => "10.70.230.0/24",
"1_more_AFTER" => "fake value"
}
解释
注意,(不像)返回一个散列
对于示例中定义的h
,步骤如下所示
suffix1 = "_BEFORE"
suffix2 = "_AFTER"
g = select(h, suffix1)
#=> {"id_BEFORE"=>10825, :entry1_ipnexthop_BEFORE=>"10.70.230.72",
# "name_BEFORE"=>"management", "prefix_BEFORE"=>"10.70.230.0/24"}
h1 = g.transform_keys do |k|
case k
when Symbol
k.to_s.sub(/#{suffix1}\z/, suffix2).to_sym
else # String
k.sub(/#{suffix1}\z/, suffix2)
end
end
#=> {"id_AFTER"=>10825, :entry1_ipnexthop_AFTER=>"10.70.230.72",
# "name_AFTER"=>"management", "prefix_AFTER"=>"10.70.230.0/24"}
h2 = select(h, suffix2)
#=> {"id_AFTER"=>10828, :entry1_ipnexthop_AFTER=>"10.70.230.72",
# "name_AFTER"=>"management", "prefix_AFTER"=>"10.70.230.0/24",
# "1_more_AFTER"=>"fake value"}
h1.all? { |k,v| h2[k] == v }
#=> false
返回false
,因为h2[k]==v#=>false
当k#=>id_在
之后(所以h2[k]=>10828
)和v#=>10825
如果我们将h[“id_AFTER”]
的值更改为10825
,则返回true
:
h2 = select(h.merge("id_AFTER"=>10825), suffix2)
#=> {"id_AFTER"=>10825, :entry1_ipnexthop_AFTER=>"10.70.230.72",
# "name_AFTER"=>"management", "prefix_AFTER"=>"10.70.230.0/24",
# "1_more_AFTER"=>"fake value"}
h1.all? { |k,v| h2[k] == v }
#=> true h1.all? { |k,v| h2[k] == v }
这将比较键包含字符串“BEFORE”的所有值与键包含字符串“AFTER”的所有值,如果值相同,则返回
true
,如果值不相同,则返回false
:
h.select { |k, _| k =~ /BEFORE/ }.values == h.select { |k, _| k =~ /AFTER/ }.values
我想这就是你要找的?如果不能保证值的顺序相同,你就必须做一些额外的事情。如果值的顺序不一致,那么首先将它们全部映射到字符串,然后对它们进行排序。(如果你不将它们全部映射到字符串,如果你试图用#sort
将整数与字符串进行比较,你就会遇到麻烦。)例如:h.select{k,{k=~/BEFORE/}.values.map(&:to.s).sort
要将其插入代码中,请执行以下操作:
if h.select { |k, _| k =~ /BEFORE/ }.values == h.select { |k, _| k =~ /AFTER/ }.values
# Execute the code you want to execute
end
您还可以将其分解为方法。这样读起来可能更容易:
def values_are_equal(h)
h.select { |k, _| k =~ /BEFORE/ }.values == h.select { |k, _| k =~ /AFTER/ }.values
end
def execute_some_code
# execute your code
end
execute_some_code if values_are_equal(h)
编辑:正如Cary所解释的,如果两个键的值被转置,这将不起作用。例如,如果您的哈希包含这些值:
:entry1_pref_BEFORE=>1
:entry1_metric_BEFORE=>0
:entry1_pref_AFTER=>0
:entry1_metric_AFTER=>1
它们在
BEFORE
和BEFORE
键中的值是相同的。如果发生这种情况,你不能使用我描述的方法,你可以使用Cary的方法。很好的答案。起初我认为hash={“id_BEFORE”=>10828,“id_BEFORE”=>10828,“cat_AFTER=“meow”}
会是个问题,但问题并不要求“键之后的所有”的值等于“
键之前的所有”的值。请注意,true
或false
的返回值是所有要求的(diff.empty?
,但是使用hash.any?
或hash.all?
更直接。我刚刚注意到一个问题。假设hash={“a_BEFORE”=>“cat”,:a_AFTER=>“cat”}
,然后diff=>
.Cary,你为什么不能将包含“BEFORE”的键与包含“AFTER”的键进行匹配,看看这两个子集是否相等?我是否遗漏了什么?@BobRodes,OP声明,“我想将所有“BEFORE”值与其对应的“AFTER”值进行比较。”我假设,对于名称以“
之前的“\u”结尾的每个键kb
,都有一个键ka
,除了在“之后以“”结尾外,其他键都有相同的名称,h[ka]==h[kb]\=>true
。另外,请注意,在我的示例中,没有键在“
之前多1个”来匹配键“之后多1个”
。我这样做是为了说明问题并不排除“
键之后可能有”而之前没有相应的”的可能性
键。好吧,但我还是不明白你为什么要这么麻烦。我并不想批评你,因为我可能遗漏了什么。但是,为什么你不能简单地选择所有正则表达式与/BEFORE/
匹配的键,抓取值,然后对所有匹配/BEFORE/
的键执行相同的操作,然后比较结果假设h={'a_在'=>1'之前,b_在'=>2'之前,b_在'=>1'之后,b_在'=>1'之后,a_在'=>2'之后。
假设h={'a_在'=>1'之前,a_在'=>2'之后,两组值是否相等值,但它们的值的数组都是[1,2]
(按键顺序)。再举一个例子,假设h={'a_在'=>1'之前,'a_在'=>1'之后,'b_在'=>2}
。所有的“BEFORE”值(只有一个)都等于它们相应的“AFTER”值,但它们的值的数组不:[1]
对于BEFORE,[1,2]
对于AFTER…,如果你比较一组值,而不是一组值,你会遇到与我给出的示例相同的问题。如果你有,比如说,{a_BEFORE'=>1,:a_BEFORE=>2,'b_BEFORE'=>2,:b_AFTER=>1},那么“…首先将它们全部映射到字符串…”可能是个问题
@CarySwoveland是的,如果出现问题,那将是一个问题。在某些问题域中,你肯定必须考虑到它。但是“下面的哈希”没有问题。我想问题是,是吗?也许一个健壮的解决方案必须解决这种可能性。
def values_are_equal(h)
h.select { |k, _| k =~ /BEFORE/ }.values == h.select { |k, _| k =~ /AFTER/ }.values
end
def execute_some_code
# execute your code
end
execute_some_code if values_are_equal(h)
:entry1_pref_BEFORE=>1
:entry1_metric_BEFORE=>0
:entry1_pref_AFTER=>0
:entry1_metric_AFTER=>1