Ruby 哈希

哈希是**键值对**的集合。它类似于其他编程语言中的字典。

哈希中的每个**值**都与一个**唯一的键**相关联,该键用于访问该值。键和值可以是任何对象(字符串、符号、数字等)。

以下是 Ruby 哈希的快速示例。您可以阅读本教程的其余部分以了解更多信息。

示例

# Using symbol keys
person = { name: "Gary", age: 32, city: "London" }

# Using string keys and hash rockets ( => )
student = { "name" => "Ash", "age" => 10, "grade" => "A" }

puts person
puts student

# Output:
# {name: "Gary", age: 32, city: "London"}
# {"name" => "Ash", "age" => 10, "grade" => "A"}

在这里,person 哈希具有Symbol 类型的键,并使用冒号: 来分隔键和值。student 哈希具有String 类型的键,并使用哈希火箭=> 来分隔键和值。


创建哈希

您可以通过以下两种方式之一创建哈希

  • 使用符号作为键,并使用冒号: 来分隔键和值。
  • 使用哈希火箭=> 来分隔键和值。

1. 使用符号键

创建具有符号键的哈希的语法是

hash_name = { key1: value1, key2: value2...keyN: valueN }

在这里,键和值由冒号: 分隔。例如,

person = { name: "Gary", age: 32, city: "London" }

符号比字符串占用的内存更少,并且符号比较更快。因此,您应该尽可能使用符号键。

注意: 符号键在现代 Ruby 中更常见且更受青睐。

2. 使用哈希火箭 (=>)

创建具有哈希火箭=> 的哈希的语法是

hash_name = { key1 => value1, key2 => value2...keyN => valueN }

在这里,键和值由=> 分隔。例如,

person = { "name" => "Gary", "age" => 32, "city" => "London" }

这是创建哈希的**经典语法**,如果您的键不能被Symbol 类型表示,您应该使用它。

注意: 为了方便理解,我们将在初始示例中使用哈希火箭=> 语法,然后切换到符号语法。


哈希键是唯一的

如果哈希中有重复的键,Ruby 会发出警告,并且只保留重复键的最后一次赋值。例如,

# Hash with a duplicate key "age"
person = { "name" => "Gary", "age" => 32, "age" => 25 }

puts person

输出

main.rb:2: warning: key "age" is duplicated and overwritten on line 2
{"name" => "Gary", "age" => 25}

在这里,哈希中有两个名为"age" 的键。Ruby 会用最新的覆盖早期的。

换句话说,它会删除键值对"age" => 32,但保留"age" => 25

提示: 创建您自己的哈希,其中包含两个以上相同的重复键,看看会发生什么。


访问哈希值

我们可以通过在方括号[] 中使用键来访问哈希中的值。如果键不存在,Ruby 会返回nil。例如,

person = { "name" => "Gary", "age" => 32, "city" => "London" }

# Access values whose keys exist
puts person["name"]
puts person["age"]
puts person["city"]

# Access value of a non-existent key
# Returns "nil" so nothing gets printed on the screen
puts person["country"]

# Print the class/type of the "nil" output
puts person["country"].class

输出

Gary
32
London

NilClass

此程序的工作原理如下:

代码 描述 输出
person["name"] 返回与键"name" 相关联的值。 Gary
person["age"] 返回与键"age" 相关联的值。 32
person["city"] 返回与键"city" 相关联的值。 London
person["country"] 返回与键"country" 相关联的值(该键不存在)。 这将返回nil,因为键不存在。在 Ruby 中,nil 表示“无”。因此不会打印任何内容。

正如您所看到的,我们可以使用语法hash_name[key] 访问任何值。但是,键必须存在,否则我们会得到空白输出或nil


修改哈希

您可以使用赋值运算符= 来更新或添加新的键值对。例如,

person = { "name" => "Gary", "age" => 32, "city" => "London" }

puts "Original Hash:"
puts person

# Update an existing key
person["name"] = "Robert"

# Add a new key
person["country"] = "UK"

puts "\nModified Hash:"
puts person

输出

Original Hash:
{"name" => "Gary", "age" => 32, "city" => "London"}

Modified Hash:
{"name" => "Robert", "age" => 32, "city" => "London", "country" => "UK"}

正如您所看到的,您可以使用以下语法来添加新的键值对或修改现有的键值对

hash_name[key] = value

使用符号键访问和修改值

让我们访问具有Symbol 类型键的哈希的值。

person = { name: "Gary", age: 32, city: "London" }

# Modify existing key
person[:age] = 25

# Add a new key
person[:country] = "UK"

# Access values
puts "Name: #{person[:name]}"
puts "Age: #{person[:age]}"
puts "City: #{person[:city]}"
puts "Country: #{person[:country]}"

输出

Name: Gary
Age: 25
City: London
Country: UK

在此示例中,我们使用了一个带有符号键的哈希。

在访问和修改/创建键时,请注意,符号键写在文本前面带有冒号

  • :name
  • :age
  • :city
  • :country

