Sort hash by unique value - ruby

I'm using the cinch IRC bot framework to create an irc bot that intelligently kicks a user from the channel. In order to do that, I need to know the bot's user modes and compare it to the user's user modes. There's 4 modes that matter: q, a, o, h.
Basically: owners (q) > admins (a) > operators (o) > half-ops (h) > *. Users can have multiple usermodes. Someone can have the modes qao, but I don't care if the user is an owner and an admin, I only care that the user is an owner.
Cinch provides a hash of all known users in the channel and their usermodes. For example:
Users{"bot" => {"a", "o"}, "fred" => {"q", "o"}, "mike" => "o", "larry" => "v"}
What I'm looking to do is, as concisely as possible, take some logic that can interpret when the bot is, for example, "ao", fred is "qo" and mike is "o" and then say "OK. I'm an admin and mike is an operator, so I can kick mike, but fred is an owner so I can't kick fred."
My idea of implementation is messy (involves a lot of if then else looping..) and I know there has to be a better way. In addition, I'm not really sure how to iterate through a key value and ignore the values at a certain point. I feel as though my idea would iterate, come across an "ao" and set as an admin, then reset as an operator which would not be what I need.
Thanks for the help.

#I, #carolclarinet has the right idea, and you probably don't need any more than that. However, I would like to mention a somewhat more elaborate way of dealing with the problem, which you might find useful in related applications. First, suppose your hash looks like this:
h = {"bot" => ["a", "o"], "fred" => ["q", "o"], "mike" => ["o"], "larry" => ["h"]}
This is just what you have, except I've made all the single hash values arrays. Obviously, it would be simple to get it into this form. Below I show how you can redefine the value arrays as being instances of a new class that I've called UserModes, that is a subclass of Array. By doing that, you can compare the value arrays in a very natural way:
h["bot"] # => ["a", "o"]
h["fred"] # => ["q", "o"]
h["bot"].can_kick? h["fred"] # => false
h["mike"] # => ["o"]
h["bot"].can_kick? h["mike"] # => true
h["larry"] # => ["h"]
h["bot"].can_kick? h["larry"] # => true
kickees[]
h.each {|k,v| kickees << k if k!="bot" && h["bot"].can_kick?(h[k])} # final () req'd
# kickees => ["mike", "larry"]
If you add the other methods:
h["bot"] < h["fred"] # => true
h["bot"] >= h["fred"] # => false
h["bot"] == h["mike"] # => false
h["bot"] > h["mike"] # => true
h["bot"] <= h["larry"] # => false
h["bot"] >= h["larry"] # => true
Here's the class definition:
class UserModes < Array
# Change > to >= in in can_kick? if I've misunderstood the kicking rule
def can_kick?(other) rating(self) > rating(other) end
# Add any or all of the following if useful to you:
def <=>(other) rating(self) <=> rating(other) end
def ==(other) rating(self) == rating(other) end
def <(other) rating(self) < rating(other) end
def >(other) rating(self) > rating(other) end
def <=(other) rating(self) <= rating(other) end
def >=(other) rating(self) >= rating(other) end
private
def rating a
case
when (a.include? ?q) then 3
when (a.include? ?a) then 2
when (a.include? ?o) then 1
when (a.include? ?v) then 0
else
# raise exception
end
end
end
You see that all the public methods make use of the private method rating, which converts an array to a numeric score, along the lines of #carolclarinet's answer. You can convert each hash value from an instance of Array to an instance of UserModes like this:
h.each_key {|k| h[k] = UserModes.new(h[k])}
We can confirm this works as intended:
h["fred"].class => UserModes
You can treat the values h as ordinary arrays, but you now also have the method can_kick? and, if you want, several others. Some of those (< <= >= >) are not defined for Array objects; others (<=> ==) must be defined to override methods of the same name in Array.

I would think about it like taking the "greatest" usermode the bot has and comparing it to the "greatest" usermode that the user you'd like to kick is. If the bot's is greater than or equal to the other user's, then the bot can kick the user.
In that case, you'd need a method that takes a list of usermodes and could return a numerical value corresponding to the greatest one. One implementation could be:
def greatest_usermode(array_of_usermodes)
if array_of_usermodes.include?("q")
4
elsif array_of_usermodes.include?("a")
3
elsif array_of_usermodes.include?("o")
2
elsif array_of_usermodes.include?("h")
1
else
0
end
end
Then you could define a method that just uses the mathematical >= operator to compare the numbers returned from this method for the bot and the user in question.
There are many other ways this could be done, but this is a pretty simple and straightforward way to do it.

Related

Updating multiple hash keys after merging values

