Ruby中Hash的7個平常使用範例

此文翻譯自7 daily use cases of Ruby Hash,限於本人水平,翻譯不當之處,敬請指教!html

每一天,你都須要跟Hash相處。建立一個新的Hash或者是經過它的某一個鍵去檢索其中的元素這樣的工做,都是常見也是很是簡單的。可是當你須要合併兩個嵌套的Hash或者是從某一個Hash裏邊過濾某些鍵,你可能須要考慮得多一點。經過完整的文檔,你能夠找到對Hash中的每個方法的充分解釋。可是因爲文檔不是面向應用場景的,你可能無法很快找到你的解決方案。在下面,我分享了我平常中常常遇到的Hash中的7個經常使用場景,但願它們對你有用。
web

1. 如何將一個JSON轉換爲一個Hash?

假設你剛剛接收到一個用JSON表示的Twitter帳號的資料信息:json

data = '{
  "name": "Aaron Patterson",
  "screen_name": "tenderlove",
  "location": "Seattle, WA"
}'

你但願可以將它轉化爲一個Hash,這樣會更方便你進行對數據的操做:api

require 'json'

profile = JSON.parse(data)

** 在IRB中的輸出結果:**ruby

=> {
  "name"=>"Aaron Patterson",
  "screen_name"=>"tenderlove",
  "location"=>"Seattle, WA"
}

查看文檔:JSON#parseide

2. 如何將一個Hash轉換爲一個JSON?

在你的web應用程序中,你須要追蹤當前星期每一天新註冊用戶的數量:ui

signups_of_the_week = {
    monday: 2,
    tuesday: 3,
    wednesday: 4,
    thursday: 20,
    friday: 5,
    saturday: 2,
    sunday: 5
}

你能夠經過API的方式把它們以JSON格式提供給客戶端:翻譯

require 'json'

signups_of_the_week.to_json

** 在IRB中的輸出結果:**調試

=> "{\"monday\":2,\"tuesday\":3,\"wednesday\":4,\"thursday\":20,\"friday\":5,\"saturday\":2,\"sunday\":5}"

查看文檔:JSON#generate
邊注:JSON#pretty_generate對於更好的打印以及調試很是有用。code

3. 如何爲一個嵌套的Hash設置默認值?

你有一個以name爲索引的聯繫人的集合,也就是一個嵌套的Hash:

contacts = {
  'John' => {
    name: 'John',
    email: 'john@doe.com'
  },
  'Freddy' => {
    name 'Freddy',
    email: 'freddy@mercury.com'
  }
}

當你在處理單個聯繫人的時候,你不須要每一次都檢查它是否存在。你只須要寫:

contacts['Jane'][:email] = 'jane@doe.com'
puts contacts['Jane']

** IRB輸出 **:

=> {:name=>"Jane", :email=>"jane@doe.com"}

你能夠在建立Hash的時候經過設置代碼塊來實現默認值:

contacts = Hash.new do |hsh, key|
  hsh[key] = {
    name: key,
    email: ''
  }
end

或者是使用:

contacts.default_proc = Proc.new do |hsh, key|
  hsh[key] = {
    name: key,
    email: ''
  }
end

查看文檔:Hash#new, Hash#default_proc

4. 如何合併兩個嵌套的Hash?

在一個在線商店裏,你想要將一個心願單與當前的購物籃進行合併,這二者都是以商品的id號做爲索引:

wish_list = {
  8 => {
    title: "The Color of Magic",
  },
  42 => {
    title: "The Hitch-Hiker's Guide to the Galaxy",
    price: 5
  }
}
 
basket = {
  8 => {
    price: 10
  },
  1729 => {
    title: "Ramanujan:  Twelve Lectures on Subjects Suggested by His Life and Work",
  price: 28
  }
}

藉助於ActiveSupport,你能夠簡單地實現你的目標:

require 'active_support/core_ext/hash' # not necessary if in Rails
 
basket.deep_merge(wish_list)

又或者,在沒有ActiveSupport的狀況下:

def deep_merge(h1, h2)
  h1.merge(h2) { |key, h1_elem, h2_elem| deep_merge(h1_elem, h2_elem) }
end
 
deep_merge(basket, wish_list)

** IRB輸出: **

=> {
  8=>{:price=>10, :title=>"The Color of Magic"},
  1729=>{:title=>"Ramanujan:  Twelve Lectures on Subjects Suggested by His Life and Work", :price=>28},
  42=>{:title=>"The Hitch-Hiker's Guide to the Galaxy", :price=>5}
}

查看文檔:Hash#merge, Hash#deep_merge

5. 如何過濾掉一個Hash中的某些key?

你已經建立了一個表示日銷售額的矩形圖,而且你將它以Hash的方式存儲,每一天就是一個key:

histogram = {
  monday: 5,
  tuesday: 7,
  wednesday: 10,
  thursday: 18,
  friday: 7,
  saturday: 2,
  sunday: 0
}

你想從中過濾掉Saturday以及Sunday。經過ActiveSupport,你能夠像下面這樣作:

require 'active_support/core_ext/hash' # not necessary if Rails
 
histogram.except(:saturday, :sunday)

或者在沒有ActiveSupport的狀況下:

def filter(hsh, *keys)
  hsh.dup.tap do |h|
    keys.each { |k| h.delete(k) }
  end
end
 
filter(histogram, :saturday, :sunday)

另外一個簡潔點實現則是基於reject方法的:

def filter2(hsh, *keys)
  hsh.reject { |k, _| keys.include? k }
end

請注意,若是你正在處理一個比較大的集合,你最好是先衡量下你的實現,一次選擇最好的其中一個實現。
** IRB輸出:**

=> {:monday=>5, :tuesday=>7, :wednesday=>10, :thursday=>18, :friday=>7}

查看文檔:Hash#except, Hash#delete, Hash#reject, Object#dup, Object#tap

6. 如何經過value對一個Hash進行「排序」?

在一個骰子類遊戲中,你在Hash中儲存了每個選手的得分:

scores = {
  'The Lady' => 3,
  'Fate' => 2,
  'Death' => 10
}

你想要經過他們的得分對他們進行排序。你能夠這樣作:

leaderboard = scores.sort_by { |_, score| -score }

** IRB輸出:**

=> [["Death", 10], ["The Lady", 3], ["Fate", 2]]

查看文檔:Enumerable#sort_by
邊注:Hash經過元素插入時的順序去枚舉它們的值。

7. 如何找出兩個Hash中的不一樣?

假設你按期地從RSS訂閱源中讀取數據,而且將他們放在了一個Hash裏邊:

entries = {
  1372284000 => "CVE-2013-4073",
  1368482400 => "CVE-2013-2065"
}

當你更新了以後,你可能獲得另外一個Hash:

updated_entries = {
  1385074800 => "CVE-2013-4164",
  1372284000 => "CVE-2013-4073",
  1368482400 => "CVE-2013-2065"
}

你想要查找出哪一條記錄纔是新加的,這樣你就能夠經過email的方式將它們發送出去。最好的解決方案是:

new_entries = updated_entries.reject { |k, _| entries.include? k }

** IRB輸出:**

=> {1385074800=>"CVE-2013-4164"}

查看文檔:Hash#include?

相關文章
相關標籤/搜索