这是因为符号通常在名称前面带有冒号,如:age

但在创建哈希时,Ruby 允许使用**更简洁的语法**,其中冒号放在键名之后

person = { name: "Gary", age: 32, city: "London" }

您仍然可以使用正常的符号表示法,并在符号文本前放置冒号,但那时您需要使用哈希火箭=>

person = { :name => "Gary", :age => 32, :city => "London" }

正如您所看到的,这种语法不如前面的语法简洁。


Ruby 哈希方法

Ruby 哈希有许多实用的方法供您使用。一些常用的方法是

方法 描述
keys 返回哈希中的所有键。
values 返回哈希中的所有值。
fetch(k) 访问与键k 相关联的值,如果键不存在则返回错误。
each 迭代键值对。
key?(k) 检查键k 是否存在于哈希中。
value?(v) 检查值v 是否存在于哈希中。
delete(k) 通过键k 删除键值对。
clear 删除所有键值对。
empty? 如果哈希为空,则返回true
size 返回键值对的数量。

注意: key?value? 是不太推荐的 has_key?has_value? 方法的别名。我们更喜欢 key?value?,因为它们更简洁,并且与其他谓词方法(如 empty?even? 等)匹配。


示例 1:用于打印键和值的哈希方法

person = { name: "Gary", age: 32, city: "London" }

# Print only the keys of the hash
puts "Hash Keys:"
puts person.keys

# Print only the values of the hash
puts "\nHash Values:"
puts person.values

puts "\nAccessing Values Using fetch:"

# Use fetch to access values
puts person.fetch(:name)
puts person.fetch(:age)
puts person.fetch(:city)

# Error: Attempt to access a non-existent key
puts person.fetch(:country)

输出

Hash Keys:
name
age
city

Hash Values:
Gary
32
London

Accessing Values Using fetch:
Gary
32
London
main.rb:17:in 'Hash#fetch': key not found: :country (KeyError)

正如您所看到的,使用fetch 访问不存在的键会导致错误。


示例 2:用于检查键和值是否存在的哈希方法

person = { name: "Gary", age: 32, city: "London" }

# Check if the :city key exists
print "Key :city Exists? "
puts person.key?(:city)

# Check if the :country key exists
print "Key :country Exists? "
puts person.key?(:country)

# Check if the value 32 exists
print "\nValue 32 Exists? "
puts person.value?(32)

# Check if the value "UK" exists
print "Value 'UK' Exists? "
puts person.value?("UK")

输出

Key :city Exists? true
Key :country Exists? false

Value 32 Exists? true
Value 'UK' Exists? false

示例 3:用于删除键和检查大小的哈希方法

person = { name: "Gary", age: 32, city: "London" }

puts "Original Hash"
puts person

# Print the original size of the hash
print "Original Hash Size: "
puts person.size

# Delete :age key
person.delete(:age)

puts "\nHash After Deleting the Second Key"
puts person

# Print the new size of the hash
puts "\nNew Hash Size: #{person.size}"

# Check if the hash is empty
print "\nThe Hash is Empty? "
puts person.empty?

# Delete all key-value pairs
puts "\nClearing the Hash!"
person.clear

# Check if the hash is empty again
puts "\nThe Hash is Empty? #{person.empty?}"

输出

Original Hash
{name: "Gary", age: 32, city: "London"}

Original Hash Size: 3

Hash After Deleting the Second Key
{name: "Gary", city: "London"}

New Hash Size: 2

The Hash is Empty? false

Clearing the Hash!

The Hash is Empty? true

迭代哈希

您可以使用 each 方法来循环遍历哈希。例如,

person = { name: "Gary", age: 32, city: "London" }

person.each do |key, value|
    puts "#{key}: #{value}"
end

输出

name: Gary
age: 32
city: London

仅迭代键或值

您也可以像这样仅迭代键或仅迭代值

person = { name: "Gary", age: 32, city: "London" }

puts "Keys:"
person.keys.each { |key| puts key }

puts "\nValues:"
person.values.each { |value| puts value }

输出

Keys:
name
age
city

Values:
Gary
32
London

嵌套哈希

一个哈希也可以包含其他哈希。这种结构称为**嵌套哈希**。例如,

person = {
    Gary: { age: 32, city: "London" },
    Ash: {age: 10, city: "New York" }
}

# Print the person hash
puts person

# Access the hash associated with the key :Gary
# Then, print the value associated with :age

print "\nGary's Age: "
puts person[:Gary][:age]

# Access the hash associated with the key :Ash
# Then, print the value associated with :city

print "Ash's City: "
puts person[:Ash][:city]

输出

{Gary: {age: 32, city: "London"}, Ash: {age: 10, city: "New York"}}

Gary's Age: 32
Ash's City: New York

在这里,person 哈希在其内部嵌套了两个其他哈希。其工作原理如下

:Gary {age: 32, city: "London"}
:Ash {age: 10, city: "New York" }