I have a hash where multiple keys (lets say 1-5) point to one object (lets call a).
Keys (6-10) points to another object(say, b).
At some point I merged "b" into "a", now i have to make sure everyone sees the same object (also merging "a" into "b" and creating two objects with same content is not an option)
Is there a way to make any reference to "b" just redirect to "a" (keys 1-10 now point to object a) without manually updating keys 6-10 ?
You can't switch out one object for another unless you have some kind of a wrapper. Unless performance matters a lot, the easiest wrappers to use are proxy objects, because you don't need to unwrap them: they transparently behave exactly like the wrapped object.
class ProxyObject
# thanks to https://alisdair.mcdiarmid.org/invisible-proxies-with-ruby/
instance_methods.each do |m|
undef_method(m) unless m =~ /(^__|^nil\?$|^send$|^object_id$)/
end
attr_accessor :target
def initialize(target)
#target = target
end
def respond_to?(symbol, include_priv=false)
#target.respond_to?(symbol, include_priv)
end
private def method_missing(method, *args, &block)
#target.send(method, *args, &block)
end
end
a = 1
b = 10
a_proxy = ProxyObject.new(a)
b_proxy = ProxyObject.new(b)
a_proxy.class # verify how well they masquerade
# => Integer
hash = 10.times.map { |i| [i + 1, i < 5 ? a_proxy : b_proxy] }.to_h
# => {1=>1, 2=>1, 3=>1, 4=>1, 5=>1, 6=>10, 7=>10, 8=>10, 9=>10, 10=>10}
hash.values.sum() # confirm it behaves exactly like a number
# => 55
b_proxy.target = a_proxy.target # switch reference
hash
# => {1=>1, 2=>1, 3=>1, 4=>1, 5=>1, 6=>1, 7=>1, 8=>1, 9=>1, 10=>1}
hash.values.sum() # confirm the reference is changed
# => 10
I think I found an answer but I still have to code it
instead of the hash having an object, it will contain an array
array[0] will originally point to itself, array[1] will be the actual object
so this is the setup: hash1-5 points to arr1, hash6-10 points to arr2, arr1[0] points to itself and arr1[0]
after merging arr2[1] (b in the original question) into arr1[1] (a into the original question), I will update arr2[0] to point to arr1.
finally, after every key retrieval I will run something along the lines of
test = hash[6]
while test[0] != test
test = test[0]
end

avoid keys duplication to get a random hash key

