ZetCode

Ruby 哈希

最后修改于 2023 年 10 月 18 日

在本 Ruby 教程中,我们将学习 Ruby 哈希。

Ruby 哈希定义

Ruby 哈希 是一组键值对的集合。它类似于数组。与数组不同的是,哈希可以使用任意对象作为索引。数组只能使用整数。哈希以插入对应键的顺序枚举它们的值。哈希有时被称为关联数组。

哈希是强大的集合。它们有很多方法,程序员可以使用它们来完成工作。

Ruby 哈希创建

哈希可以通过两种基本方式创建:使用 new 关键字或使用哈希字面量。

create_hash.rb
#!/usr/bin/ruby

names = Hash.new
names[1] = "Jane"
names[2] = "Thomas"

puts names

第一个脚本创建了一个哈希,并在哈希对象中添加了两个键值对。

names = Hash.new

创建了一个哈希对象。

names[1] = "Jane"
names[2] = "Thomas"

我们向哈希中添加了两对值。数字 1、2 是哈希的键。键放在方括号内。名称是属于键的值。

puts names

puts 方法将哈希的字符串表示形式打印到控制台。它也是哈希的字符串字面量。

$ ./create_hash.rb
{1=>"Jane", 2=>"Thomas"}

从输出中,我们可以看到 names 哈希的字面量表示形式。哈希用大括号括起来。键和值用 => 字符配对。

可以使用 store 方法使用一些值初始化哈希。它可以用作方括号的替代方案。

create_hash2.rb
#!/usr/bin/ruby

names = Hash.new
names.store(1, "Jane")
names.store(2, "Thomas")
names.store(3, "Rebecca")

puts names

我们有一个类似的脚本。这次我们使用 store 方法。该方法将给定的键与给定的值关联起来,并将该对存储在哈希中。

names.store(1, "Jane")

store 方法的第一个参数是键,第二个参数是值。

在第三个脚本中,我们使用哈希字面量表示法创建一个哈希。值用大括号括起来。键值对与 => 字符相关联。

create_hash3.rb
#!/usr/bin/ruby

domains = { "de" => "Germany",
            "sk" => "Slovakia",
            "hu" => "Hungary",
            "us" => "United States",
            "no" => "Norway"
          }

puts domains["de"]
puts domains["sk"]

我们创建了一个 domains 哈希,其中有 5 对。这次键和值都是字符串类型。

domains = { "de" => "Germany",
            "sk" => "Slovakia",
            "hu" => "Hungary",
            "us" => "United States",
            "no" => "Norway"
}

这是哈希字面量表示法。键值对放在大括号之间。项目由逗号分隔。键与值使用 => 字符组合关联。

puts domains["de"]

在这里,我们打印与 "de" 键关联的域名值名称。

$ ./create_hash3.rb
Germany
Slovakia

Ruby 哈希的基本操作

在本节中,我们将介绍一些用于 Ruby 哈希基本操作的方法。

basic_work.rb
#!/usr/bin/ruby

names = Hash.new

names[1] = "Jane"
names[2] = "Thomas"
names[3] = "Robert"
names[4] = "Julia"
names[5] = "Rebecca"

puts "The size of the hash is #{names.size}"

puts names.keys.inspect
puts names.values.inspect

在上面的 Ruby 脚本中,我们创建了一个包含五个值的哈希。我们介绍了三种哈希方法。

puts "The size of the hash is #{names.size}"

size 方法返回哈希的大小。它是 length 方法的同义词。

puts names.keys.inspect
puts names.values.inspect

keys 方法返回哈希的所有键。类似地,values 方法返回哈希的所有值。返回的数据以数组的形式出现。为了使输出更具可读性,我们还对返回的数组调用 inspect 方法。

$ ./basic_work.rb
The size of the hash is 5
[1, 2, 3, 4, 5]
["Jane", "Thomas", "Robert", "Julia", "Rebecca"]

我们看到了示例的输出。请注意,最后两种方法的输出是两个数组。

本节的第二个示例介绍了三种不同的哈希方法。

basic_work2.rb
#!/usr/bin/ruby

names1 = Hash.new

names1[1] = "Jane"
names1[2] = "Thomas"
names1[3] = "Robert"
names1[4] = "Julia"
names1[5] = "Rebecca"

names2 = names1.dup

puts names1.eql? names2

