Home > database >  Collections in Ruby - is there a collections class like Set but with the member access of Hash?
Collections in Ruby - is there a collections class like Set but with the member access of Hash?

Time:11-05

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(" ")
  • Related