【问题标题】:Comparing the values of two Ruby hashes比较两个 Ruby 哈希值
【发布时间】:2017-07-15 15:17:24
【问题描述】:

我正在做一个需要比较两个哈希值的项目。哈希键 'title' 有一个数组作为其值,其中包含两个问题的标题。 我打算实现的是说'如果 issue_yaml 中的标题存在于 fwparse_issues 中,则将标题保留在 issue_yaml 中(哈希内的数组),否则将其删除。

示例代码:

  fwparse_issues = [
  {:section_title=>"Security Audit",
  :ref=>"FILTER.LOG.DROP",
  :title=>"Filter Drop Rules Were Configured Without Logging"},
 {:section_title=>"Security Audit",
  :ref=>"LOGGING.SYSLOG.NO.ENCRYPTION",
  :title=>"Syslog Logging Configured With No Encryption"},
 {:section_title=>"Security Audit",
  :ref=>"FILTER.RULE.EENE",
  :title=>"Filter Rules Allow Packets To A Network Destination"},
 {:section_title=>"Security Audit",
  :ref=>"FILTER.RULE.EEER",
  :title=>"Filter Rules Allow Packets To A Port Range"},
 {:section_title=>"Security Audit",
  :ref=>"BANNER.NO.POST.LOGON.MESSAGE",
  :title=>"No Post Logon Banner Message"},
 {:section_title=>"Security Audit",
  :ref=>"LOGGING.SYSLOG.SEVERITY",
  :title=>"Weak Syslog Severity Level Configured"},
 {:section_title=>"Security Audit",
  :ref=>"FILTER.RULE.NEEE",
  :title=>"Filter Rules Allow Packets From A Network Source"}]

issue_yaml = {"ABC-1234"=>
  {"title"=>["No Pre-Logon Banner Message", "No Post Logon Banner Message"],
   "desc"=>"some text",
   "rec"=>"recommendations go here",
   "ref"=>"references"},
 "ABC-5678"=>
  {"title"=>"SSH Protocol Version 1 Supported",
   "desc"=>"some text\nwhich spans\nmultiple lines\n",
   "rec"=>"recommendations go here",
   "ref"=>"references"}}

    fwparse_issues.each do |issue|
      issue_yaml.keys.each do |key|
        if issue_yaml[key]["title"].is_a?(Array)
          unless issue_yaml[key]["title"].include?(issue[:title])
            issue_yaml[key]["title"].delete(issue[:title])
          end
        end 
      end
    end

我最终需要的是:

{"ABC-1234"=>
  **{"title"=>["No Post Logon Banner Message"],**
   "desc"=>"some text",
   "rec"=>"recommendations go here",
   "ref"=>"references"}}

但粗体线最终变成:

{"ABC-1234"=>
  {"title"=>["No Pre-Logon Banner Message", "No Post Logon Banner Message"],
   "desc"=>"some text",
   "rec"=>"recommendations go here",
   "ref"=>"references"},

本质上,除非位不起作用。我花了这么长时间看它的那些情况之一,我什至无法再思考了。如果我将“除非”更改为“如果”,它会从 issue_yaml 中删除“无登录后横幅消息”,因此相反的情况似乎有效!

EDIT更正了预期的输出!

【问题讨论】:

  • 如果标题SSH Protocol Version 1 Supported 在 fwparse_issues 中不存在,为什么还会保留它?
  • 它不会,这就是脚本的目的。它采用用户定义的问题列表,然后在 fwparse_issues 哈希数组中查找它们。所以在这个用例中,SSH 协议版本 1 不会成功,“没有登录前横幅消息”也不会。由用户为这些问题添加映射。对不起,我不清楚:)

标签: ruby hash compare


【解决方案1】:

假设fwparse_issues如示例中给出,issue_yaml定义如下。

issue_yaml =
  {"ABC-1234"=>
     {"title"=>["No Pre-Logon Banner Message",
                "No Post Logon Banner Message"],
      "desc"=>"some text",
      "rec"=>"recommendations go here",
      "ref"=>"references"},
   "ABC-5678"=>
     {"title"=>"SSH Protocol Version 1 Supported",
      "desc"=>"some text\nwhich spans\nmultiple lines\n",
      "rec"=>"recommendations go here",
      "ref"=>"references"},
   "ABC-5679"=>
     {"title"=>"Weak Syslog Severity Level Configured",
      "desc"=>"some text\nwhich spans\nmultiple lines\n",
      "rec"=>"recommendations go here",
      "ref"=>"references"},
   "ABC-1230"=>
     {"title"=>["No Post Logon Banner Message",
                "No Post Logon Banner Message"],
      "desc"=>"some text",
      "rec"=>"recommendations go here",
      "ref"=>"references"}
  }

我们首先从fwparse_issues构造一个标题数组。

titles = fwparse_issues.map { |h| h[:title] }
  #=> ["Filter Drop Rules Were Configured Without Logging",
  #    "Syslog Logging Configured With No Encryption",
  #    "Filter Rules Allow Packets To A Network Destination",
  #    "Filter Rules Allow Packets To A Port Range",
  #    "No Post Logon Banner Message",
  #    "Weak Syslog Severity Level Configured",
  #    "Filter Rules Allow Packets From A Network Source"]