I need to pick a hash entry at random, so I do
h = {1 => 'one', 2 => 'two', 3 => 'three'}
k = h.keys.sample
result = h[k]
Since h.keys creates a new array I do not like it. Is there a way to avoid creating a new array every time?
This will not generate another array. On average hash_random_value will iterate halfway through the given hash to produce a random value.
def hash_random_value(h)
i = rand(h.length)
h.each_with_index do |(_, v), i2|
return v if i == i2
end
end
h = {1 => 'one', 2 => 'two', 3 => 'three'}
hash_random_value(h)
This being said, you should optimize only when you are certain you need to do that. The only way you can know is to profile your code, otherwise you are most likely doing premature optimisation. I.e. complicating your code and increasing the chance of introducing bugs--sometimes even decreasing the performance of your program. Your original solution is much easier to understand than mine, and it is immediately obvious that it is correct.
I'd like to first reiterate what most people are saying: this probably doesn't matter.
Second, I'll point out that it sure seems like you want a random value, not a random key. Maybe that's just because your example snippet of code doesn't show what you're really doing.
If you very frequently need a random value, and very infrequently update the Hash, I'd recommend caching the values any time the Hash is modified and then taking a random value from the cache. One way to do that might be like this:
class RandomValueHash < Hash
def []=(k, v)
super(k, v)
#values = self.values
end
def sample_value
#values ||= self.values
#values.sample
end
end
rvh = RandomValueHash[{1 => 'one', 2 => 'two', 3 => 'three'}]
rvh.sample_value
# => "one"
rvh[4] = 'four'
rvh[5] = 'five'
rvh.sample_value
# => "four"
Of course, if you really do want a random key rather than value, the exact same concept applies. Either way, this avoids recreating the Array every time you get a value; it only creates it when necessary.
If you need to make the random sample a lot, and need it to be efficient, then perhaps a Ruby Hash is not the right data structure or storage for your problem. Even a wrapper class that maintained Hash and Array attributes together might work well - if for instance for every write to the hash you needed to read 20 random samples.
Whether or not that works for you not only depends on the ratio of reading and writing, it also relates to the logical structure of your problem data (as opposed to how you've chosen to represent it in your solution).
But before you set off on re-thinking your problem, you need to have a practical need for higher performance in the affected code. The hash would need to be pretty large in order to have a noticeable cost to fetching its keys. h.keys takes about 250ms when the hash has 1 million entries on my laptop.
How about...
h = {1 => 'one', 2 => 'two', 3 => 'three'}
k = h.keys
...
result = h[k.sample]
You can do the result = h[k.sample] times as often as you like, and it won't be regenerating the k array. However, you should regenerate k any time h changes.
ADDENDUM: I'm throwing in benchmark code for several of the proposed solutions. Enjoy.
#!/usr/bin/env ruby
require 'benchmark'
NUM_ITERATIONS = 1_000_000
def hash_random_value(h)
i = rand(h.length)
h.each_with_index do |(_, v), i2|
return v if i == i2
end
end
class RandomValueHash < Hash
def []=(k, v)
super(k, v)
#values = self.values
end
def sample_value
#values ||= self.values
#values.sample
end
end
Benchmark.bmbm do |b|
h = {1 => 'one', 2 => 'two', 3 => 'three'}
b.report("original proposal") do
NUM_ITERATIONS.times {k = h.keys.sample; result = h[k]}
end
b.report("hash_random_value") do
NUM_ITERATIONS.times {result = hash_random_value(h)}
end
b.report("manual keyset") do
k = h.keys
NUM_ITERATIONS.times {result = h[k.sample]}
end
rvh = RandomValueHash[{1 => 'one', 2 => 'two', 3 => 'three'}]
b.report("RandomValueHash") do
NUM_ITERATIONS.times {result = rvh.sample_value}
end
end
Not really. Hashes don't have an index so you either convert them to an Array and pick a random index or you Enumerate your hash for a random number of times. You should benchmark which method is fastest but I doubt you can avoid creating a new object.
If you don't care about your object you could shift it's keys for a random number of times but then you'd cerate Arrays for return values.
Unless you have a gigantic hash, this is a pointless concern. Ruby is no efficiency powerhouse, and if you're that worried about this, you should be using C(++).
something like this:
h.each_with_index.reduce(nil) {|m, ((_, v), i)|
rand(i + 1) == 0 ? v : m
}

What would be the equivalent of java enum in Ruby [duplicate]

What's the best way to implement the enum idiom in Ruby? I'm looking for something which I can use (almost) like the Java/C# enums.
Two ways. Symbols (:foo notation) or constants (FOO notation).
Symbols are appropriate when you want to enhance readability without littering code with literal strings.
postal_code[:minnesota] = "MN"
postal_code[:new_york] = "NY"
Constants are appropriate when you have an underlying value that is important. Just declare a module to hold your constants and then declare the constants within that.
module Foo
BAR = 1
BAZ = 2
BIZ = 4
end
flags = Foo::BAR | Foo::BAZ # flags = 3
Added 2021-01-17
If you are passing the enum value around (for example, storing it in a database) and you need to be able to translate the value back into the symbol, there's a mashup of both approaches
COMMODITY_TYPE = {
currency: 1,
investment: 2,
}
def commodity_type_string(value)
COMMODITY_TYPE.key(value)
end
COMMODITY_TYPE[:currency]
This approach inspired by andrew-grimm's answer https://stackoverflow.com/a/5332950/13468
I'd also recommend reading through the rest of the answers here since there are a lot of ways to solve this and it really boils down to what it is about the other language's enum that you care about
I'm surprised that no one has offered something like the following (harvested from the RAPI gem):
class Enum
private
def self.enum_attr(name, num)
name = name.to_s
define_method(name + '?') do
#attrs & num != 0
end
define_method(name + '=') do |set|
if set
#attrs |= num
else
#attrs &= ~num
end
end
end
public
def initialize(attrs = 0)
#attrs = attrs
end
def to_i
#attrs
end
end
Which can be used like so:
class FileAttributes < Enum
enum_attr :readonly, 0x0001
enum_attr :hidden, 0x0002
enum_attr :system, 0x0004
enum_attr :directory, 0x0010
enum_attr :archive, 0x0020
enum_attr :in_rom, 0x0040
enum_attr :normal, 0x0080
enum_attr :temporary, 0x0100
enum_attr :sparse, 0x0200
enum_attr :reparse_point, 0x0400
enum_attr :compressed, 0x0800
enum_attr :rom_module, 0x2000
end
Example:
>> example = FileAttributes.new(3)
=> #<FileAttributes:0x629d90 #attrs=3>
>> example.readonly?
=> true
>> example.hidden?
=> true
>> example.system?
=> false
>> example.system = true
=> true
>> example.system?
=> true
>> example.to_i
=> 7
This plays well in database scenarios, or when dealing with C style constants/enums (as is the case when using FFI, which RAPI makes extensive use of).
Also, you don't have to worry about typos causing silent failures, as you would with using a hash-type solution.
I use the following approach:
class MyClass
MY_ENUM = [MY_VALUE_1 = 'value1', MY_VALUE_2 = 'value2']
end
I like it for the following advantages:
It groups values visually as one whole
It does some compilation-time checking (in contrast with just using symbols)
I can easily access the list of all possible values: just MY_ENUM
I can easily access distinct values: MY_VALUE_1
It can have values of any type, not just Symbol
Symbols may be better cause you don't have to write the name of outer class, if you are using it in another class (MyClass::MY_VALUE_1)
The most idiomatic way to do this is to use symbols. For example, instead of:
enum {
FOO,
BAR,
BAZ
}
myFunc(FOO);
...you can just use symbols:
# You don't actually need to declare these, of course--this is
# just to show you what symbols look like.
:foo
:bar
:baz
my_func(:foo)
This is a bit more open-ended than enums, but it fits well with the Ruby spirit.
Symbols also perform very well. Comparing two symbols for equality, for example, is much faster than comparing two strings.
If you are using Rails 4.2 or greater you can use Rails enums.
Rails now has enums by default without the need for including any gems.
This is very similar (and more with features) to Java, C++ enums.
Quoted from http://edgeapi.rubyonrails.org/classes/ActiveRecord/Enum.html :
class Conversation < ActiveRecord::Base
enum status: [ :active, :archived ]
end
# conversation.update! status: 0
conversation.active!
conversation.active? # => true
conversation.status # => "active"
# conversation.update! status: 1
conversation.archived!
conversation.archived? # => true
conversation.status # => "archived"
# conversation.update! status: 1
conversation.status = "archived"
# conversation.update! status: nil
conversation.status = nil
conversation.status.nil? # => true
conversation.status # => nil
I know it's been a long time since the guy posted this question, but I had the same question and this post didn't give me the answer. I wanted an easy way to see what the number represents, easy comparison, and most of all ActiveRecord support for lookup using the column representing the enum.
I didn't find anything, so I made an awesome implementation called yinum which allowed everything I was looking for. Made ton of specs, so I'm pretty sure it's safe.
Some example features:
COLORS = Enum.new(:COLORS, :red => 1, :green => 2, :blue => 3)
=> COLORS(:red => 1, :green => 2, :blue => 3)
COLORS.red == 1 && COLORS.red == :red
=> true
class Car < ActiveRecord::Base
attr_enum :color, :COLORS, :red => 1, :black => 2
end
car = Car.new
car.color = :red / "red" / 1 / "1"
car.color
=> Car::COLORS.red
car.color.black?
=> false
Car.red.to_sql
=> "SELECT `cars`.* FROM `cars` WHERE `cars`.`color` = 1"
Car.last.red?
=> true
This is my approach to enums in Ruby. I was going for short and sweet, not necessarily the the most C-like. Any thoughts?
module Kernel
def enum(values)
Module.new do |mod|
values.each_with_index{ |v,i| mod.const_set(v.to_s.capitalize, 2**i) }
def mod.inspect
"#{self.name} {#{self.constants.join(', ')}}"
end
end
end
end
States = enum %w(Draft Published Trashed)
=> States {Draft, Published, Trashed}
States::Draft
=> 1
States::Published
=> 2
States::Trashed
=> 4
States::Draft | States::Trashed
=> 5
Check out the ruby-enum gem, https://github.com/dblock/ruby-enum.
class Gender
include Enum
Gender.define :MALE, "male"
Gender.define :FEMALE, "female"
end
Gender.all
Gender::MALE
Perhaps the best lightweight approach would be
module MyConstants
ABC = Class.new
DEF = Class.new
GHI = Class.new
end
This way values have associated names, as in Java/C#:
MyConstants::ABC
=> MyConstants::ABC
To get all values, you can do
MyConstants.constants
=> [:ABC, :DEF, :GHI]
If you want an enum's ordinal value, you can do
MyConstants.constants.index :GHI
=> 2
If you're worried about typos with symbols, make sure your code raises an exception when you access a value with a non-existent key. You can do this by using fetch rather than []:
my_value = my_hash.fetch(:key)
or by making the hash raise an exception by default if you supply a non-existent key:
my_hash = Hash.new do |hash, key|
raise "You tried to access using #{key.inspect} when the only keys we have are #{hash.keys.inspect}"
end
If the hash already exists, you can add on exception-raising behaviour:
my_hash = Hash[[[1,2]]]
my_hash.default_proc = proc do |hash, key|
raise "You tried to access using #{key.inspect} when the only keys we have are #{hash.keys.inspect}"
end
Normally, you don't have to worry about typo safety with constants. If you misspell a constant name, it'll usually raise an exception.
Another solution is using OpenStruct. Its pretty straight forward and clean.
https://ruby-doc.org/stdlib-2.3.1/libdoc/ostruct/rdoc/OpenStruct.html
Example:
# bar.rb
require 'ostruct' # not needed when using Rails
# by patching Array you have a simple way of creating a ENUM-style
class Array
def to_enum(base=0)
OpenStruct.new(map.with_index(base).to_h)
end
end
class Bar
MY_ENUM = OpenStruct.new(ONE: 1, TWO: 2, THREE: 3)
MY_ENUM2 = %w[ONE TWO THREE].to_enum
def use_enum (value)
case value
when MY_ENUM.ONE
puts "Hello, this is ENUM 1"
when MY_ENUM.TWO
puts "Hello, this is ENUM 2"
when MY_ENUM.THREE
puts "Hello, this is ENUM 3"
else
puts "#{value} not found in ENUM"
end
end
end
# usage
foo = Bar.new
foo.use_enum 1
foo.use_enum 2
foo.use_enum 9
# put this code in a file 'bar.rb', start IRB and type: load 'bar.rb'
It all depends how you use Java or C# enums. How you use it will dictate the solution you'll choose in Ruby.
Try the native Set type, for instance:
>> enum = Set['a', 'b', 'c']
=> #<Set: {"a", "b", "c"}>
>> enum.member? "b"
=> true
>> enum.member? "d"
=> false
>> enum.add? "b"
=> nil
>> enum.add? "d"
=> #<Set: {"a", "b", "c", "d"}>
Someone went ahead and wrote a ruby gem called Renum. It claims to get the closest Java/C# like behavior. Personally I'm still learning Ruby, and I was a little shocked when I wanted to make a specific class contain a static enum, possibly a hash, that it wasn't exactly easily found via google.
Recently we released a gem that implements Enums in Ruby. In my post you will find the answers on your questions. Also I described there why our implementation is better than existing ones (actually there are many implementations of this feature in Ruby yet as gems).
Symbols is the ruby way. However, sometimes one need to talk to some C code or something or Java that expose some enum for various things.
#server_roles.rb
module EnumLike
def EnumLike.server_role
server_Symb=[ :SERVER_CLOUD, :SERVER_DESKTOP, :SERVER_WORKSTATION]
server_Enum=Hash.new
i=0
server_Symb.each{ |e| server_Enum[e]=i; i +=1}
return server_Symb,server_Enum
end
end
This can then be used like this
require 'server_roles'
sSymb, sEnum =EnumLike.server_role()
foreignvec[sEnum[:SERVER_WORKSTATION]]=8
This is can of course be made abstract and you can roll our own Enum class
I have implemented enums like that
module EnumType
def self.find_by_id id
if id.instance_of? String
id = id.to_i
end
values.each do |type|
if id == type.id
return type
end
end
nil
end
def self.values
[#ENUM_1, #ENUM_2]
end
class Enum
attr_reader :id, :label
def initialize id, label
#id = id
#label = label
end
end
#ENUM_1 = Enum.new(1, "first")
#ENUM_2 = Enum.new(2, "second")
end
then its easy to do operations
EnumType.ENUM_1.label
...
enum = EnumType.find_by_id 1
...
valueArray = EnumType.values
module Status
BAD = 13
GOOD = 24
def self.to_str(status)
for sym in self.constants
if self.const_get(sym) == status
return sym.to_s
end
end
end
end
mystatus = Status::GOOD
puts Status::to_str(mystatus)
Output:
GOOD
This seems a bit superfluous, but this is a methodology that I have used a few times, especially where I am integrating with xml or some such.
#model
class Profession
def self.pro_enum
{:BAKER => 0,
:MANAGER => 1,
:FIREMAN => 2,
:DEV => 3,
:VAL => ["BAKER", "MANAGER", "FIREMAN", "DEV"]
}
end
end
Profession.pro_enum[:DEV] #=>3
Profession.pro_enum[:VAL][1] #=>MANAGER
This gives me the rigor of a c# enum and it is tied to the model.
Most people use symbols (that's the :foo_bar syntax). They're sort of unique opaque values. Symbols don't belong to any enum-style type so they're not really a faithful representation of C's enum type but this is pretty much as good as it gets.
Sometimes all I need is to be able to fetch enum's value and identify its name similar to java world.
module Enum
def get_value(str)
const_get(str)
end
def get_name(sym)
sym.to_s.upcase
end
end
class Fruits
include Enum
APPLE = "Delicious"
MANGO = "Sweet"
end
Fruits.get_value('APPLE') #'Delicious'
Fruits.get_value('MANGO') # 'Sweet'
Fruits.get_name(:apple) # 'APPLE'
Fruits.get_name(:mango) # 'MANGO'
This to me serves the purpose of enum and keeps it very extensible too. You can add more methods to the Enum class and viola get them for free in all the defined enums. for example. get_all_names and stuff like that.
Try the inum.
https://github.com/alfa-jpn/inum
class Color < Inum::Base
define :RED
define :GREEN
define :BLUE
end
Color::RED
Color.parse('blue') # => Color::BLUE
Color.parse(2) # => Color::GREEN
see more https://github.com/alfa-jpn/inum#usage
Another approach is to use a Ruby class with a hash containing names and values as described in the following RubyFleebie blog post. This allows you to convert easily between values and constants (especially if you add a class method to lookup the name for a given value).
I think the best way to implement enumeration like types is with symbols since the pretty much behave as integer (when it comes to performace, object_id is used to make comparisons ); you don't need to worry about indexing and they look really neat in your code xD
irb(main):016:0> num=[1,2,3,4]
irb(main):017:0> alph=['a','b','c','d']
irb(main):018:0> l_enum=alph.to_enum
irb(main):019:0> s_enum=num.to_enum
irb(main):020:0> loop do
irb(main):021:1* puts "#{s_enum.next} - #{l_enum.next}"
irb(main):022:1> end
Output:
1 - a
2 - b
3 - c
4 - d
Another way to mimic an enum with consistent equality handling (shamelessly adopted from Dave Thomas). Allows open enums (much like symbols) and closed (predefined) enums.
class Enum
def self.new(values = nil)
enum = Class.new do
unless values
def self.const_missing(name)
const_set(name, new(name))
end
end
def initialize(name)
#enum_name = name
end
def to_s
"#{self.class}::##enum_name"
end
end
if values
enum.instance_eval do
values.each { |e| const_set(e, enum.new(e)) }
end
end
enum
end
end
Genre = Enum.new %w(Gothic Metal) # creates closed enum
Architecture = Enum.new # creates open enum
Genre::Gothic == Genre::Gothic # => true
Genre::Gothic != Architecture::Gothic # => true

Calculate sum of objects for each unique object property in Ruby

I was helping with an answer in this question and it sparked a question of my own.
Pie is an object that has a pieces array made of of PiePiece objects.
Each PiePiece has a flavor attribute
How do I create a hash that looks like this:
# flavor => number of pieces
{
:cherry => 3
:apple => 1
:strawberry => 2
}
This works, but I think it could be improved
def inventory
hash = {}
pieces.each do |p|
hash[p.flavor] ||= 0
hash[p.flavor] += 1
end
hash
end
Any ideas?
def inventory
Hash[pieces.group_by(&:flavor).map{|f,p| [f, p.size]}]
end

How to implement Enums in Ruby?

What's the best way to implement the enum idiom in Ruby? I'm looking for something which I can use (almost) like the Java/C# enums.
Two ways. Symbols (:foo notation) or constants (FOO notation).
Symbols are appropriate when you want to enhance readability without littering code with literal strings.
postal_code[:minnesota] = "MN"
postal_code[:new_york] = "NY"
Constants are appropriate when you have an underlying value that is important. Just declare a module to hold your constants and then declare the constants within that.
module Foo
BAR = 1
BAZ = 2
BIZ = 4
end
flags = Foo::BAR | Foo::BAZ # flags = 3
Added 2021-01-17
If you are passing the enum value around (for example, storing it in a database) and you need to be able to translate the value back into the symbol, there's a mashup of both approaches
COMMODITY_TYPE = {
currency: 1,
investment: 2,
}
def commodity_type_string(value)
COMMODITY_TYPE.key(value)
end
COMMODITY_TYPE[:currency]
This approach inspired by andrew-grimm's answer https://stackoverflow.com/a/5332950/13468
I'd also recommend reading through the rest of the answers here since there are a lot of ways to solve this and it really boils down to what it is about the other language's enum that you care about
I'm surprised that no one has offered something like the following (harvested from the RAPI gem):
class Enum
private
def self.enum_attr(name, num)
name = name.to_s
define_method(name + '?') do
#attrs & num != 0
end
define_method(name + '=') do |set|
if set
#attrs |= num
else
#attrs &= ~num
end
end
end
public
def initialize(attrs = 0)
#attrs = attrs
end
def to_i
#attrs
end
end
Which can be used like so:
class FileAttributes < Enum
enum_attr :readonly, 0x0001
enum_attr :hidden, 0x0002
enum_attr :system, 0x0004
enum_attr :directory, 0x0010
enum_attr :archive, 0x0020
enum_attr :in_rom, 0x0040
enum_attr :normal, 0x0080
enum_attr :temporary, 0x0100
enum_attr :sparse, 0x0200
enum_attr :reparse_point, 0x0400
enum_attr :compressed, 0x0800
enum_attr :rom_module, 0x2000
end
Example:
>> example = FileAttributes.new(3)
=> #<FileAttributes:0x629d90 #attrs=3>
>> example.readonly?
=> true
>> example.hidden?
=> true
>> example.system?
=> false
>> example.system = true
=> true
>> example.system?
=> true
>> example.to_i
=> 7
This plays well in database scenarios, or when dealing with C style constants/enums (as is the case when using FFI, which RAPI makes extensive use of).
Also, you don't have to worry about typos causing silent failures, as you would with using a hash-type solution.
I use the following approach:
class MyClass
MY_ENUM = [MY_VALUE_1 = 'value1', MY_VALUE_2 = 'value2']
end
I like it for the following advantages:
It groups values visually as one whole
It does some compilation-time checking (in contrast with just using symbols)
I can easily access the list of all possible values: just MY_ENUM
I can easily access distinct values: MY_VALUE_1
It can have values of any type, not just Symbol
Symbols may be better cause you don't have to write the name of outer class, if you are using it in another class (MyClass::MY_VALUE_1)
The most idiomatic way to do this is to use symbols. For example, instead of:
enum {
FOO,
BAR,
BAZ
}
myFunc(FOO);
...you can just use symbols:
# You don't actually need to declare these, of course--this is
# just to show you what symbols look like.
:foo
:bar
:baz
my_func(:foo)
This is a bit more open-ended than enums, but it fits well with the Ruby spirit.
Symbols also perform very well. Comparing two symbols for equality, for example, is much faster than comparing two strings.
If you are using Rails 4.2 or greater you can use Rails enums.
Rails now has enums by default without the need for including any gems.
This is very similar (and more with features) to Java, C++ enums.
Quoted from http://edgeapi.rubyonrails.org/classes/ActiveRecord/Enum.html :
class Conversation < ActiveRecord::Base
enum status: [ :active, :archived ]
end
# conversation.update! status: 0
conversation.active!
conversation.active? # => true
conversation.status # => "active"
# conversation.update! status: 1
conversation.archived!
conversation.archived? # => true
conversation.status # => "archived"
# conversation.update! status: 1
conversation.status = "archived"
# conversation.update! status: nil
conversation.status = nil
conversation.status.nil? # => true
conversation.status # => nil
I know it's been a long time since the guy posted this question, but I had the same question and this post didn't give me the answer. I wanted an easy way to see what the number represents, easy comparison, and most of all ActiveRecord support for lookup using the column representing the enum.
I didn't find anything, so I made an awesome implementation called yinum which allowed everything I was looking for. Made ton of specs, so I'm pretty sure it's safe.
Some example features:
COLORS = Enum.new(:COLORS, :red => 1, :green => 2, :blue => 3)
=> COLORS(:red => 1, :green => 2, :blue => 3)
COLORS.red == 1 && COLORS.red == :red
=> true
class Car < ActiveRecord::Base
attr_enum :color, :COLORS, :red => 1, :black => 2
end
car = Car.new
car.color = :red / "red" / 1 / "1"
car.color
=> Car::COLORS.red
car.color.black?
=> false
Car.red.to_sql
=> "SELECT `cars`.* FROM `cars` WHERE `cars`.`color` = 1"
Car.last.red?
=> true
This is my approach to enums in Ruby. I was going for short and sweet, not necessarily the the most C-like. Any thoughts?
module Kernel
def enum(values)
Module.new do |mod|
values.each_with_index{ |v,i| mod.const_set(v.to_s.capitalize, 2**i) }
def mod.inspect
"#{self.name} {#{self.constants.join(', ')}}"
end
end
end
end
States = enum %w(Draft Published Trashed)
=> States {Draft, Published, Trashed}
States::Draft
=> 1
States::Published
=> 2
States::Trashed
=> 4
States::Draft | States::Trashed
=> 5
Check out the ruby-enum gem, https://github.com/dblock/ruby-enum.
class Gender
include Enum
Gender.define :MALE, "male"
Gender.define :FEMALE, "female"
end
Gender.all
Gender::MALE
Perhaps the best lightweight approach would be
module MyConstants
ABC = Class.new
DEF = Class.new
GHI = Class.new
end
This way values have associated names, as in Java/C#:
MyConstants::ABC
=> MyConstants::ABC
To get all values, you can do
MyConstants.constants
=> [:ABC, :DEF, :GHI]
If you want an enum's ordinal value, you can do
MyConstants.constants.index :GHI
=> 2
If you're worried about typos with symbols, make sure your code raises an exception when you access a value with a non-existent key. You can do this by using fetch rather than []:
my_value = my_hash.fetch(:key)
or by making the hash raise an exception by default if you supply a non-existent key:
my_hash = Hash.new do |hash, key|
raise "You tried to access using #{key.inspect} when the only keys we have are #{hash.keys.inspect}"
end
If the hash already exists, you can add on exception-raising behaviour:
my_hash = Hash[[[1,2]]]
my_hash.default_proc = proc do |hash, key|
raise "You tried to access using #{key.inspect} when the only keys we have are #{hash.keys.inspect}"
end
Normally, you don't have to worry about typo safety with constants. If you misspell a constant name, it'll usually raise an exception.
Another solution is using OpenStruct. Its pretty straight forward and clean.
https://ruby-doc.org/stdlib-2.3.1/libdoc/ostruct/rdoc/OpenStruct.html
Example:
# bar.rb
require 'ostruct' # not needed when using Rails
# by patching Array you have a simple way of creating a ENUM-style
class Array
def to_enum(base=0)
OpenStruct.new(map.with_index(base).to_h)
end
end
class Bar
MY_ENUM = OpenStruct.new(ONE: 1, TWO: 2, THREE: 3)
MY_ENUM2 = %w[ONE TWO THREE].to_enum
def use_enum (value)
case value
when MY_ENUM.ONE
puts "Hello, this is ENUM 1"
when MY_ENUM.TWO
puts "Hello, this is ENUM 2"
when MY_ENUM.THREE
puts "Hello, this is ENUM 3"
else
puts "#{value} not found in ENUM"
end
end
end
# usage
foo = Bar.new
foo.use_enum 1
foo.use_enum 2
foo.use_enum 9
# put this code in a file 'bar.rb', start IRB and type: load 'bar.rb'
It all depends how you use Java or C# enums. How you use it will dictate the solution you'll choose in Ruby.
Try the native Set type, for instance:
>> enum = Set['a', 'b', 'c']
=> #<Set: {"a", "b", "c"}>
>> enum.member? "b"
=> true
>> enum.member? "d"
=> false
>> enum.add? "b"
=> nil
>> enum.add? "d"
=> #<Set: {"a", "b", "c", "d"}>
Someone went ahead and wrote a ruby gem called Renum. It claims to get the closest Java/C# like behavior. Personally I'm still learning Ruby, and I was a little shocked when I wanted to make a specific class contain a static enum, possibly a hash, that it wasn't exactly easily found via google.
Recently we released a gem that implements Enums in Ruby. In my post you will find the answers on your questions. Also I described there why our implementation is better than existing ones (actually there are many implementations of this feature in Ruby yet as gems).
Symbols is the ruby way. However, sometimes one need to talk to some C code or something or Java that expose some enum for various things.
#server_roles.rb
module EnumLike
def EnumLike.server_role
server_Symb=[ :SERVER_CLOUD, :SERVER_DESKTOP, :SERVER_WORKSTATION]
server_Enum=Hash.new
i=0
server_Symb.each{ |e| server_Enum[e]=i; i +=1}
return server_Symb,server_Enum
end
end
This can then be used like this
require 'server_roles'
sSymb, sEnum =EnumLike.server_role()
foreignvec[sEnum[:SERVER_WORKSTATION]]=8
This is can of course be made abstract and you can roll our own Enum class
I have implemented enums like that
module EnumType
def self.find_by_id id
if id.instance_of? String
id = id.to_i
end
values.each do |type|
if id == type.id
return type
end
end
nil
end
def self.values
[#ENUM_1, #ENUM_2]
end
class Enum
attr_reader :id, :label
def initialize id, label
#id = id
#label = label
end
end
#ENUM_1 = Enum.new(1, "first")
#ENUM_2 = Enum.new(2, "second")
end
then its easy to do operations
EnumType.ENUM_1.label
...
enum = EnumType.find_by_id 1
...
valueArray = EnumType.values
module Status
BAD = 13
GOOD = 24
def self.to_str(status)
for sym in self.constants
if self.const_get(sym) == status
return sym.to_s
end
end
end
end
mystatus = Status::GOOD
puts Status::to_str(mystatus)
Output:
GOOD
This seems a bit superfluous, but this is a methodology that I have used a few times, especially where I am integrating with xml or some such.
#model
class Profession
def self.pro_enum
{:BAKER => 0,
:MANAGER => 1,
:FIREMAN => 2,
:DEV => 3,
:VAL => ["BAKER", "MANAGER", "FIREMAN", "DEV"]
}
end
end
Profession.pro_enum[:DEV] #=>3
Profession.pro_enum[:VAL][1] #=>MANAGER
This gives me the rigor of a c# enum and it is tied to the model.
Most people use symbols (that's the :foo_bar syntax). They're sort of unique opaque values. Symbols don't belong to any enum-style type so they're not really a faithful representation of C's enum type but this is pretty much as good as it gets.
Sometimes all I need is to be able to fetch enum's value and identify its name similar to java world.
module Enum
def get_value(str)
const_get(str)
end
def get_name(sym)
sym.to_s.upcase
end
end
class Fruits
include Enum
APPLE = "Delicious"
MANGO = "Sweet"
end
Fruits.get_value('APPLE') #'Delicious'
Fruits.get_value('MANGO') # 'Sweet'
Fruits.get_name(:apple) # 'APPLE'
Fruits.get_name(:mango) # 'MANGO'
This to me serves the purpose of enum and keeps it very extensible too. You can add more methods to the Enum class and viola get them for free in all the defined enums. for example. get_all_names and stuff like that.
Try the inum.
https://github.com/alfa-jpn/inum
class Color < Inum::Base
define :RED
define :GREEN
define :BLUE
end
Color::RED
Color.parse('blue') # => Color::BLUE
Color.parse(2) # => Color::GREEN
see more https://github.com/alfa-jpn/inum#usage
Another approach is to use a Ruby class with a hash containing names and values as described in the following RubyFleebie blog post. This allows you to convert easily between values and constants (especially if you add a class method to lookup the name for a given value).
I think the best way to implement enumeration like types is with symbols since the pretty much behave as integer (when it comes to performace, object_id is used to make comparisons ); you don't need to worry about indexing and they look really neat in your code xD
irb(main):016:0> num=[1,2,3,4]
irb(main):017:0> alph=['a','b','c','d']
irb(main):018:0> l_enum=alph.to_enum
irb(main):019:0> s_enum=num.to_enum
irb(main):020:0> loop do
irb(main):021:1* puts "#{s_enum.next} - #{l_enum.next}"
irb(main):022:1> end
Output:
1 - a
2 - b
3 - c
4 - d
Another way to mimic an enum with consistent equality handling (shamelessly adopted from Dave Thomas). Allows open enums (much like symbols) and closed (predefined) enums.
class Enum
def self.new(values = nil)
enum = Class.new do
unless values
def self.const_missing(name)
const_set(name, new(name))
end
end
def initialize(name)
#enum_name = name
end
def to_s
"#{self.class}::##enum_name"
end
end
if values
enum.instance_eval do
values.each { |e| const_set(e, enum.new(e)) }
end
end
enum
end
end
Genre = Enum.new %w(Gothic Metal) # creates closed enum
Architecture = Enum.new # creates open enum
Genre::Gothic == Genre::Gothic # => true
Genre::Gothic != Architecture::Gothic # => true

Resources