Class: TrickBag::Enumerables::CompoundEnumerable

Inherits:
Object
  • Object
show all
Extended by:
Meta::Classes
Includes:
Enumerable
Defined in:
lib/trick_bag/enumerables/compound_enumerable.rb

Overview

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, :-

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.

Constant Summary

Constants included from Meta::Classes

Meta::Classes::VALID_ACCESS_MODES

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Meta::Classes

attr_access, class?, private_attr_accessor, private_attr_reader, private_attr_writer, protected_attr_accessor, protected_attr_reader, protected_attr_writer, undef_class

Constructor Details

#initialize(mode, keys, *enumerables) ⇒ CompoundEnumerable

Permissible values are [:yields_arrays, :yields_hashes].

Parameters:

  • mode

    determines whether each should yield hashes or arrays.



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# 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

Instance Attribute Details

#enum_countObject (readonly)

Returns the value of attribute enum_count.



34
35
36
# File 'lib/trick_bag/enumerables/compound_enumerable.rb', line 34

def enum_count
  @enum_count
end

#keysObject (readonly)

Returns the value of attribute keys.



34
35
36
# File 'lib/trick_bag/enumerables/compound_enumerable.rb', line 34

def keys
  @keys
end

#modeObject (readonly)

Returns the value of attribute mode.



36
37
38
# File 'lib/trick_bag/enumerables/compound_enumerable.rb', line 36

def mode
  @mode
end

Class Method Details

.array_enumerable(*enumerables) ⇒ Object

Creates a compound enumerable that returns an array whenever ‘each’ is called



45
46
47
# File 'lib/trick_bag/enumerables/compound_enumerable.rb', line 45

def self.array_enumerable(*enumerables)
  self.new(:yields_arrays, nil, *enumerables)
end

.hash_enumerable(keys, *enumerables) ⇒ Object

Creates a compound enumerable that returns a hash whenever ‘each’ is called



39
40
41
# File 'lib/trick_bag/enumerables/compound_enumerable.rb', line 39

def self.hash_enumerable(keys, *enumerables)
  self.new(:yields_hashes, keys, *enumerables)
end

Instance Method Details

#each(&block) ⇒ Object



73
74
75
76
77
78
# 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

#each_multi_enumerable(depth, node, values, &block) ⇒ Object

This method will be called recursively down the list of enumerables.

Parameters:

  • node

    will advance to the next node for each recursive call

  • values

    will be a list of values collected on the stack



84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# 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