Skip to content

Instantly share code, notes, and snippets.

@dacr
Created July 31, 2013 19:58
Show Gist options
  • Save dacr/6125575 to your computer and use it in GitHub Desktop.
Save dacr/6125575 to your computer and use it in GitHub Desktop.
Generic compact collection functions : compact and compactBy
import scala.collection.generic.CanBuildFrom
import scala.annotation.tailrec
def compact[A, I[A]](list: I[A])
(implicit bf: CanBuildFrom[I[A], A, I[A]], c2i:I[A]=>Iterable[A]): I[A] = {
var builder = bf.apply()
var prev: Option[A] = None
for (item <- list) {
if (prev == None || prev.get != item) builder += item
prev = Some(item)
}
builder.result
}
compact(Array(1,2,2,3))
// res0: Array[Int] = Array(1, 2, 3)
compact(List(1,2,2,3,3,3,4))
// res1: List[Int] = List(1, 2, 3, 4)
compact(Vector(BigDecimal(1), BigDecimal(2), BigDecimal(2)))
//res2: scala.collection.immutable.Vector[scala.math.BigDecimal] = Vector(1, 2)
def compactBy[A, I](list: I, by: A => _, merge: (A,A)=>A)
(implicit bf: CanBuildFrom[I, A, I], c2i:I=>Iterable[A]): I = {
var builder = bf.apply()
@tailrec
def docompact(remain:Iterable[A], prevOpt:Option[A]=None) {
prevOpt match {
case None if remain.isEmpty=>
case None => docompact(remain.tail, Some(remain.head))
case Some(prev) if remain.isEmpty => builder += prev
case Some(prev) if by(prev)==by(remain.head) =>
docompact(remain.tail, Some(merge(prev, remain.head)))
case Some(prev) =>
builder += prev
docompact(remain.tail, Some(remain.head))
}
}
docompact(list)
builder.result
}
case class Rect(w:Int, h:Int)
def height(x:Rect) = x.h
def merger(x:Rect, y:Rect) = if (x.w<y.w) x else y
compactBy(List.empty[Rect], height, merger)
// res3: List[toto.Compact.Rect] = List()
compactBy(List(Rect(1,2),Rect(2,2),Rect(2,3), Rect(0,3)),
height, merger)
// res4: List[toto.Compact.Rect] = List(Rect(1,2), Rect(0,3))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment