2012-04-20 81 views
7

動態創建多維散列我是誰試圖獲得在Ruby中的一些精通PHP開發人員。我現在正在削減我的一個項目是一個源代碼審計工具,它可以掃描webapp文件,查找幾種網絡編程語言中潛在的危險功能。當找到匹配時,腳本保存在一個poi(點利息)類顯示器後面的相關信息。在Ruby中

這個類中的一個實例會是這個樣子(在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對象數組構造成一個鏡像上述結構的散列。這將允許我簡單地遍歷散列中的項目以產生期望的屏幕上組織。 (或者至少,計劃是這樣的。)

而現在,我的問題:我應該怎麼做,在紅寶石?

在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 
+1

這有什麼錯你有?它會導致錯誤還是輸出不是您所期望的?另外,提供樣本輸入/輸出也很有幫助。 – 2012-04-20 20:36:53

+0

@AndrewMarshall,感謝您的關注。我剛更新了這個問題。 – 2012-04-20 20:47:57

回答

27

@約翰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}}} 

注意,這種自動vivification的可能是「危險的」,如果你訪問不鍵存在的,因爲它爲您創建它們:

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

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

你仍然可以使用生機default_proc沒有碰到這樣的危險,如果你使用key?首先測試的關鍵在於:

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] 
+1

因此有經驗... – texasbruce 2012-04-20 21:03:54

+0

+1不錯! (雖然對於Ruby初學者來說有點壓倒性) – 2012-04-20 21:04:26

+0

@Phrogz,感謝您花時間向我解釋這一點。我真的開始喜歡Ruby,但是男人,這很棘手!這使得它顯而易見我有更多的閱讀做:) – 2012-04-20 21:20:32

2

與上面的例子最明顯的問題是,嵌套哈希和數組您嘗試使用別不存在。試試這個:

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 
+0

這是手動創建嵌套的'安全'方法;看到我的答案是一種不太安全但更方便的方式。 – Phrogz 2012-04-20 20:49:48

+0

Phrogz,你說得對,謝謝你的注意,我修好了。 – 2012-04-20 21:22:02

7

您可能感興趣的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

+1是正確的;如果您在鏈接到文檔之外顯示它的使用方式,則可以收集更多有價值的信息。 – Phrogz 2012-04-20 20:45:17