问题描述
我想要做的想法是将同义词符号聚集到相同的值,而不必一遍又一遍地重新定义相同的值。基本上转这个:
fruits = { orange: "Citrus",grapefruit: "Citrus",tangerine: "Citrus" }
进入这个:
fruits = { orange:,grapefruit:,tangerine: => "Citrus" }
完成此操作的正确语法是什么?
谢谢
解决方法
使用哈希,以便使用水果名称访问水果类型。例如:
fruits = %i{ orange grapefruit tangerine apple }
citrus_fruits = %i{ orange grapefruit tangerine }
fruit_type = citrus_fruits.zip([:citrus] * citrus_fruits.length).to_h
fruit_type[:apple] = :rosaceae
puts fruit_type
# {:orange=>:citrus,:grapefruit=>:citrus,:tangerine=>:citrus,:apple=>:rosaceae}
这里,zip
和 to_h
用于简化哈希创建并避免重复代码。
按值分组键;可选地转换返回值
在 Ruby 中,Symbol 是为事物提供标识符的核心类,并且 Symbol 在运行时永远不会重复。撇开它们在内部如何使用,在代码中使用 Symbol 的最常见用例是在 Hash 中定义键。您可以使用其他类型的键,但 Symbol 的属性使它们作为哈希键特别有用。
除此之外,您似乎正在尝试对相似的哈希值进行分组,但不清楚您希望如何使用这种分组。有不止一种方法可以做到这一点,所以我只举一个例子。
给定一个这样的哈希:
produce =
{
:orange => "citrus",:grapefruit => "citrus",:tangerine => "citrus",:raspberry => "berry",:strawberry => "berry",:canteloupe => "melon",:honeydew => "melon"
}
您可以使用 Hash#group_by(继承自 Enumerable)按值快速对您的 Hash 进行排序。例如,使用 Ruby 3.0.0:
produce.group_by { _2 }
#=>
{"citrus"=>
[[:orange,"citrus"],[:grapefruit,[:tangerine,"citrus"]],"berry"=>[[:raspberry,"berry"],[:strawberry,"berry"]],"melon"=>[[:canteloupe,"melon"],[:honeydew,"melon"]]}
这将返回按您的唯一值分组的哈希,但您可能更愿意放弃嵌套 Array 对象中的生产类型。您可以像这样使用 Hash#transform_values 做到这一点:
produce.group_by { _2 }.transform_values { _1.map &:first }
#=>
{"citrus"=>[:orange,:grapefruit,:tangerine],"berry"=>[:raspberry,:strawberry],"melon"=>[:canteloupe,:honeydew]}
无论哪种方式,重点是哈希键与几乎可以属于任何类的值相关联,因此您可以检查每个值的内容以确定它们是否属于您想要的分组(当前由您的密钥定义)。
您当前的数据结构并未真正针对轻松检索产品类型(例如柑橘类水果)进行优化,但当然可以做到。但是,您可能需要重新考虑是否拥有适合访问或操作数据的方式的正确数据结构。您的里程肯定会有所不同。
,你对主板和蓝图的评论表明你得到了类似的东西
h = { :mb1=>:bp3,:mb_2=>:bp1,:mb3=>:bp3,:mb4=>:bp2,:mb5=>:bp1 }
并想要生成哈希
{ :bp3=>[:mb1,:mb3],:bp1=>[:mb_2,:mb5],:bp2=>[:mb4] }
执行此操作的众多方法之一如下:
h.each_with_object({}) { |(k,v),g| (g[v] ||= []) << k }
请参阅 Enumerable#each_with_object,并了解我为何编写块变量 |(k,g|
,请参阅 array decomposition。
这是以下代码的精简翻译(我用三个 puts
语句加盐以显示正在执行的计算):
g = {}
h.each do |key_value_pair|
k,v = key_value_pair
puts "\nkey_value_pair = #{key_value_pair},k = #{k},v = #{v}"
puts "g[#{v}] set to [] because g[#{v}] == nil" if g[v].nil?
g[v] = [] if g[v].nil?
g[v] << k
puts "g after g[#{v}] << #{g}"
end
#=> {:mb1=>:bp3,:mb5=>:bp1}
显示如下:
key_value_pair = [:mb1,:bp3],k = mb1,v = bp3
g[bp3] set to [] because g[bp3] == nil
g after g[bp3] << {:bp3=>[:mb1]}
key_value_pair = [:mb_2,:bp1],k = mb_2,v = bp1
g[bp1] set to [] because g[bp1] == nil
g after g[bp1] << {:bp3=>[:mb1],:bp1=>[:mb_2]}
key_value_pair = [:mb3,k = mb3,v = bp3
g after g[bp3] << {:bp3=>[:mb1,:bp1=>[:mb_2]}
key_value_pair = [:mb4,:bp2],k = mb4,v = bp2
g[bp2] set to [] because g[bp2] == nil
g after g[bp2] << {:bp3=>[:mb1,:bp1=>[:mb_2],:bp2=>[:mb4]}
key_value_pair = [:mb5,k = mb5,v = bp1
g after g[bp1] << {:bp3=>[:mb1,:bp2=>[:mb4]}
,
@Charles Persson,如果我正确理解您的问题,您的主要目标是重构此代码段:
hash = {
:orange => "citrus",:tangerine => "citrus",:raspberry => "berry",:cherry => "drupe",:honeydew => "melon",:apple => "pome"
}
类似于:
hash = {
[:orange,:tangerine] => "citrus",[:raspberry,:strawberry] => "berry",:cherry => "drupe",[:canteloupe,:honeydew] => "melon",:apple => "pome"
}
如果我是对的,那么我可以建议实施这样的方法:
# source - an input hash that can contain arrays as keys.
# dest - a new output hash where all array keys are replaced by singular keys.
def mhash(source)
dest = {}
source.each_pair do |key,value|
if key.instance_of?(Array)
key.each do |sub_key|
dest[sub_key] = value
end
else
dest[key] = value
end
end
dest
end
或其较短的替代方案:
def mhash(source)
source.each_pair.with_object({}) do |(key,value),dest|
key.instance_of?(Array) ? key.each { |sub_key| dest[sub_key] = value } : dest[key] = value
end
end
这样,您将能够编写如下代码:
hash = mhash({
[:orange,:apple => "pome"
})
p hash
# => {
:orange => "citrus",:apple => "pome"
}
否则,请更好地解释您的问题。谢谢。