此文翻译自7 daily use cases of Ruby Hash,限于本人水平,翻译不当之处,敬请指教!
每一天,你都需要跟Hash相处。创建一个新的Hash或者是通过它的某一个键去检索其中的元素这样的工作,都是常见也是非常简单的。但是当你需要合并两个嵌套的Hash或者是从某一个Hash里边过滤某些键,你可能需要考虑得多一点。通过完整的文档,你可以找到对Hash中的每一个方法的充分解释。但是由于文档不是面向应用场景的,你可能没法很快找到你的解决方案。在下面,我分享了我日常中经常遇到的Hash中的7个常用场景,希望它们对你有用。
1. 如何将一个JSON转换为一个Hash?
假设你刚刚接收到一个用JSON表示的Twitter账号的资料信息:
data = '{
"name": "Aaron Patterson",
"screen_name": "tenderlove",
"location": "Seattle, WA"
}'
你希望能够将它转化为一个Hash,这样会更方便你进行对数据的操作:
require 'json'
profile = JSON.parse(data)
** 在IRB中的输出结果:**
=> {
"name"=>"Aaron Patterson",
"screen_name"=>"tenderlove",
"location"=>"Seattle, WA"
}
查看文档:JSON#parse
2. 如何将一个Hash转换为一个JSON?
在你的web应用程序中,你需要追踪当前星期每一天新注册用户的数量:
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对于更好的打印以及调试非常有用。
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?