正如您所看到的,键的值是哈希。


常见问题

哈希和数组有什么区别?

哈希和数组的运作方式不同,并且在不同的情况下很有用。

哈希以键值对的形式存储数据,其中每个键都是唯一的,并映射到特定的值。

数组以有序列表的形式存储数据,其中每个项都有一个索引。

何时使用哈希?

  • 当您需要存储键值对时。
  • 当您需要存储带有标签的结构化数据时。
  • 当您需要通过名称而不是位置来搜索数据时。

何时使用数组?

  • 当您需要存储项目的有序列表时。
  • 当您比标签更关心数据的**位置**时。
我可以使用非符号或非字符串键吗?

是的,您可以使用任何对象作为哈希键。例如,

# Create an empty hash
hash = {}

# Create a pair with integer key
hash[1] = "integer key"

# Create a pair with array key
hash[[1, 2]] = "array key"

puts hash

输出

{1 => "integer key", [1, 2] => "array key"}

在这里,我们在哈希中使用了整数键和数组键。

重要! 避免使用可变对象(如数组)作为键,因为它们的值可能会更改,从而使它们存在风险。

为什么我应该使用 fetch 来访问哈希值?

在 Ruby 中,您可以使用方括号[] 或使用fetch 方法来访问值。

为什么 fetch 更好?

但是,使用fetch 更好,因为它在您尝试访问不存在键的值时**会抛出错误**。这有助于检测代码中的错误和 bug。例如,

person = { name: "Gary", age: 32, city: "London" }

# Error: Attempt to access a non-existent key
puts person.fetch(:country)

# Output: in 'Hash#fetch': key not found: :country (KeyError)

方括号不会抛出错误。

相比之下,[] 只会为不存在的键返回nil。因此,您会发现调试代码很困难

person = { name: "Gary", age: 32, city: "London" }

# Attempt to access a non-existent key
# Code gives nil (blank output)
# No error message is printed

puts person[:country]

默认值可以防止 fetch 中的错误。

您可以通过使用fetch 的**默认值**来避免此错误。例如,

person = { name: "Gary", age: 32, city: "London" }

# Supply a default value
puts person.fetch(:country, "Not found")

# Output: Not found

在这里,fetch 方法提供了一个默认值“Not found”,当它查找的键不存在时,该值将被返回。

哈希可以有默认值吗?

是的,您可以提供一个默认值,当键不存在时将返回该值。有多种方法可以做到这一点,但让我们来看三种主要方法

1. 在哈希创建后使用 .default

# Create an empty hash
person = {}

# Create a default value
person.default = "Key not found!"

# Hash that returns a message when the key is not found
person = Hash.new("Key not found!")

# Try to access a non-existent key
puts person[:name]

# Create a key-value pair
person[:name] = "Gary"

# Access the key again
puts person[:name]

输出

Key not found!
Gary

在这里,我们将字符串"Key not found!" 定义为哈希的默认值。没有它,Ruby 会为缺失的键返回nil

2. 使用 new 方法。

# Create a hash that returns a message
# when the key is not found
person = Hash.new("Key not found!")

# Try to access a non-existent key
puts person[:name]

输出

Key not found!

在这里,默认值作为参数传递给new 方法。

3. 使用 new 方法的默认值块。

您还可以使用以下语法为默认值赋值

hash_name = Hash.new { |hash, key| block }

这里,

  • hash - 新创建的哈希。
  • key - 已访问但尚不存在的键。
  • block - 定义访问缺失键时要执行的操作。

例如,

person = Hash.new { |hash, key| hash[key] = "Key not found!" }

# Try to access a non-existent key
puts person[:name]

输出

Key not found!

在上面的示例中,代码hash[key] = "Key not found!" 对应于语法中给出的block

此代码为任何不存在的key 分配默认值"Key not found!"

我如何合并两个哈希?

您可以使用merge 方法来合并两个哈希

hash1 = { x: 1, y: 2 }
hash2 = { y: 3, z: 4 }

merged_hash = hash1.merge(hash2)

puts merged_hash

输出

{x: 1, y: 3, z: 4}

第二个哈希会覆盖任何重复的键。在我们的例子中,hash2 是第二个哈希,因为它包含在括号内

merged_hash = hash1.merge(hash2)

您可以通过以下代码使hash1 成为第二个哈希

merged_hash = hash2.merge(hash1)
我可以使用 for 循环迭代哈希吗?

是的,您可以使用 for 循环来迭代哈希,但 Ruby 开发者更喜欢 each 循环。

示例

person = { name: "Gary", age: 32, city: "London" }

for key, value in person
    puts "#{key}: #{value}"
end

输出

name: Gary
age: 32
city: London
你觉得这篇文章有帮助吗?

我们的高级学习平台,凭借十多年的经验和数千条反馈创建。

以前所未有的方式学习和提高您的编程技能。

试用 Programiz PRO
  • 交互式课程
  • 证书
  • AI 帮助
  • 2000+ 挑战