【问题标题】:Dynamically creating a multi-dimensional hash in Ruby在 Ruby 中动态创建多维哈希
【发布时间】:2012-04-20 20:26:38
【问题描述】:

我是一名 PHP 开发人员,正在努力精通 Ruby。我现在正在努力的一个项目是一个源代码审计工具,它可以扫描 webapp 文件以查找几种 web 编程语言中的潜在危险功能。找到匹配项后,脚本会将相关信息保存在 poi(兴趣点)类中,以便稍后显示。

该类的示例实例如下所示(以 YAML 建模):

poi:
    file_type: "php"
    file: "the-scanned-file.php"
    line_number: 100
    match: "eval()"
    snippet: "echo eval()"

在展示中,我想像这样组织这些兴趣点:

- file_type
-- file
--- match (the searched payload)

因此,在演示之前,我试图将poi 对象的平面数组构造成一个哈希镜像上面的结构。这将允许我简单地遍历散列中的项目以生成所需的屏幕组织。 (或者至少,这是计划。)

现在,对于我的问题:我如何在 Ruby 中做到这一点?

在 PHP 中,我可以很容易地做这样的事情:

<?php

$sorted_pois = array();
foreach($points_of_interest as $point){
    $sorted_pois[$point->file_type][$point->file][$point->match][] = $point;
}

?>

我曾尝试像这样将这种想法从 PHP 转换为 Ruby,但无济于事:

sorted_pois = {}
@points_of_interest.each_with_index do |point, index|
    sorted_pois[point.file_type.to_sym][point.file.to_sym][point.match.to_sym].push point
end

我在这方面花了几个小时,此时我有点想把我的头撞到墙上,所以大概我离基地很远。在 Ruby 中处理这个问题的正确方法是什么?

更新:

作为参考,这是我定义的精确方法:

# sort the points of interest into a structured hash
def sort
  sorted_pois = {}
  @points_of_interest.each_with_index do |point, index|
    sorted_pois[point.file_type.to_sym][point.file.to_sym][point.match.to_sym].push point
  end
end

这是我在运行代码时收到的错误:

./lib/models/vulnscanner.rb:63:in `sort': undefined method `[]' for nil:NilClass (NoMethodError)
    from /usr/lib/ruby/1.8/rubygems/custom_require.rb:31:in `each_with_index'
    from ./lib/models/vulnscanner.rb:62:in `each'
    from ./lib/models/vulnscanner.rb:62:in `each_with_index'
    from ./lib/models/vulnscanner.rb:62:in `sort'
    from ./webapp-vulnscan:69

第 62 行(您可能会推断)是这一行:

@points_of_interest.each_with_index do |point, index|

作为附加参考,以下是@points_of_interest 转换为 YAML 后的样子:

- !ruby/object:PoI 
  file: models/couponkimoffer.php
  file_type: php
  group: :dangerous_functions
  line_number: "472"
  match: `
  snippet: ORDER BY `created_at` DESC
- !ruby/object:PoI 
  file: models/couponkimoffer.php
  file_type: php
  group: :dangerous_functions
  line_number: "818"
  match: `
  snippet: WHERE `company_slug` = '$company_slug'
- !ruby/object:PoI 
  file: models/couponkimoffer.php
  file_type: php
  group: :dangerous_functions
  line_number: "819"
  match: `
  snippet: ORDER BY `created_at` DESC

【问题讨论】:

  • 你有什么问题?它会导致错误还是输出不是您所期望的?此外,提供示例输入/输出也很有帮助。
  • @AndrewMarshall,感谢您的关注。我刚刚更新了问题。

标签: ruby hash


【解决方案1】:

@John 的Enumerable#group_by 建议是解决您的需求的一种好方法。另一种方法是像这样创建一个自动激活的哈希(就像你在 PHP 中看到的那样):

hash = Hash.new{ |h,k| h[k] = Hash.new(&h.default_proc) }
hash[:a][:b][:c] = 42
p hash
#=> {:a=>{:b=>{:c=>42}}}

请注意,如果您访问不存在的密钥,这种自动激活可能是“危险的”,因为它会为您创建它们:

p hash["does this exist?"]
#=> {}

p hash
#=> {:a=>{:b=>{:c=>42}}, "does this exist?"=>{}}

如果您先使用key? 测试密钥,您仍然可以使用活跃的default_proc 而不会遇到这种危险:

val = hash["OH NOES"] if hash.key?("OH NOES")
#=> nil

p hash
#=> {:a=>{:b=>{:c=>42}}, "does this exist?"=>{}}

FWIW,你得到的错误是,“嘿,你把 [] 放在评估为 nil 的东西之后,而 nil 没有 [] 方法。” 具体来说,您的代码...

sorted_pois[point.file_type.to_sym]

计算为nil(因为哈希还没有这个键的值)然​​后你试图请求

nil[point.file.to_sym]

【讨论】:

  • @Phrogz,感谢您花时间向我解释。我真的开始喜欢 Ruby,但是伙计,这很棘手!这很明显我还有更多的阅读要做:)
  • 很好的答案!真的让我明白了!感谢您对事情的解释和您所做的一样。
【解决方案2】:

您可能对group_by 感兴趣。

示例用法:

birds = ["Golden Eagle", "Gyrfalcon", "American Robin",
         "Mountain BlueBird", "Mountain-Hawk Eagle"]
grouped_by_first_letter = birds.group_by { |s| s[0] }

# { "G"=>["Golden Eagle", "Gyrfalcon"], "A"=>["American Robin"],
#   "M"=>["Mountain BlueBird", "Mountain-Hawk Eagle"] }

【讨论】:

  • +1 表示正确;如果您展示它在链接到文档之外的用途,您可能会收集更多的赞成票。
【解决方案3】:

上面示例的明显问题是您尝试使用的嵌套哈希和数组不存在。试试这个:

sorted_pois = {}
pois.each do |point|
  # sanitize data - convert to hash of symbolized keys and values
  poi = Hash[ %w{file_type file match}.map do |key| 
    [key.to_sym, point.send(key).to_sym]
  end ]

  # create nested hash/array if it doesn't already exist
  sorted_pois[ poi[:file_type] ] ||= {}
  sorted_pois[ poi[:file_type] ][ poi[:file] ] ||= {}
  sorted_pois[ poi[:file_type] ][ poi[:file] ][ poi[:match] ] ||= []

  sorted_pois[ poi[:file_type] ][ poi[:file] ][ poi[:match] ] << point
end

【讨论】:

  • 这是手动创建嵌套的“更安全”的方法;请参阅我的答案,了解一种不太安全但更方便的方式。
猜你喜欢
  • 1970-01-01
  • 2011-07-31
  • 2014-09-18
  • 1970-01-01
  • 2018-01-26
  • 1970-01-01
  • 2014-07-21
  • 1970-01-01
  • 2012-11-23
相关资源
最近更新 更多