我们现在可以构造所需的哈希。

issue_yaml.each_with_object({}) do |(k,v),h|
  keepers = [*v["title"]].select { |s| titles.include?(s) }
  h[k] = v.merge("title"=>keepers.size==1 ? keepers.first : keepers) unless keepers.empty?
end
  #=> {"ABC-1234"=>{"title"=>"No Post Logon Banner Message",
  #                 "desc"=>"some text",
  #                 "rec"=>"recommendations go here",
  #                 "ref"=>"references"},
  #    "ABC-5679"=>{"title"=>"Weak Syslog Severity Level Configured",
  #                 "desc"=>"some text\nwhich spans\nmultiple lines\n",
  #                 "rec"=>"recommendations go here", "ref"=>"references"},
  #    "ABC-1230"=>{"title"=>["No Post Logon Banner Message",
  #                           "No Post Logon Banner Message"],
  #                 "desc"=>"some text",
  #                 "rec"=>"recommendations go here",
  #                 "ref"=>"references"}
  #   }

如果s = v["title"] 是字符串,则[*v["title"]] 返回[s]。如果arr = v["title"] 是一个数组,则[*v["title"]] 返回v。例如,如果v = "hi"[*v] #=> ['hi'];如果v = ['hi', 'ho'][*v["title"]] 返回['hi', 'ho']

让我们更仔细地检查执行的计算。我们首先创建一个枚举器。

enum = issue_yaml.each_with_object({})
  #=> enum=#<Enumerator:0x00000000e5be38>

我们可以检查将由枚举器enum 生成的值,方法是将其转换为数组(或执行enum.entries)。

enum.to_a
  #=> [[["ABC-1234", {"title"=>["No Pre-Logon Banner Message",
  #                             "No Post Logon Banner Message"],
  #                   "desc" =>"some text",
  #                   "rec"  =>"recommendations go here",
  #                   "ref"  =>"references"}], {}],
  #    [["ABC-5678", {"title"=>"SSH Protocol Version 1 Supported",
  #                   "desc" =>"some text\nwhich spans\nmultiple lines\n",
  #                   "rec"  =>"recommendations go here",
  #                   "ref"  =>"references"}], {}],
  #    [["ABC-5679", {"title"=>"Weak Syslog Severity Level Configured", 
  #                   "desc" =>"some text\nwhich spans\nmultiple lines\n",
  #                   "rec"  =>"recommendations go here",
  #                   "ref"=>"references"}], {}],
  #    [["ABC-1230", {"title"=>["No Post Logon Banner Message",
  #                             "No Post Logon Banner Message"],
  #                   "desc"=>"some text",
  #                   "rec"=>"recommendations go here",
  #                   "ref"=>"references"}], {}]]

enum 生成一系列元素,这些元素传递给块并分配给块变量。生成并传递给块的第一个元素如下。

(k,v), h = enum.next
  #=> [["ABC-1234", {"title"=>["No Pre-Logon Banner Message",
  #                            "No Post Logon Banner Message"],
  #                  "desc"=>"some text",
  #                  "rec" =>"recommendations go here",
  #                  "ref" =>"references"}], {}]

Ruby 消除此数组的歧义并将值分配给块变量。

k #=> ABC-1234
v #=> {"title"=>["No Pre-Logon Banner Message",
  #              "No Post Logon Banner Message"],
  #    "desc"=>"some text", 
  #    "rec" =>"recommendations go here",
  #    "ref"=>"references"}
h #=> {}

我们现在可以执行块计算了。

a = v["title"]
  #=> ["No Pre-Logon Banner Message", "No Post Logon Banner Message"]
b = [*a]
  #=> ["No Pre-Logon Banner Message", "No Post Logon Banner Message"]
keepers = b.select { |s| titles.include?(s) }
  #=> ["No Post Logon Banner Message"]
keepers.empty?
  #=> false
keepers.size == 1,
  #=> true
c = keepers.first
  #=> No Post Logon Banner Message
h[k] = v.merge("title"=>c)
  #=> {"title"=>"No Post Logon Banner Message",
  #    "desc"=>"some text",
  #    "rec"=>"recommendations go here", "ref"=>"references"}
h #=> {"ABC-1234"=>{"title"=>"No Post Logon Banner Message",
  #                 "desc" =>"some texte",
  #                 "ref"=>"references"}}

其余的计算类似。

将键 "titles" 的所有值设为(字符串)数组可能更方便,即使这些数组仅包含单个字符串。

【讨论】:

  • 感谢您的深入报道。这对我构建这个东西会有很大帮助。我可能会稍微改变 yaml 的结构以使其更简单。它的目的是让每个“标题”也成为一个标题数组。
【解决方案2】:

这应该可以满足您的需求。

valid_titles = fwparse_issues.map { |i| i[:title] }
issue_yaml.each do |k, v|
  v['title'] = [v['title']].flatten.select {|vt| valid_titles.include?(vt)}
end

编辑:改为使用适当的答案。

产量:

{
  "ABC-1234"=>{"title"=>["No Post Logon Banner Message"], ...}, 
  "ABC-5678"=>{"title"=>[], ... "}
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-05-03
    • 2011-04-15
    • 2014-07-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多