How to functionally merge overlapping number-ranges from a List - scala

I have a number of range-objects which I need to merge so that all overlapping ranges disappear:
case class Range(from:Int, to:Int)
val rangelist = List(Range(3, 40), Range(1, 45), Range(2, 50), etc)
Here is the ranges:
3 40
1 45
2 50
70 75
75 90
80 85
100 200
Once finished we would get:
1 50
70 90
100 200
Imperative Algorithm:
Pop() the first range-obj and iterate through the rest of the list comparing it with each of the other ranges.
if there is an overlapping item,
merge them together ( This yields a new Range instance ) and delete the 2 merge-candidates from the source-list.
At the end of the list add the Range object (which could have changed numerous times through merging) to the final-result-list.
Repeat this with the next of the remaining items.
Once the source-list is empty we're done.
To do this imperatively one must create a lot of temporary variables, indexed loops etc.
So I'm wondering if there is a more functional approach?
At first sight the source-collection must be able to act like a Stack in providing pop() PLUS
giving the ability to delete items by index while iterating over it, but then that would not be that functional anymore.

Try tail-recursion. (Annotation is needed only to warn you if tail-recursion optimization doesn't happen; the compiler will do it if it can whether you annotate or not.)
import annotation.{tailrec => tco}
#tco final def collapse(rs: List[Range], sep: List[Range] = Nil): List[Range] = rs match {
case x :: y :: rest =>
if (y.from > x.to) collapse(y :: rest, x :: sep)
else collapse( Range(x.from, x.to max y.to) :: rest, sep)
case _ =>
(rs ::: sep).reverse
}
def merge(rs: List[Range]): List[Range] = collapse(rs.sortBy(_.from))

I love these sorts of puzzles:
case class Range(from:Int, to:Int) {
assert(from <= to)
/** Returns true if given Range is completely contained in this range */
def contains(rhs: Range) = from <= rhs.from && rhs.to <= to
/** Returns true if given value is contained in this range */
def contains(v: Int) = from <= v && v <= to
}
def collapse(rangelist: List[Range]) =
// sorting the list puts overlapping ranges adjacent to one another in the list
// foldLeft runs a function on successive elements. it's a great way to process
// a list when the results are not a 1:1 mapping.
rangelist.sortBy(_.from).foldLeft(List.empty[Range]) { (acc, r) =>
acc match {
case head :: tail if head.contains(r) =>
// r completely contained; drop it
head :: tail
case head :: tail if head.contains(r.from) =>
// partial overlap; expand head to include both head and r
Range(head.from, r.to) :: tail
case _ =>
// no overlap; prepend r to list
r :: acc
}
}

Here's my solution:
def merge(ranges:List[Range]) = ranges
.sortWith{(a, b) => a.from < b.from || (a.from == b.from && a.to < b.to)}
.foldLeft(List[Range]()){(buildList, range) => buildList match {
case Nil => List(range)
case head :: tail => if (head.to >= range.from) {
Range(head.from, head.to.max(range.to)) :: tail
} else {
range :: buildList
}
}}
.reverse
merge(List(Range(1, 3), Range(4, 5), Range(10, 11), Range(1, 6), Range(2, 8)))
//List[Range] = List(Range(1,8), Range(10,11))

I ran into this need for Advent of Code 2022, Day 15, where I needed to merge a list of inclusive ranges. I had to slightly modify the solution for inclusiveness:
import annotation.{tailrec => tco}
#tco final def collapse(rs: List[Range], sep: List[Range] = Nil): List[Range] = rs match {
case x :: y :: rest =>
if (y.start - 1 > x.end) collapse(y :: rest, x :: sep)
else collapse(Range.inclusive(x.start, x.end max y.end) :: rest, sep)
case _ =>
(rs ::: sep).reverse
}

Related

How to send the output of the FOR loop as the input to itself in Scala?

