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?

相关文章
相关标签/搜索