puts names1.empty?
names1.clear
puts names1.empty?

Ruby 脚本创建了一个 names 哈希。它在该对象上调用了三个哈希方法。

names2 = names1.dup

我们通过调用 dup 方法创建了哈希的副本。哈希从父对象继承了该方法。

puts names1.eql? names2

eql? 方法比较两个哈希对象。在我们的例子中,哈希相等,并且该行打印 true。

puts names1.empty?

empty? 方法检查哈希是否为空。该行打印 false,因为 names1 哈希有五个项目。

names1.clear
puts names1.empty?

clear 方法从哈希中删除所有项目。连续调用 empty? 方法将返回 true。

$ ./basic_work2.rb
true
false
true

我们有一些方法可以确定哈希中是否存在键或值。

is_present.rb
#!/usr/bin/ruby

domains = { :de => "Germany", :sk => "Slovakia",
            :no => "Norway", :us => "United States"
          }

puts domains.has_key? :de
puts domains.include? :no
puts domains.key? :me
puts domains.member? :sk

puts domains.has_value? "Slovakia"
puts domains.value? "Germany"

我们创建了一个 domains 哈希,其中有四个键值对。键是符号。符号通常用作键,因为它们更有效。

puts domains.has_key? :de
puts domains.include? :no
puts domains.key? :me
puts domains.member? :sk

这里我们有四种方法来确定哈希中是否存在键。它们都做同样的事情;它们是同义词。

puts domains.has_value? "Slovakia"
puts domains.value? "Germany"

这两种方法检查字符串是否在哈希中。

$ ./is_present.rb
true
true
false
true
true
true

在本节的最后一个示例中,我们从哈希中读取值。

reading.rb
#!/usr/bin/ruby

stones = { 1 => "garnet", 2 => "topaz",
           3 => "opal", 4 => "amethyst"
         }

puts stones.fetch 1
puts stones[2]
puts stones.values_at 1, 2, 3

Ruby 脚本介绍了三种用于读取哈希值的哈希方法。

puts stones.fetch 1

fetch 方法读取给定键的值。

puts stones[2]

可以使用方括号获取一个值。在我们的例子中,该行将 "topaz" 打印到控制台。

puts stones.values_at 1, 2, 3

可以使用 values_at 方法一步获取多个值。该方法返回给定键的值的数组。

$ ./reading.rb
garnet
topaz
garnet
topaz
opal

Ruby 哈希迭代

有几种方法可以用于循环 Ruby 哈希。

looping.rb
#!/usr/bin/ruby

stones = { 1 => "garnet", 2 => "topaz",
           3 => "opal", 4 => "amethyst"
         }

stones.each { |k, v| puts "Key: #{k}, Value: #{v}" }
stones.each_key { |key| puts "#{key}" }
stones.each_value { |val| puts "#{val}" }
stones.each_pair { |k, v| puts "Key: #{k}, Value: #{v}" }

在上面的示例中,我们介绍了四种方法。我们使用它们来显示哈希的所有键、值以及键和值。

stones.each { |k, v| puts "Key: #{k}, Value: #{v}" }

each 方法为哈希中的每个键调用给定的块,将键值对作为参数传递。

stones.each_key { |key| puts "#{key}" }

我们使用 each_key 方法循环遍历哈希的所有键。它们被打印到控制台。

stones.each_value { |val| puts "#{val}" }

each_value 方法可用于循环遍历哈希的值。

stones.each_pair { |k, v| puts "Key: #{k}, Value: #{v}" }

each_pair 方法是 each 方法的同义词。我们循环遍历 stones 哈希的键和值。

$ ./looping.rb
Key: 1, Value: garnet
Key: 2, Value: topaz
Key: 3, Value: opal
Key: 4, Value: amethyst
1
2
3
4
garnet
topaz
opal
amethyst
Key: 1, Value: garnet
Key: 2, Value: topaz
Key: 3, Value: opal
Key: 4, Value: amethyst

输出显示了 stones 哈希的键和值、键和值。

Ruby 删除哈希中的键值对

在以下示例中,我们关注从哈希中删除键值对的方法。这包括删除单个键值对的方法以及可以一步删除多个键值对的方法。

delete_items.rb
#!/usr/bin/ruby

names = Hash.new

names[1] = "Jane"
names[2] = "Thomas"
names[3] = "Robert"
names[4] = "Julia"
names[5] = "Rebecca"