Add elements to the array buffer such that the element to be added is the product of the previous two elements of the input array buffer. The elements in the output array buffer should not exceed 10000.
Input ArrayBuffer:- ArrayBuffer(2, 3)
Expected Output ArrayBuffer:- ArrayBuffer(2, 3, 6, 18, 108, 1944)
Actual Output ArrayBuffer:- ArrayBuffer(2, 3, 6, 18)
def main(args: ArrayBuffer[Int]): Unit = {
for(k <- 1 to args.length if(args(k) < 10000)){
args += args(k)*args(k-1)
}
println(args)
}
Here I took args from main function as input to the for block. Then it calculates and appends the result to args only upto the previous length of the input say (2, 3) that is 2. But the length should increase as the elements keep appending to the arraybuffer. Say after the 1st iteration args will be (2, 3, 6, 18). Now this should be fed as input to the FOR block and it should continue till last value reaches closest min value to 10000.
If I find solution for this, I will update here. Until then, please help me solving this 🤞
Following is an adder version,
#tailrec
def adder(args: List[BigDecimal], size: Int = args.length): List[BigDecimal] = {
args.reverse match {
case first :: second :: rest if size < 10000 => adder(((first + second) :: first :: second :: rest).reverse, size + 1)
case _ => args
}
}
But this won't work for multiplication as even a simple input of List(1,2) would crash with ArithmeticException: Overflow at size 48.
EDIT
I had misunderstood the OP as the total number of elements to be less than 10000.
updated version:
#tailrec
def multiplier(args: List[Int]): List[Int] = {
args.reverse match {
case first :: second :: rest if first * second < 10000 => multiplier(((first * second) :: first :: second :: rest).reverse)
case _ => args
}
}
or
def multiplier(args: List[Int]): List[Int] = {
#tailrec
def generate(args: List[Int]): List[Int] = {
args match {
case first :: second :: rest if first * second < 10000 => generate((first * second) :: first :: second :: rest)
case _ => args
}
}
generate(args.reverse).reverse
}
An implementation in FP style would be
2 :: 3 :: List.unfold((2, 3)) { case (x, y) =>
val p = x * y
Option.when(p < 10000)((p, (y, p)))
}
https://github.com/scala/scala/blob/v2.13.10/src/library/scala/collection/Factory.scala#L114-L124
Thanks for your responses. As I like to solve any question in a simple and easy way, I found this one too in same manner. I've replaced for loop with a while loop and made tiny changes like below
while(args(i)*args(i-1) < 10000){
args += args(i)*args(i-1)
i += 1
}
Now I got the expected Output as I mentioned in my question above
See you all when my next doubt pops up...👋

add two numbers represented by linked list in scala

