在Ruby中动态创建多维哈希

Chr*_*ane 7 ruby hash

我是一名PHP开发人员,他试图获得一些Ruby熟练程度.我现在正在削减其中一个项目是源代码审计工具,它可以扫描webapp文件,查找多种Web编程语言中的潜在危险函数.找到匹配项后,脚本会将相关信息保存在poi(兴趣点)类中,以便稍后显示.

该类的示例实例看起来像这样(在YAML中建模):

poi:
    file_type: "php"
    file: "the-scanned-file.php"
    line_number: 100
    match: "eval()"
    snippet: "echo eval()"
Run Code Online (Sandbox Code Playgroud)

在展出时,我想组织这些兴趣点,如下所示:

- file_type
-- file
--- match (the searched payload)
Run Code Online (Sandbox Code Playgroud)

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

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

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

<?php

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

?>
Run Code Online (Sandbox Code Playgroud)

我试过将这种思想从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
Run Code Online (Sandbox Code Playgroud)

我已经花了几个小时在这上面,而且此刻我有点撞到墙上,所以我想我离开了基地.在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
Run Code Online (Sandbox Code Playgroud)

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

./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
Run Code Online (Sandbox Code Playgroud)

第62行(你可以推断)特别是这一行:

@points_of_interest.each_with_index do |point, index|
Run Code Online (Sandbox Code Playgroud)

作为一个额外的参考,这里是@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
Run Code Online (Sandbox Code Playgroud)

Phr*_*ogz 28

@ John的Enumerable#group_by建议是解决您需求的好方法.另一种方法是创建一个自动生存的Hash(就像你似乎在PHP中一样),如下所示:

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

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

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

p hash
#=> {:a=>{:b=>{:c=>42}}, "does this exist?"=>{}}
Run Code Online (Sandbox Code Playgroud)

default_proc如果您首先使用key?测试键,您仍然可以使用vivifying 而不会遇到这种危险:

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

p hash
#=> {:a=>{:b=>{:c=>42}}, "does this exist?"=>{}}
Run Code Online (Sandbox Code Playgroud)

FWIW,你得到的错误是,"嘿,你把[]事情评估为nil,并nil没有[]方法." 具体来说,你的代码......

sorted_pois[point.file_type.to_sym]
Run Code Online (Sandbox Code Playgroud)

评估为nil(因为哈希还没有这个键的值),然后你试图要求

nil[point.file.to_sym]
Run Code Online (Sandbox Code Playgroud)


Joh*_*ter 8

您可能对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"] }
Run Code Online (Sandbox Code Playgroud)

  • +1是正确的;如果您展示除链接到文档外的用法,您可能会收集更多的赞誉。 (2认同)