I have a list of Pair's which represent lines with a start index and end index. The type is (Int,Int) being a tuple of two integers.
Now I wish to join together any lines which are touching each other, for example.
List( (1,5),(6,10),(12,20) ) should transform into List( (1,10), (12,20) ) because line (1,5) touches line (6,10). Only touching lines are joined together into a single line.
My first thought was to use foldleft on the list, the problem is that foldleft needs to transform two elements into one and would eventually end up with just one (Int,Int) outputted at the end.
Second thought is a recursive solution which I did program but this doesn't seem like the best or simplest idea. The idea that we are taking two elements and sometimes outputting one element or two elements seems like something which should be applied to many cases, like foldleft does.
Is there any common way or pattern or library to solve this problem?
A single line is on the 1 dimensional plane, its not a x,y point. The (Int,Int) is a start and end point on the 1 dimensional plane.
To clear up the definition of touching. None of the lines in the list overlap, this is a pre-condition, that is (1, 3) and (2, 4) cannot exist in a list because they overlap.
(1,3), (4,5) could exist in a list because they do not overlap.
(1,3), (4,5) does touch because 3 and 4 have exactly a distance of 1 between them.
For the list as given in the questions ((1, 5), (6, 10), (6, 12), (13, 15)), this is an invalid list because (6, 10), (6, 12) overlaps.
For your info, this is my working code before I wrote this question here, you can see its not great. I was just using my knowledge of building recursive function which builds up a result and returns the result.
private def joinAtoB(a: LineLike, b: LineLike): LineLike = {
newFunction(a.start, b.end)
}
private def joinTouchingLines(a: LineLike, b: LineLike): Option[LineLike] = {
if ((a.end + 1) == b.start)
Some(joinAtoB(a, b))
else
None
}
@tailrec
def joinLinesRec(list: List[LineLike], result: List[LineLike] = List[LineLike]())
:List[LineLike] = {
list match {
case Nil => result
case item :: Nil => item :: result
case first :: second :: rest => {
val joined = joinTouchingLines(first, second)
val prepend = joined match {
case None => List(first, second)
case Some(joinedItem) => List(joinedItem)
}
joinLinesRec(rest, prepend ::: result)
}
}
}
The foldLeft() method, available for all collections in Scala, allows a given 2-argument function to run against consecutive elements of that collection, where the result of that function is passed as the first argument in the next invocation.
The foldLeft function is applicable to both Scala's Mutable and Immutable collection data structures. The foldLeft method takes an associative binary operator function as parameter and will use it to collapse elements from the collection.
The fold function is applicable to both Scala's Mutable and Immutable collection data structures. The fold method takes an associative binary operator function as parameter and will use it to collapse elements from the collection. The fold method allows you to also specify an initial value.
The fold method for a List takes two arguments; the start value and a function. This function also takes two arguments; the accumulated value and the current item in the list. So here's what happens:
You can use foldRight
instead of foldLeft
with a binary function that mimicks cons (::
) except when adjacent elements touch:
def combine(x: (Int, Int), lst: List[(Int, Int)]) = (x, lst) match {
case (_, Nil) => List(x)
case ((a, b), (c, d) :: rest) => if (c == b+1)
(a, d) :: rest
else
(a, b) :: lst
}
It's simple to perform this with foldLeft, but it's even simpler if you take out the part that rules when we're overlapping or touching:
def touching(fp: (Int, Int), sp: (Int, Int)) =
if (fp._2 >= sp._1) sys.error("Overlap " + fp + " and " + sp)
else if (fp._2 == sp._1 - 1) true
else false
Then you can decide when you want to just append and when you want to extend the last pair:
list.foldLeft(List[(Int,Int)]()){
(l, p) =>
if (l.isEmpty || !touching(l.head, p)) p :: l
else (l.head._1, p._2) :: l.tail
}.reverse
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With