Last active
December 17, 2015 14:39
-
-
Save dodecaphonic/5626216 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'minitest/autorun' | |
TowEvent = Struct.new(:id, :license_plate, :category, :operator, :tow_car, | |
:entry_time, :address, :ordering) | |
class OrdersTowEvents | |
WEIGHTS = { truck: 100, car: 200, bike: 300 } | |
attr_reader :grouped_events | |
private :grouped_events | |
def initialize(tow_events) | |
@grouped_events = group_events(tow_events) | |
end | |
def order | |
grouped_events.flat_map do |group_name, events| | |
if events.size == 1 | |
order_as_start_of_sequence(events.first) | |
else | |
order_resolving_ties(events) | |
end | |
end | |
end | |
private | |
def order_resolving_ties(events) | |
sorted_events = sort_events_by_weight_and_sequence(events) | |
sorted_events.each_with_index do |event, i| | |
event.ordering = i + 1 | |
end | |
events | |
end | |
def sort_events_by_weight_and_sequence(events) | |
previous_category = nil | |
category_sequence = Hash.new { 0 } | |
events.sort_by do |event| | |
category = event.category | |
weight = WEIGHTS[category] | |
sequence_number = category_sequence[category] | |
new_sequence_number = if sequence_number > 0 | |
sequence_number + 1 | |
else | |
weight | |
end | |
category_sequence[category] = new_sequence_number | |
end | |
end | |
def order_as_start_of_sequence(event) | |
event.ordering = 1 | |
event | |
end | |
def group_events(tow_events) | |
criteria = [:operator, :tow_car, :entry_time, :address] | |
event_map = Hash.new { |h, k| h[k] = [] } | |
tow_events.map do |event| | |
key = criteria.map { |c| event.public_send(c) }.hash | |
event_map[key] << event | |
end | |
event_map | |
end | |
end | |
describe OrdersTowEvents do | |
let(:entry_time) { Time.now } | |
let(:events) { | |
[ | |
TowEvent.new(1, 'AAA-1234', :bike, 1, 'FOO', entry_time, | |
'1 Sad Lonely St.'), | |
TowEvent.new(1, 'AAA-1234', :bike, 1, 'FOO', entry_time, | |
'1 Sad Lonely St.'), | |
] | |
} | |
describe 'when events do not match up' do | |
describe 'by operator' do | |
it "orders as '1'" do | |
events[1].operator = 2 | |
ordering = OrdersTowEvents.new(events) | |
ordered_events = ordering.order | |
ordered_events.map(&:ordering).uniq.must_equal [1] | |
end | |
end | |
describe 'by time' do | |
it "orders as '1'" do | |
events[1].entry_time = Time.now | |
ordering = OrdersTowEvents.new(events) | |
ordered_events = ordering.order | |
ordered_events.map(&:ordering).uniq.must_equal [1] | |
end | |
end | |
describe 'by address' do | |
it "orders as '1'" do | |
events[1].address = '23 Crappy Code Lane' | |
ordering = OrdersTowEvents.new(events) | |
ordered_events = ordering.order | |
ordered_events.map(&:ordering).uniq.must_equal [1] | |
end | |
end | |
describe 'by tow car' do | |
it "orders as '1'" do | |
events[1].tow_car = 'BAR' | |
ordering = OrdersTowEvents.new(events) | |
ordered_events = ordering.order | |
ordered_events.map(&:ordering).uniq.must_equal [1] | |
end | |
end | |
end | |
describe 'when operators do match' do | |
describe 'completely' do | |
describe 'and towed vehicle type is the same' do | |
it 'orders events sequentially' do | |
ordering = OrdersTowEvents.new(events) | |
ordered_events = ordering.order | |
ordered_events.map(&:ordering).must_equal [1, 2] | |
end | |
end | |
describe 'and towed vehicle types differ' do | |
it 'uses weights to sort them' do | |
car_event = TowEvent.new(3, 'AAA-1234', :car, 1, 'FOO', | |
entry_time, '1 Sad Lonely St.') | |
truck_event = TowEvent.new(4, 'AAA-1234', :truck, 1, 'FOO', | |
entry_time, '1 Sad Lonely St.') | |
events << car_event | |
events << truck_event | |
ordering = OrdersTowEvents.new(events) | |
ordered_events = ordering.order | |
ordered_events.map(&:ordering).must_equal [3, 4, 2, 1] | |
end | |
end | |
end | |
end | |
describe 'using the original data' do | |
it 'works' do | |
raw_data = [ | |
['12345-00', 'AAA0000', 'Passeio', 'XPTO', 'TTT9999', '11:00', 'Rua da Subida'], | |
['12346-01', 'BBB0000', 'Moto', 'XPTO', 'TTT9999', '11:00', 'Rua da Subida'], | |
['12347-02', 'CCC0000', 'Moto', 'XPTO', 'TTT9999', '11:00', 'Rua da Subida'], | |
['12348-03', 'DDD0000', 'Passeio', 'XPTO', 'TTT9999', '11:00', 'Rua da Subida'], | |
['12349-04', 'EEE0000', 'Moto', 'XPTO', 'TTT9999', '11:00', 'Rua da Subida'], | |
] | |
events = to_events(raw_data) | |
ordering = OrdersTowEvents.new(events) | |
ordered_events = ordering.order | |
ordered_events.map(&:ordering).must_equal [1, 3, 4, 2, 5] | |
end | |
end | |
def to_events(raw_data) | |
raw_data.map do |event_data| | |
event_data[2] = case event_data[2] | |
when 'Passeio' then :car | |
when 'Moto' then :bike | |
end | |
TowEvent.new(*event_data) | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Acredito que você consiga resolver essa ordenação aumentando o espaço entre os pesos (ex.: 10-20-30) e subordenando dentro dele.