как сравнить много значений в хэше друг с другом - PullRequest
0 голосов
/ 11 октября 2019

У меня есть хеш, который выглядит как показано ниже. Я хочу сравнить все значения «ДО» с соответствующими значениями «ПОСЛЕ». Когда все идентичны, я выполняю некоторый код. В настоящее время я делаю это с помощью оператора 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 +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=>""
 }

Ответы [ 4 ]

1 голос
/ 11 октября 2019

Попробуйте так:

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?
0 голосов
/ 14 октября 2019

При этом будут сравниваться все значения, ключи которых содержат строку «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)

Редактировать: Как объяснил Кэри, это не будет работать, если значения двух ключей транспонированы. Например, если ваш хэш включает эти значения:

:entry1_pref_BEFORE=>1
:entry1_metric_BEFORE=>0    
:entry1_pref_AFTER=>0
:entry1_metric_AFTER=>1

, они будут оцениваться как одинаковые значения в клавишах BEFORE и AFTER. Если это может произойти, вы не можете использовать метод, который я описал, и вы можете использовать метод Кэри.

0 голосов
/ 11 октября 2019

Код

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" 
}

pairs?(h, "_BEFORE", "_AFTER")
  #=> false

pairs?(h.merge("id_AFTER"=>10825), "_BEFORE", "_AFTER")
  #=> true

Объяснение

Обратите внимание, что Hash # select (в отличие от Enumerable # select ) возвращает хэш.

Для 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

Enumerable # all? возвращает false, посколькуh2[k] == v #=> false когда k #=> "id_AFTER (т. Е. 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 }
0 голосов
/ 11 октября 2019

Если вы сделаете все строки ключей, вы можете сделать что-то вроде

hash.select { |k, _v| k.end_with?("BEFORE") }.all? { |k, v| v == hash["#{k.gsub("_BEFORE", "")}_AFTER"] }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...