2012-09-26 6 views
2

Мне часто приходится проверять, равно ли много значений и в случае извлечь общее значение. То есть, мне нужна функция, которая будет работать, как следует:Scala: извлечение повторяющегося значения из списка

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

Предполагая, что один имеет сутенера, который добавит tailOption к seqs (это тривиально, чтобы написать одну или есть один в scalaz), одна реализация выглядит

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 
} 

есть ли что-то подобное - возможно, более общий - уже в Scalaz, или какой-нибудь простой способ, чтобы написать это?

+0

Что нужно извлечь (List (2,2,2,3,3)) вернуть? – Jan

+0

он должен возвращать 'None' – Andrea

+0

Так вы в основном хотите знать, если все значения в списке равны? – drexin

ответ

3

Это кажется очень сложным способом, чтобы написать

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

Вам не нужно tailOption, так как анонимная функция, которая получает передается в качестве аргумента flatMap выполняется только если l не пусто.

+2

С 'headOption.filter' он может быть немного короче. – incrop

+0

Да, но это не так, как обычно используется фильтр, поэтому я думаю, что это сделает его менее читаемым. –

+0

Да, это был довольно сложный способ написать это :-) – Andrea

-1

Вы можете использовать карту, чтобы подсчитать число вхождений каждого элемента в списке, а затем возвращать только те, которые встречаются более одного раза:

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) 

Вы также можете использовать foldLeft вместо foreach и использования пустая карта в качестве исходного аккумулятора foldLeft.

+0

Почему downvote? Прокомментируйте, пожалуйста. –

+0

Сдвиг не мой, но я могу только догадываться, что он есть, потому что это не отвечает на мой вопрос, который должен вернуть общее значение, если все значения равны, и None иначе – Andrea

+0

@Andrea Хорошо, тогда я неверно истолковал ваш вопрос (и ответил на него до разъяснений, которые были сделаны). Сожалею. –

2

Если вы хотите, чтобы удалить дубликаты toSet достаточно:

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) 
+0

или xs.distinct вместо xs.toSet. –

2

Это беглое решение

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