I am new to scala and want to write a code that add two numbers represented by linked list in scala as per the below given example
Input:
First List: 5->6->3 // represents number 365
Second List: 8->4->2 // represents number 248
Output
Resultant list: 3->1->6 // represents number 613
I have implemented a code of mutable singly linked list in scala for adding,updating and inserting elements to linked list. Find my code below
class SinglyLinkedList[A] extends ListADT[A] {
private class Node(var data: A,var next: Node)
private var head: Node = null
def apply(index: Int): A = {
require(index >= 0)
var rover = head
for (i <- 0 until index) rover = rover.next
rover.data
}
def update(index: Int,data: A): Unit = {
require(index >= 0)
var rover = head
for (i <- 0 until index) rover = rover.next
rover.data = data
}
def insert(index: Int,data: A): Unit = {
require(index >= 0)
if(index == 0) {
head = new Node(data, head)
}
else{
var rover = head
for (i <- 0 until index-1)
rover = rover.next
rover.next = new Node(data, rover.next)
}
}
def remove(index: Int): A = {
require(index >= 0)
if(index == 0){
val ret = head.data
head = head.next
ret
} else {
var rover = head
for (i <- 0 until index-1) rover = rover.next
val ret = rover.next.data
rover.next = rover.next.next
ret
}
}
}
Can anyone let me know how I am going to perform the addition of two numbers represented by linked list.
How does addition works? I mean the addition on paper: one number under the other?
Let's try for 465 + 248
465
+ 248
---
We start with the least significant digits: 5 + 8. But 5 + 8 = 13, so the result won't fit into a single digit. Which is why we do just like a teacher in preschool taught us: we leave the unit digit and carry the tens digit to the next column
1
465
+ 248
---
3
Now tens. 6 + 4 + (carried) 1 = 11. Again, we leave 1 and carry 1 to the next column:
11
465
+ 248
---
13
And the last column. 4 + 2 + 1 = 7.
11
465
+ 248
---
713
Thus result is 713. If one these 2 numbers have more column or you would carry in the last addition, you could just rewrite remaining numbers.
With immutable liked list it would work the same way (I'll explain in a moment why I used immutable):
take both lists
take heads of both lists (if one of them is empty, you can just return the other as a result of addition)
add heads, and split the result into carry and current digit (carry would be 0 or 0, digit 0 to 9)
if there is carry > 0 add list carry :: Nil to one of tails recursively
prepend digit to recursively added tails
You should end up with something like that:
val add: (List[Int], List[Int]) => List[Int] = {
case (a :: as, b :: bs) =>
val digit = (a + b) % 10
val carry = (a + b) / 10
if (carry > 0) digit :: add(add(as, carry :: Nil), bs)
else digit :: add(as, bs)
case (as, Nil) => as
case (Nil, bs) => bs
}
add(5 :: 6 :: 4 :: Nil, 8 :: 4 :: 2 :: Nil) // 3 :: 1 :: 7 :: Nil
Now, if you would use mutable list it would get trickier. If you want to use mutable list you want to update one of them, right? Which one - first? Second? Both? Your algorithm might calculate the right result but butcher the input.
Let's say you always add the second list to the fist one, and you want to leave the second intact. If the second list is longer, and you would have to add some new places for digits, you have to copy all remaining segments (otherwise you could e.g. update one number in second list and change the first one). You would also have to handle the corner case with carry.
Quite counter-intuitive behavior - numbers are not mutable, and you want to represent numbers.
Try this:
def add(a: List[Int], b: List[Int], o: Int): List[Int] = (a,b,o) match {
case (x::xs, y::ys, d) =>
val v = d + x + y
(v%10)::add(xs, ys, v/10)
case (Nil, Nil, 0) => Nil
case (Nil, Nil, d) => d::Nil
case (xs, Nil, d) => add(xs, 0::Nil, d)
case (Nil, ys, d) => add(0::Nil, ys, d)
}

How to assign a name to intermediate pattern of a List?

This code don't compile, what am I doing wrong? is it possible to do it?
How can I pattern match a list with at least 2 elements, and have the pattern have a variable for the tail (meaning y :: _)
I know it's possible desugaring the :: or with a simple if. But without desugaring and without if... it's possible?
val list:List[Int] = ...
list match {
case x :: tail#(y:: _) =>
}
Try if this code works for you:
list match {
case x :: (tail#(y :: _)) =>
}
You use another variable to hold the second element:
list match {
case x :: y :: _ =>
}
This will only match a list with at least two elements, will bind x to the first element, y to the second element and ignore the rest.
If you need to just ensure the remainder of the list is at least 1 long, then
list match {
case x :: y if y.size > 0 =>
}
will do the job.

Scala Pattern Matching Enigma

Here's my attempt of the 3rd problem (P03) of the 99 Problems in Scala (http://aperiodic.net/phil/scala/s-99/):
import scala.annotation._
// Find the nth element of a list.
// nth(2, List(1, 1, 2, 3, 5, 8)) = 2
object P03 {
#tailrec def nth[A](n: Int, ls: List[A]): A = (n, ls) match {
case (0, h :: t :: Nil) => h
case (n, _ :: t) => nth(n - 1, t)
case _ => println(n); throw new IllegalArgumentException
}
The enigma is that this code prints -4 and throws an IllegalArgumentException
The solution of course is to change the first pattern to:
case (0, h :: _) => h
This now prints the correct answer 2
Question is Why? What is the subtle difference between:
case (0, h :: t :: Nil) => h
&
case (0, h :: _) => h
Thanks!
The difference is that h :: t :: Nil matches only a list with two elements (h and t, Nil is the marker for the end of a list (I'm not 100% sure it's the exact nomenclature)) while h :: _ matches every non empty list, ie a list that has at least one element, if you check the :: class you'll see:
final case class ::[B](private var hd: B, private[scala] var tl: List[B]) extends List[B]
Which has a head and a tail where the first is the first element of the list and the second is the rest, matching on h :: t :: Nil means getting the first element of the list, than the first of the tail and then there should be a Nil, matching on h :: _ means getting the head and then you don't care of what's left as long as there's a head.

Recursively accumulate elements of collection

I have a collection of elements with an integer range such as
case class Element(id: Int, from: Int, to: Int)
val elementColl: Traversable[Element]
and I want to accumulate them into
case class ElementAcc(ids: List[Int], from: Int, to: Int)
according to the following algorithm:
Take one Element from my elementColl and use it to create a new ElementsAcc which has the same from/to as the Element taken.
Iterate over remaining elements in elementColl to look for an Element that has an overlapping integer range with our ElementAcc.
If one is found, add it to ElementAcc and expand the integer range of ElementAcc to include the range of the new Element
If none is found, repeat the process above on the remaining elements of elementColl that have not yet been assigned to an ElementAcc
This should result in collection of ElementAcc's. While just recursively adding elements to an accumulator seems easy enough, I don't know how to handle the shrinking size of elementColl so that I don't add the same Element to multiple ElementAcc's
Edit: I think I was unclear regarding the extension of the range. So let my clarify this on an example:
My accumulator currently has a range from 1 to 5. An Element with a range from 6 to 8 does not overlap with the accumulator range and thus will not be included. An Element with a range of 4 to 7 does overlap, will be included and the resulting accumulator has a range from 1 to 7.
I'll go like this:
1) Write a function that takes an ElementAcc and an Element and returns an ElementAcc.
The function would look like:
def extend(acc: ElementAcc, e: Element): ElementAcc = {
if(acc.from <= e.from && e.from <= acc.to)
ElementAcc(e.id :: acc.ids, acc.from, math.max(acc.to, e.to))
else if (acc.from <= e.to && e.to <= acc.to)
ElementAcc(e.id :: acc.ids, math.min(acc.from, e.from), acc.to)
else acc
}
foldLeft is often the good solution when accumulating objects.
It needs an initial value for the accumulator and an function that takes an accumulator and an element and returns an accumulator. Then it accumulates all elements of the traversable.
EDIT:
2) To accumulate on different lists you would have to create another function to combine a List[ElementAcc] and an Element :
def overlap(acc: ElementAcc, e: Element): Boolean = {
(acc.from <= e.from && e.from <= acc.to) || (acc.from <= e.to && e.to <= acc.to)
}
def dispatch(accList: List[ElementAcc], e: Element): List[ElementAcc] = accList match {
case Nil => List(ElementAcc(List(e.id), e.from, e.to))
case acc :: tail =>
if (overlap(acc, e)) extend(acc, e) :: tail
else acc :: dispatch(tail, e)
}
3) And it's used with a foldLeft:
val a = Element(0, 0, 5)
val b = Element(1, 3, 8)
val c = Element(2, 20, 30)
val sorted = List(a, b, c).foldLeft(List[ElementAcc]())(dispatch)
sorted: List[ElementAcc] = List(ElementAcc(List(1, 0),0,8), ElementAcc(List(2),20,30))