After reading the Collections chapter in Peter Jones' book, Effective Ruby, the Set class may seem to provide an interesting alternative to Hash. Concerning the Set class, Jones relates:
It’s a lot like its mathematical cousin, a collection of unordered unique elements with operations such as union, intersection, and subset/superset testing.
My only concern is in whether the Set class may not provide direct access to actual member objects.
Presently, I'm trying to use Set
(or something like Set
) to implement a naive OptionSet
API, such that an option in the OptionSet
may be of a type Option
or ValueOption
, with the latter being a subclass of Option
. This API may need access to the objects stored in what respective collection for each OptionSet
- whether stored in a separate Hash, Array, some other container object, or stored in the OptionSet
via some inherited implementation e.g with Set
as a superclass.
I'd like to use Set
within OptionSet
, or to simply implement OptionSet
as a subclass of Set
. However, if Set
may not provide direct access to the member objects, once stored - short of iterating across every member object in the Set
, as until finding any ostensible match (e.g. once finding any Option
or ValueOption
in the collection, such that the "match" would have an equivalent Option.name
, typically a symbol) - maybe there's a more effective alternative?
I'd provide sample code, but the implementation of OptionSet
is not presently usable. In pseudocode, I'm trying to implement OptionSet#getopt(name)
as to return the value of any named Option
object stored in the OptionSet
, or false if no such option is stored.
The #getopt
method would call self.getoptObj(name)
, a protected method such that would need to access the named option's actual Option
object in the collection. Except for that part of the implementation, otherwise Set might be directly suitable.
In any analogy to Scheme-like languages, the Ruby standard library might not provide an AssociativeList class, per se? Simply, I wonder if there's any class like Set
-- i.e with set-theoretic methods as in Set
-- but with the member access of Hash
?
Update
I've tried to implement a MappedSet
class, at least in pseudocode, such as to use an instance Hash
value to store a mapping between general "Keys" and member objects. I believe that would be redundant to the international storage of Set
, however. Maybe I should simply extend Hash
.
CodePudding user response:
If you want to create a hash like class you can use DelegateClass:
class FalsyHash < DelegateClass(Hash)
def initialize(hash)
super.tap { |result| result.default = false }
end
end
irb(main):001:0> f = FalsyHash.new(a: :b)
=> {:a=>:b}
irb(main):002:0> f[:b]
=> false
irb(main):003:0> f.is_a?(Hash)
=> false
This is basically just a class that takes an instance of the provided class and wraps it so that method calls are forwarded. Since its not actually an instance of Hash we avoid the pitfalls that happen when core methods check if we are dealing with a hash which would occur if we used a subclass of Hash.
The same thing can be acheived with Delegator, SimpleDelegator and Forwardable - DelegateClass is just a straight forward way of wrapping classes.
You can also augment specific instances of Hash by extending them with a module:
module Reversable
def reverse
transform_values { |v| v.respond_to?(:reverse) ? v.reverse : v }
end
end
# Guess what this returns for a cookie
{ foo: 'olleH', bar: nil, baz: 'dlrow' }.extend(Reversable)
.reverse
.values
.compact
.sort
.join(" ")