2016-07-28 2 views
2

Я, имеющие следующую итератора:Итерация в Scala: проверка предыдущих значений

val it = Iterator(("a",5),("a",3),("a",2),("a",1),("b",8),("b",2),("b",1),("c",1)) 

Значения внутри отсортированные во-первых, первый элемент (строка), а во-вторых, от второго (Int). Как я могу получить первые 2 значения из каждой буквы. Таким образом, результат должен быть в этом примере:

Iterator(("a",5),("a",3),("b",8),("b",2),("c",1)) 

Это может быть сделано с GroupBy:

it.toList.groupBy(_._1).mapValues(_.take(2)).values.flatten.toIterator 

, но я хотел бы увидеть решение, которое проходит через каждый элемент и проверить предыдущий «строка» элемент, и если его то же самое и «счет» меньше 2, то это значение yield.

Edit:

Следуя логике @jwvh ответа: Как это может быть обобщена принять первые значения N вместо первого 2?

ответ

2

Было бы хорошо, если бы мы не сделали должны потреблять весь итератор сразу.

ОБНОВЛЕНО

case class LimitItr[A,B](var itr: Iterator[(A,B)], reps:Int) extends Iterator[(A,B)] { 
    private var memory: List[A] = List() 
    def hasNext = itr.hasNext 
    def next() = { 
    val current = itr.next 
    if (!memory.headOption.contains(current._1)) 
     memory = List() 
    memory = current._1 :: memory 
    if (memory.length >= reps) { 
     itr = itr.dropWhile(_._1 == memory.head) 
     itr.hasNext // force the iterator forward 
    } 
    current 
    } 
} 

Использование:

val repLimitedItr = LimitItr(itrOfTuples, numOfRepetitionsAllowed) 
+0

спасибо! Я попытался «вывести» результаты в цикле 'while':' while (lit.hasNext()) {yield lit.next()} ', но он не работает. Есть ли способ сделать это? –

+0

или это 'for (elem <- lit) yield elem' считается хорошей практикой? –

+0

Приносим извинения за комментарии. как это можно было бы обобщить на N первых значений вместо первых 2? –

3

Вы можете использовать fold операцию, но она более громоздкая, чем ваше решение:

val result = it.foldLeft((Seq[(String, Int)](), "", 0)){ 
    case ((acc, prev, count), (l, n)) => 
    if (prev == l) { 
     if (count < 2) (acc :+ (l, n), prev, count + 1) 
     else (acc, prev, count + 1) 
    } 
    else (acc :+ (l, n), l, 1) 
} 

println(result._1) 
2

Другое решение с использованием foldLeft может быть:

it.foldLeft (List[(String, Int)]()) { 
    case (acc, (k,v)) if acc.takeWhile(_._1==k).size<2 => (k,v)::acc 
    case (acc, _) => acc 
}.reverse 
+0

Прохладный один! Есть ли способ использования 'yield' в вашем ответе, вместо того, чтобы« хранить »его до конечного результата? –

Смежные вопросы