Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Grouping list items by comparing them with their neighbors

Tags:

scala

What is the most elegant way of grouping a list of values into groups based on their neighbor values?

The wider context I have is having a list of lines, that need to be grouped into paragraphs. I want to be able to say that if the vertical difference between two lines is lower than threshold, they are in the same paragraph.

I ended up solving this problem differently, but I'm wondering about the correct solution here.

case class Box(y: Int)
val list = List(Box(y=1), Box(y=2), Box(y=5))

def group(list: List[Box], threshold: Int): List[List[Box]] = ???

val grouped = group(list, 2)
> List(List(Box(y=1), Box(y=2)), List(Box(y=5)))

I have looked at groupBy(), but that can only work with one element at a time. I have also tried an approach that involved pre-computing differences using sliding(), but then it becomes awkward to retrieve the elements from the original collection.

like image 712
mirosval Avatar asked Jun 14 '16 15:06

mirosval


1 Answers

It's a one liner. Generalising types left as an exercise for the reader.

Using ints and absolute difference rather than lines and spacing to avoid clutter.

val zs = List(1,2,4,8,9,10,15,16)  
def closeEnough(a:Int, b:Int) = (Math.abs(b -a) <= 2)

zs.drop(1).foldLeft(List(List(zs.head)))
                      ((acc, e)=> if (closeEnough(e, acc.head.head)) 
                                      (e::acc.head)::acc.tail
                                  else
                                       List(e)::acc)
       .map(_.reverse)
       .reverse

// List(List(1, 2, 4), List(8, 9, 10), List(15, 16))

Or a two liner for a slight efficiency gain

val ys = zs.reverse
ys.drop(1).foldLeft(List(List(ys.head)))
                ((acc, e)=> if (closeEnough(e, acc.head.head)) 
                   (e::acc.head)::acc.tail
                else
                   List(e)::acc)
// List(List(1, 2, 4), List(8, 9, 10), List(15, 16))
like image 82
The Archetypal Paul Avatar answered Nov 15 '22 03:11

The Archetypal Paul