Sha256: 808da8e52aca1ee61bb8ea17823988f32e83f065ddb4de4b805991838339e965

Contents?: true

Size: 1.45 KB

Versions: 1

Compression:

Stored size: 1.45 KB

Contents

require 'transformable_list/version'
require 'set'

class TransformableList
  def initialize(items, matcher: -> a, b { a == b })
    @items = items
    @matcher = matcher
  end

  def transform(new_items)
    build_remaining_items_index!

    moves = []
    creates = []
    deletes = []

    indexes_to_keep = Set.new

    new_items.map.with_index do |new_item, new_index|
      if existing_index = find_index_and_eliminate_match(new_item)
        if new_index != existing_index
          moves << [:move, @items[existing_index], new_index]
        end

        indexes_to_keep << existing_index
      else
        creates << [:create, new_item, new_index]
      end
    end

    (0...@items.size).each do |index|
      unless indexes_to_keep.include?(index)
        deletes << [:delete, @items[index], index]
      end
    end

    deletes.sort_by(&:last) + creates.sort_by(&:last) + moves.sort_by(&:last)
  end

  private

  def build_remaining_items_index!
    @remaining_items = @items.map.with_index{|item, i| [item, i]}
  end

  def find_index_and_eliminate_match(new_item)
    remaining_index =
      @remaining_items.index{|item, _| item.object_id == new_item.object_id} ||
        @remaining_items.index{|item, _| match?(item, new_item)}

    _, original_index = if remaining_index
      result = @remaining_items[remaining_index]
      @remaining_items.delete_at(remaining_index)
      result
    end

    original_index
  end

  def match?(a, b)
    @matcher.(a, b)
  end
end

Version data entries

1 entries across 1 versions & 1 rubygems

Version Path
transformable_list-0.1.0 lib/transformable_list.rb