How do I assign multiple symbols to the same value in Ruby?

吃可爱长大的小学妹 提交于 2021-02-19 05:40:29

问题


The idea of what I am trying to do is to clump synonym symbols to the same value, without having to redefine the same value over and over. Basically turn this:

fruits = { orange: "Citrus", grapefruit: "Citrus", tangerine: "Citrus" }

Into this:

fruits = { orange:, grapefruit:, tangerine: => "Citrus" }

What is the proper syntax for accomplishing this?

Thanks


回答1:


Use a hash, in order to access the type of fruit using the fruit name. For example:

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}

Here, zip and to_h are used to simplify the hash creation and avoid repetitive code.




回答2:


Group Keys by Value; Optionally Transform Returned Values

In Ruby, a Symbol is a core class that provides an identifier for things, and the Symbol is never duplicated during runtime. Setting aside how they're used internally, the most common use case for using a Symbol in your code is to define keys in a Hash. You can use other types of keys, but the properties of a Symbol make them especially useful as Hash keys.

With that out of the way, it looks like you're trying to group similar Hash values, but it's unclear how you expect to use this grouping. There is more than one way to do this, so I'll just pick one as an example.

Given a Hash like this one:

produce =
  {
        :orange => "citrus",
    :grapefruit => "citrus",
     :tangerine => "citrus",
     :raspberry => "berry",
    :strawberry => "berry",
    :canteloupe => "melon",
      :honeydew => "melon"
  }

you can use Hash#group_by (inherited from Enumerable) to quickly sort your Hash by value. For example, using Ruby 3.0.0:

produce.group_by { _2 }
#=> 
{"citrus"=>
  [[:orange, "citrus"], [:grapefruit, "citrus"], [:tangerine, "citrus"]],
 "berry"=>[[:raspberry, "berry"], [:strawberry, "berry"]],
 "melon"=>[[:canteloupe, "melon"], [:honeydew, "melon"]]}

This returns a Hash grouped by your unique values, but you may prefer to discard the produce type in the nested Array objects. You can do that with Hash#transform_values like so:

produce.group_by { _2 }.transform_values { _1.map &:first }
#=> 
{"citrus"=>[:orange, :grapefruit, :tangerine],
 "berry"=>[:raspberry, :strawberry],
 "melon"=>[:canteloupe, :honeydew]}

Either way, the main point is that a Hash key is associated with a value that can be of almost any class, and so you can examine the contents of each value to determine whether or not they belong to the grouping you want (which is currently defined by your key).

Your current data structure isn't really optimized for retrieving types of produce (e.g. citrus fruits) easily, but it can certainly be done. However, you may want to reconsider whether you have the right data structure for the way you want to access or manipulate your data. Your mileage will certainly vary.




回答3:


Your comment about motherboards and blueprints suggest that you are given something like

h = { :mb1=>:bp3, :mb_2=>:bp1, :mb3=>:bp3, :mb4=>:bp2, :mb5=>:bp1 }

and want to produce the hash

{ :bp3=>[:mb1, :mb3], :bp1=>[:mb_2, :mb5], :bp2=>[:mb4] }

One of many ways to do that is the following:

h.each_with_object({}) { |(k,v),g| (g[v] ||= []) << k }

See Enumerable#each_with_object, and to understand why I've written the block varaiables |(k,v),g|, see array decomposition.

This is a condensed translation of the following code (which I've salted with three puts statement to show the calculations being performed):

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, :mb_2=>:bp1, :mb3=>:bp3, :mb4=>:bp2, :mb5=>:bp1}

The following is displayed:

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, :bp3], k = mb3, v = bp3
g after g[bp3] << {:bp3=>[:mb1, :mb3], :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, :mb3], :bp1=>[:mb_2], :bp2=>[:mb4]}

key_value_pair = [:mb5, :bp1], k = mb5, v = bp1
g after g[bp1] << {:bp3=>[:mb1, :mb3], :bp1=>[:mb_2, :mb5], :bp2=>[:mb4]}



回答4:


@Charles Persson, if I understood your question correctly, your main goal is to refactor this snippet:

hash = {
  :orange     => "citrus",
  :grapefruit => "citrus",
  :tangerine  => "citrus",
  :raspberry  => "berry",
  :cherry     => "drupe",
  :strawberry => "berry",
  :canteloupe => "melon",
  :honeydew   => "melon",
  :apple      => "pome"
}

to something similar to:

hash = {
  [:orange, :grapefruit, :tangerine] => "citrus",
  [:raspberry, :strawberry]          => "berry",
  :cherry                            => "drupe",
  [:canteloupe, :honeydew]           => "melon",
  :apple                             => "pome"
}

If I am right, then I can suggest implementing a method like this one:

# 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

or its shorter alternative:

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

This way, you will be able to write a code like the following:

hash = mhash({
  [:orange, :grapefruit, :tangerine] => "citrus",
  [:raspberry, :strawberry]          => "berry",
  :cherry                            => "drupe",
  [:canteloupe, :honeydew]           => "melon",
  :apple                             => "pome"
})

p hash
# => {
  :orange     => "citrus",
  :grapefruit => "citrus",
  :tangerine  => "citrus",
  :raspberry  => "berry",
  :strawberry => "berry",
  :cherry     => "drupe",
  :canteloupe => "melon",
  :honeydew   => "melon",
  :apple      => "pome"
}

Otherwise, please provide a better explanation of your question. Thanks.



来源:https://stackoverflow.com/questions/65926954/how-do-i-assign-multiple-symbols-to-the-same-value-in-ruby

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!