names.delete 4
names.shift

puts names

在脚本中,我们有两种方法:deleteshiftdelete 方法删除并返回指定键的值。shift 方法删除哈希中的第一个键值对。它还以数组的形式返回已删除的键值对。

names.delete 4

在这里,我们删除一个键值对 4 => "Julia"

names.shift

此代码行删除第一个键值对,即 1 => "Jane"

$ ./delete_items.rb
{2=>"Thomas", 3=>"Robert", 5=>"Rebecca"}

在输出中,我们可以看到哈希中剩余的键值对。

rejectdelete_if 方法可以从哈希中删除多个键值对。这些方法删除在块中对给定条件返回 true 的键值对。这两种方法之间存在一个重要的区别。reject 方法在哈希的副本上工作,而 delete_if 方法在原始哈希上工作。

delete_if.rb
#!/usr/bin/ruby

names1 = Hash.new

names1[1] = "Jane"
names1[2] = "Thomas"
names1[3] = "Robert"
names1[4] = "Julia"
names1[5] = "Rebecca"

puts names1.reject { |k, v| v =~ /R.*/ }
puts names1
puts names1.delete_if { |k, v| k <= 3 }
puts names1

该示例使用前面提到的方法删除多个键值对。

puts names1.reject { |k, v| v =~ /R.*/ }

reject 方法删除所有符合块中正则表达式的值。修改后的哈希被返回,而原始哈希未被更改。

puts names1

该行的输出确认了原始哈希未被更改。

puts names1.delete_if { |k, v| k <= 3 }

在这种情况下,我们删除所有键小于或等于 3 的键值对。该方法修改了原始哈希。

$ ./delete_if.rb
{1=>"Jane", 2=>"Thomas", 4=>"Julia"}
{1=>"Jane", 2=>"Thomas", 3=>"Robert", 4=>"Julia", 5=>"Rebecca"}
{4=>"Julia", 5=>"Rebecca"}
{4=>"Julia", 5=>"Rebecca"}

Ruby 向哈希添加元素

Ruby 的 merge 和 update 方法将 (key, value) 对添加到哈希中。Ruby 有用于哈希加法的方法。

adding.rb
#!/usr/bin/ruby

names1 = Hash.new

names1[1] = "Jane"
names1[2] = "Thomas"

names2 = Hash.new

names2[3] = "Robert"
names2[4] = "Julia"

names = names1.merge names2
puts names

names = names1.update names2
puts names

在 Ruby 脚本中,我们创建了两个哈希。然后我们在它们上应用 mergeupdate 方法。

names = names1.merge names2
puts names

names1 和 names2 哈希合并。结果分配给 names 哈希。我们打印新创建的哈希。

$ ./adding.rb
{1=>"Jane", 2=>"Thomas", 3=>"Robert", 4=>"Julia"}
{1=>"Jane", 2=>"Thomas", 3=>"Robert", 4=>"Julia"}

正如我们所看到的,最终的哈希包含来自 names1names2 哈希的键值对。

Ruby 哈希的 merge 和 merge! 方法

在最后一节中,我们回顾了一个常见的 Ruby 习惯用法。一些 Ruby 方法具有以感叹号结尾的对应方法。此标记没有任何语法意义,它表示该方法修改了调用该方法所针对的对象。

merging.rb
#!/usr/bin/ruby

names1 = Hash.new

names1[1] = "Jane"
names1[2] = "Thomas"

names2 = Hash.new

names2[3] = "Robert"
names2[4] = "Julia"

names = names1.merge names2
puts names
puts names1

names = names1.merge! names2
puts names
puts names1

我们在 mergemerge! 方法上演示了区别。

names = names1.merge names2

merge 不会修改 names1 哈希。它在它的副本上工作。

names = names1.merge! names2

merge! 方法在原始哈希上工作。names1 哈希被更改。

$ ./merging.rb
{1=>"Jane", 2=>"Thomas", 3=>"Robert", 4=>"Julia"}
{1=>"Jane", 2=>"Thomas"}
{1=>"Jane", 2=>"Thomas", 3=>"Robert", 4=>"Julia"}
{1=>"Jane", 2=>"Thomas", 3=>"Robert", 4=>"Julia"}

在本章中,我们学习了 Ruby 哈希。