Skip to content

Commit

Permalink
Merge c7896ee into d40efbb
Browse files Browse the repository at this point in the history
  • Loading branch information
vaskoz authored Sep 3, 2020
2 parents d40efbb + c7896ee commit 1b1e0d4
Show file tree
Hide file tree
Showing 3 changed files with 202 additions and 1 deletion.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,8 @@ problems from
* [Day 367](https://github.com/vaskoz/dailycodingproblem-go/issues/740)
* [Day 368](https://github.com/vaskoz/dailycodingproblem-go/issues/743)
* [Day 369](https://github.com/vaskoz/dailycodingproblem-go/issues/745)
* Day 370 - Never received an e-mail for this day.
* [Day 370](https://github.com/vaskoz/dailycodingproblem-go/issues/954)
- Thanks to @Birdy-C for providing me the problem.
* [Day 371](https://github.com/vaskoz/dailycodingproblem-go/issues/750)
* [Day 372](https://github.com/vaskoz/dailycodingproblem-go/issues/748)
* [Day 373](https://github.com/vaskoz/dailycodingproblem-go/issues/752)
Expand Down
74 changes: 74 additions & 0 deletions day370/problem.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package day370

import (
"log"
"sort"
)

// DeliveryType enumerates either a pickup or dropoff delivery.
type DeliveryType string

const (
Pickup = "pickup"
Dropoff = "dropoff"
)

// DeliveryAction represents the data supplied to the algorithm.
type DeliveryAction struct {
DeliveryID uint
Timestamp uint
Type DeliveryType
}

// CourierActiveTime returns the number of seconds the courier was active
// in delivering packages.
//
// Function panics if bad data is discovered e.g. mismatch of dropoff and pickups.
// Runtime is O(N log N) due to sort.
func CourierActiveTime(data []DeliveryAction) uint {
if len(data) == 0 {
return 0
}

sort.Slice(data, func(i, j int) bool {
return data[i].Timestamp < data[j].Timestamp
})

var answer, startActiveTime uint

outForDelivery := make(map[uint]DeliveryAction)
d := data[0]

if d.Type != Pickup {
log.Panicf("mismatched. can't have first chronological item as Dropoff. %v", d)
}

startActiveTime = d.Timestamp
outForDelivery[d.DeliveryID] = d

for _, d := range data[1:] {
switch d.Type {
case Pickup:
if len(outForDelivery) == 0 {
startActiveTime = d.Timestamp
} else if _, found := outForDelivery[d.DeliveryID]; found {
log.Panicf("duplicate pickup exists. %v", d)
}

outForDelivery[d.DeliveryID] = d
case Dropoff:
if _, found := outForDelivery[d.DeliveryID]; !found {
log.Panicf("mismatched. can't have Dropoff without preceding Pickup. %v", d)
} else {
delete(outForDelivery, d.DeliveryID)
if len(outForDelivery) == 0 {
answer += (d.Timestamp - startActiveTime)
}
}
default:
log.Panicf("invalid DeliveryType. %v", d)
}
}

return answer
}
126 changes: 126 additions & 0 deletions day370/problem_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package day370

import (
"testing"
)

// nolint
var testcases = []struct {
data []DeliveryAction
activeTime uint
}{
{nil, 0},
{
[]DeliveryAction{ // overlapping deliveries
{1, 10, DeliveryType("pickup")},
{1, 20, DeliveryType("dropoff")},
{2, 15, DeliveryType("pickup")},
{3, 17, DeliveryType("pickup")},
{3, 22, DeliveryType("dropoff")},
{2, 30, DeliveryType("dropoff")},
},
20,
},
{
[]DeliveryAction{ // non-overlapping deliveries
{1, 10, DeliveryType("pickup")},
{1, 20, DeliveryType("dropoff")},
{2, 40, DeliveryType("pickup")},
{3, 100, DeliveryType("pickup")},
{3, 110, DeliveryType("dropoff")},
{2, 50, DeliveryType("dropoff")},
},
30,
},
}

func TestCourierActiveTime(t *testing.T) {
t.Parallel()

for _, tc := range testcases {
if activeTime := CourierActiveTime(tc.data); activeTime != tc.activeTime {
t.Errorf("Expected %d, got %d", tc.activeTime, activeTime)
}
}
}

func TestCourierActiveTimeDropOffBeforePickup(t *testing.T) {
t.Parallel()

data := []DeliveryAction{
{1, 1573280047, DeliveryType("pickup")},
{1, 1570320725, DeliveryType("dropoff")},
{2, 1570321092, DeliveryType("pickup")},
{3, 1570321212, DeliveryType("pickup")},
{3, 1570322352, DeliveryType("dropoff")},
{2, 1570323012, DeliveryType("dropoff")},
}

defer func() {
if err := recover(); err == nil {
t.Errorf("Expected a panic due to the first event by time being a Dropoff")
}
}()

CourierActiveTime(data)
}

func TestCourierActiveTimeDuplicatePickup(t *testing.T) {
t.Parallel()

data := []DeliveryAction{
{3, 30, DeliveryType("pickup")},
{3, 40, DeliveryType("pickup")},
}

defer func() {
if err := recover(); err == nil {
t.Errorf("Expected a panic due to a duplicate pickup for the same delivery ID")
}
}()

CourierActiveTime(data)
}

func TestCourierActiveTimeDropoffBeforePickup(t *testing.T) {
t.Parallel()

data := []DeliveryAction{
{1, 10, DeliveryType("pickup")},
{1, 20, DeliveryType("dropoff")},
{3, 30, DeliveryType("dropoff")},
}

defer func() {
if err := recover(); err == nil {
t.Errorf("Expected a panic due to a dropoff only. no pickup.")
}
}()

CourierActiveTime(data)
}

func TestCourierActiveTimeInvalidDeliveryType(t *testing.T) {
t.Parallel()

data := []DeliveryAction{
{3, 30, DeliveryType("pickup")},
{4, 40, DeliveryType("gonnapanic")},
}

defer func() {
if err := recover(); err == nil {
t.Errorf("Expected a panic due to a duplicate pickup for the same delivery ID")
}
}()

CourierActiveTime(data)
}

func BenchmarkCourierActiveTime(b *testing.B) {
for i := 0; i < b.N; i++ {
for _, tc := range testcases {
CourierActiveTime(tc.data)
}
}
}

0 comments on commit 1b1e0d4

Please sign in to comment.