Last active
December 30, 2016 16:00
-
-
Save petrkotek/317dbcf6a73387682bc8d899b09c36df to your computer and use it in GitHub Desktop.
Combining (Aggregating/Adding/Subtracting) ranges in Golang
This file contains hidden or 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
package ranges | |
import ( | |
"math" | |
"sort" | |
) | |
type Range struct { | |
From int64 | |
To int64 | |
Value int64 | |
} | |
type Aggregator interface { | |
Aggregate([]*Range) ([]*Range, error) | |
} | |
type StandardAggregator struct{} | |
func (a *StandardAggregator) Aggregate(ranges []*Range) ([]*Range, error) { | |
breakingPoints := make(map[int64]int64, 0) | |
outputRanges := make([]*Range, 0) | |
// 1. determine breaking points and store how much the value goes up or down at that point | |
// store the changes in a `steps` map | |
for _, inputRange := range ranges { | |
newValFrom := inputRange.Value | |
valFrom, ok := breakingPoints[inputRange.From] | |
if ok { | |
newValFrom += valFrom | |
} | |
breakingPoints[inputRange.From] = newValFrom | |
newValTo := -inputRange.Value | |
valTo, ok := breakingPoints[inputRange.To] | |
if ok { | |
newValTo += valTo | |
} | |
breakingPoints[inputRange.To] = newValTo | |
} | |
// 2. sort the breaking points in an increasing order | |
var stepKeys Int64Slice | |
for k := range breakingPoints { | |
stepKeys = append(stepKeys, k) | |
} | |
stepKeys.Sort() | |
// 3. iterate over the breaking points and generate output ranges | |
previousKey := int64(math.MinInt64) | |
previousValue := int64(0) | |
for _, currentKey := range stepKeys { | |
diff := breakingPoints[currentKey] | |
if diff == 0 { | |
// combine neighbor ranges with the same value | |
continue | |
} | |
if previousValue != 0 { | |
outputRanges = append(outputRanges, &Range{ | |
From: previousKey, | |
To: currentKey, | |
Value: previousValue, | |
}) | |
} | |
previousValue += diff | |
previousKey = currentKey | |
} | |
return outputRanges, nil | |
} | |
type Int64Slice []int64 | |
func (p Int64Slice) Len() int { | |
return len(p) | |
} | |
func (p Int64Slice) Less(i, j int) bool { | |
return p[i] < p[j] | |
} | |
func (p Int64Slice) Swap(i, j int) { | |
p[i], p[j] = p[j], p[i] | |
} | |
func (p Int64Slice) Sort() { | |
sort.Sort(p) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
test coverage: https://gist.github.com/petrkotek/7c9adb2e1421abbf8bf437ebe67caa32