Range consolidation: Difference between revisions
Content added Content deleted
m (Better word.) |
(Kotlin implementation) |
||
Line 668: | Line 668: | ||
Array{Float64,1}[[4.0, 3.0], [2.0, 1.0], [-1.0, -2.0], [3.9, 10.0]] => Array{Float64,1}[[-2.0, -1.0], [1.0, 2.0], [3.0, 10.0]] |
Array{Float64,1}[[4.0, 3.0], [2.0, 1.0], [-1.0, -2.0], [3.9, 10.0]] => Array{Float64,1}[[-2.0, -1.0], [1.0, 2.0], [3.0, 10.0]] |
||
Array{Float64,1}[[1.0, 3.0], [-6.0, -1.0], [-4.0, -5.0], [8.0, 2.0], [-6.0, -6.0]] => Array{Float64,1}[[-6.0, -1.0], [1.0, 8.0]] |
Array{Float64,1}[[1.0, 3.0], [-6.0, -1.0], [-4.0, -5.0], [8.0, 2.0], [-6.0, -6.0]] => Array{Float64,1}[[-6.0, -1.0], [1.0, 8.0]] |
||
</pre> |
|||
=={{header|Kotlin}}== |
|||
<lang Kotlin>fun <T> consolidate(ranges: Iterable<ClosedRange<T>>): List<ClosedRange<T>> where T : Comparable<T> |
|||
{ |
|||
return ranges |
|||
.sortedWith(compareBy({ it.start }, { it.endInclusive })) |
|||
.asReversed() |
|||
.fold(mutableListOf<ClosedRange<T>>()) { |
|||
consolidatedRanges, range -> |
|||
if (consolidatedRanges.isEmpty()) |
|||
{ |
|||
consolidatedRanges.add(range) |
|||
} |
|||
// Keep in mind the reverse-sorting applied above: |
|||
// If the end of the current-range is higher, than it must start at a lower value, |
|||
else if (range.endInclusive >= consolidatedRanges[0].endInclusive) |
|||
{ |
|||
consolidatedRanges[0] = range |
|||
} |
|||
else if (range.endInclusive >= consolidatedRanges[0].start) |
|||
{ |
|||
consolidatedRanges[0] = range.start .. consolidatedRanges[0].endInclusive |
|||
} |
|||
else |
|||
{ |
|||
consolidatedRanges.add(0, range) |
|||
} |
|||
return@fold consolidatedRanges |
|||
} |
|||
.toList() |
|||
} |
|||
// What a bummer! Kotlin's range syntax (a..b) doesn't meet the task requirements when b < b, |
|||
// and on the other hand, the syntax for constructing lists, arrays and pairs isn't close enough |
|||
// to the range notation. Instead then, here's a *very* naive parser. Don't take it seriously. |
|||
val rangeRegex = Regex("""\[(.+),(.+)\]""") |
|||
fun parseDoubleRange(rangeStr: String): ClosedFloatingPointRange<Double> { |
|||
val parts = rangeRegex |
|||
.matchEntire(rangeStr) |
|||
?.groupValues |
|||
?.drop(1) |
|||
?.map { it.toDouble() } |
|||
?.sorted() |
|||
if (parts == null) throw IllegalArgumentException("Unable to parse range $rangeStr") |
|||
return parts[0] .. parts[1] |
|||
} |
|||
fun serializeRange(range: ClosedRange<*>) = "[${range.start}, ${range.endInclusive}]" |
|||
// See above. In practice you'd probably use consolidate directly |
|||
fun consolidateDoubleRanges(rangeStrings: Iterable<String>): List<String> |
|||
{ |
|||
return consolidate(rangeStrings.asSequence().map(::parseDoubleRange).toList()).map(::serializeRange) |
|||
} |
|||
fun main() { |
|||
val inputRanges = listOf( |
|||
listOf("[1.1, 2.2]"), |
|||
listOf("[6.1, 7.2]", "[7.2, 8.3]"), |
|||
listOf("[4, 3]", "[2, 1]"), |
|||
listOf("[4, 3]", "[2, 1]", "[-1, -2]", "[3.9, 10]"), |
|||
listOf("[1, 3]", "[-6, -1]", "[-4, -5]", "[8, 2]", "[-6, -6]") |
|||
) |
|||
inputRanges.associateBy(Any::toString, ::consolidateDoubleRanges).forEach({ println("${it.key} => ${it.value}") }) |
|||
}</lang> |
|||
{{Out}} |
|||
<pre> |
|||
[[1.1, 2.2]] => [[1.1, 2.2]] |
|||
[[6.1, 7.2], [7.2, 8.3]] => [[6.1, 8.3]] |
|||
[[4, 3], [2, 1]] => [[1.0, 2.0], [3.0, 4.0]] |
|||
[[4, 3], [2, 1], [-1, -2], [3.9, 10]] => [[-2.0, -1.0], [1.0, 2.0], [3.0, 10.0]] |
|||
[[1, 3], [-6, -1], [-4, -5], [8, 2], [-6, -6]] => [[-6.0, -1.0], [1.0, 8.0]] |
|||
</pre> |
</pre> |
||