2

I have often the need to check if many values are equal and in case extract the common value. That is, I need a function that will work like follows:

extract(List()) // None
extract(List(1,2,3)) // None
extract(List(2,2,2)) // Some(2)

Assuming one has a pimp that will add tailOption to seqs (it is trivial to write one or there is one in scalaz), one implementation looks like

def extract[A](l: Seq[A]): Option[A] = {

  def combine(s: A)(r: Seq[A]): Option[A] =
    r.foldLeft(Some(s): Option[A]) { (acc, n) => acc flatMap { v =>
      if (v == n) Some(v) else None
    } }

  for {
    h <- l.headOption
    t <- l.tailOption
    res <- combine(h)(t)
  } yield res
}

Is there something like that - possibly more general - already in Scalaz, or some simpler way to write it?

Andrea
  • 20,253
  • 23
  • 114
  • 183

4 Answers4

3

This seems like a really complicated way to write

def extract[A](l:Seq[A]):Option[A] = l.headOption.flatMap(h =>
  if (l.tail.forall(h==)) Some(h) else None)

You don't need tailOption, since the anonymous function that gets passed as an argument to flatMap is only executed if l is not empty.

Kim Stebel
  • 41,826
  • 12
  • 125
  • 142
2

If you only want to delete duplicates toSet is enough:

def equalValue[A](xs: Seq[A]): Option[A] = {
  val set = xs.toSet
  if (set.size == 1) Some(set.head) else None
}

scala> equalValue(List())
res8: Option[Nothing] = None

scala> equalValue(List(1,2,3))
res9: Option[Int] = None

scala> equalValue(List(2,2,2))
res10: Option[Int] = Some(2)
kiritsuku
  • 52,967
  • 18
  • 114
  • 136
2

This is a fluent solution

yourSeq.groupBy(x => x) match {case m if m.size==1 => m.head._1; case _ => None}
-1

You could use a map to count the number of occurrences of each element in the list and then return only those that occur more than once:

def extract[T](ts: Iterable[T]): Iterable[T] = {
  var counter: Map[T, Int] = Map()

  ts.foreach{t =>
    val cnt = counter.get(t).getOrElse(0) + 1
    counter = counter.updated(t, cnt)
  }

  counter.filter(_._2 > 1).map(_._1)
}

println(extract(List())) // List()
println(extract(List(1,2,3))) // List()
println(extract(List(2,2,2))) // List(2)
println(extract(List(2,3,2,0,2,3))) // List(2,3)

You can also use a foldLeft instead of foreach and use the empty map as the initial accumulator of foldLeft.

Malte Schwerhoff
  • 12,684
  • 4
  • 41
  • 71
  • The downvote is not mine, but I can only guess that it is there because this does not answer my question - which is to return the common value if all values are equal and None otherwise – Andrea Sep 26 '12 at 13:40
  • @Andrea Ok, then I misinterpreted your question (and answered it before the clarifying comments where made). Sorry. – Malte Schwerhoff Sep 26 '12 at 13:47