Class: TypeBalancer::Balancer

Inherits:
Object
  • Object
show all
Defined in:
lib/type_balancer/balancer.rb

Overview

Handles balancing of items across batches based on type ratios

Instance Method Summary collapse

Constructor Details

#initialize(types = nil, type_field: :type, type_order: nil) ⇒ Balancer

Initialize a new Balancer instance

Parameters:

  • types (Array<String>, nil) (defaults to: nil)

    Optional types

  • type_field (Symbol) (defaults to: :type)

    Field to use for type extraction (default: :type)

  • type_order (Array<String>, nil) (defaults to: nil)

    Optional order of types



16
17
18
19
20
21
# File 'lib/type_balancer/balancer.rb', line 16

def initialize(types = nil, type_field: :type, type_order: nil)
  @types = Array(types) if types
  @type_field = type_field
  @type_order = type_order
  validate_types! if @types
end

Instance Method Details

#call(collection) ⇒ Array

Main entry point for balancing items

Parameters:

  • collection (Array)

    Items to balance

Returns:

  • (Array)

    Balanced items

Raises:



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/type_balancer/balancer.rb', line 27

def call(collection)
  validate_collection!(collection)
  extractor = TypeExtractorRegistry.get(@type_field)

  begin
    items_by_type = extractor.group_by_type(collection)
  rescue TypeBalancer::Error => e
    raise TypeBalancer::Error, "Cannot access type field '#{@type_field}': #{e.message}"
  end

  # Remove nil types and validate
  items_by_type.delete(nil)
  raise TypeBalancer::Error, "Cannot access type field '#{@type_field}'" if items_by_type.empty?

  validate_types_in_collection!(items_by_type)

  target_counts = calculate_target_counts(items_by_type)
  available_positions = (0...collection.size).to_a

  result = Array.new(collection.size)
  sorted_types = sort_types(items_by_type.keys)

  sorted_types.each do |type|
    items = items_by_type[type]
    target_count = target_counts[type]
    ratio = target_count.to_f / collection.size
    positions = PositionCalculator.calculate_positions(
      total_count: collection.size,
      ratio: ratio,
      available_items: available_positions
    )

    positions.each_with_index do |pos, idx|
      result[pos] = items[idx]
    end

    # Remove used positions from available positions
    available_positions -= positions
  end

  result.compact
end