Skip to content

Instantly share code, notes, and snippets.

@dodecaphonic
Last active December 17, 2015 14:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dodecaphonic/5626216 to your computer and use it in GitHub Desktop.
Save dodecaphonic/5626216 to your computer and use it in GitHub Desktop.
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
@dodecaphonic
Copy link
Author

Acredito que você consiga resolver essa ordenação aumentando o espaço entre os pesos (ex.: 10-20-30) e subordenando dentro dele.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment