哈希是**键值对**的集合。它类似于其他编程语言中的字典。
哈希中的每个**值**都与一个**唯一的键**相关联,该键用于访问该值。键和值可以是任何对象(字符串、符号、数字等)。
以下是 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"}
在这里,我们在哈希中使用了整数键和数组键。
重要! 避免使用可变对象(如数组)作为键,因为它们的值可能会更改,从而使它们存在风险。
在 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 循环来迭代哈希,但 Ruby 开发者更喜欢 each
循环。
示例
person = { name: "Gary", age: 32, city: "London" }
for key, value in person
puts "#{key}: #{value}"
end
输出
name: Gary age: 32 city: London