class TrickBag::Enumerables::CompoundEnumerable
For example, for blood types [:a, :b, :ab, :o] and rh [:+, :-], provides an enumerator whose 'each' method gives:
- :a, :+
- :a, :-
- :b, :+
- :b, :-
- :ab, :+
- :ab, :-
- :o, :+
- :o, :-
- :a, :-
-
Initialized with enumerables whose 'each' method returns an Enumerator when no block is provided.
Guaranteed to follow the order as shown above, that is, each value of the first enumerable will be processed in its entirety before advancing to the next value.
Can be used with an arbitrary number of enumerables.
Attributes
Public Class Methods
Creates a compound enumerable that returns an array whenever 'each' is called
# File lib/trick_bag/enumerables/compound_enumerable.rb, line 45 def self.array_enumerable(*enumerables) self.new(:yields_arrays, nil, *enumerables) end
Creates a compound enumerable that returns a hash whenever 'each' is called
# File lib/trick_bag/enumerables/compound_enumerable.rb, line 39 def self.hash_enumerable(keys, *enumerables) self.new(:yields_hashes, keys, *enumerables) end
@param mode determines whether each should yield hashes or arrays. Permissible values are [:yields_arrays, :yields_hashes].
# File lib/trick_bag/enumerables/compound_enumerable.rb, line 52 def initialize(mode, keys, *enumerables) validate_inputs = ->do raise ArgumentError.new("Mode must be either :yields_arrays or :yields_hashes") unless [:yields_arrays, :yields_hashes].include?(mode) raise ArgumentError.new("Keys not provided") if mode == :yields_hashes && (! keys.is_a?(Array)) raise ArgumentError.new("No enumerables provided") if enumerables.empty? if mode == :yields_hashes && (keys.size != enumerables.size) raise ArgumentError.new("Key array size (#{keys.size}) is different from enumerables size (#{enumerables.size}).") end end validate_inputs.() @enumerables = enumerables @enum_count = enumerables.size @mode = mode @keys = keys end
Public Instance Methods
# File lib/trick_bag/enumerables/compound_enumerable.rb, line 73 def each(&block) return to_enum unless block_given? return if enum_count == 0 initial_value = mode == :yields_arrays ? [] : {} each_multi_enumerable(0, ::TrickBag::Collections::LinkedList.new(*@enumerables).first, initial_value, &block) end
This method will be called recursively down the list of enumerables. @param node will advance to the next node for each recursive call @param values will be a list of values collected on the stack
# File lib/trick_bag/enumerables/compound_enumerable.rb, line 84 def each_multi_enumerable(depth, node, values, &block) enumerable = node.value is_deepest_enumerable = node.next.nil? as_array = ->(thing) do new_value_array = values + [thing] if is_deepest_enumerable yield *new_value_array else each_multi_enumerable(depth + 1, node.next, new_value_array, &block) end end as_hash = ->(thing) do key = keys[depth] new_values = values.clone new_values[key] = thing if is_deepest_enumerable yield new_values else each_multi_enumerable(depth + 1, node.next, new_values, &block) end new_values end # TODO: Move conditional behavior outside of loop. enumerable.each do |thing| mode == :yields_arrays ? as_array.(thing) : as_hash.(thing) end end