From 3e594565d4150411af3eaecc23232123d6f51fff Mon Sep 17 00:00:00 2001 From: Markus Hauck Date: Fri, 15 Jun 2018 14:56:53 +0200 Subject: [PATCH] Import splitWith from strawman-contrib Taken from: https://github.com/scala/collection-strawman/issues/241 --- .../collection/decorators/SeqDecorator.scala | 44 +++++++++++++++++++ .../decorators/SeqDecoratorTest.scala | 7 +++ 2 files changed, 51 insertions(+) diff --git a/src/main/scala/scala/collection/decorators/SeqDecorator.scala b/src/main/scala/scala/collection/decorators/SeqDecorator.scala index d9e4aa3..3bde67b 100644 --- a/src/main/scala/scala/collection/decorators/SeqDecorator.scala +++ b/src/main/scala/scala/collection/decorators/SeqDecorator.scala @@ -41,4 +41,48 @@ class SeqDecorator[C, S <: HasSeqOps[C]](coll: C)(implicit val seq: S) { def intersperse[B >: seq.A, That](start: B, sep: B, end: B)(implicit bf: BuildFrom[C, B, That]): That = bf.fromSpecificIterable(coll)(new View.IntersperseSurround(seq(coll), start, sep, end)) + /** Splits this collection into groups according to the given predicate. + * + * @param p the predicate used to discriminate elements + * @return A nested collection with groups of elements, + * opening new groups whenever the predicate + * changes the return type + * + * @example {{{ + * // Example 1: Split a list of integers into groups that are even / odd + * List(1, 2, 4, 6, 7).splitWith(i => i % 2 == 0) => List(List(1), List(2, 4, 6), List(7)) + * + * // Example 2: Split a list of chars into groups that are upper case or lower case + * List('a', 'b', 'C', 'D', 'e', 'f').splitWith(_.isUpper) => List(List('a', 'b'), List('C', 'D'), List('e', 'f')) + * }}} + */ + def splitWith[Group, That](p: seq.A => Boolean)(implicit bfGroup: BuildFrom[C, seq.A, Group], bfThat: BuildFrom[C, Group, That]): That = { + def newGroupBuilder() = bfGroup.newBuilder(coll) + + val groups: mutable.Builder[Group, That] = bfThat.newBuilder(coll) + val it: Iterator[seq.A] = seq(coll).iterator + + var currentGroup = newGroupBuilder() + var lastTestResult = Option.empty[Boolean] + + while (it.hasNext) { + val elem = it.next() + val currentTest = p(elem) + + lastTestResult match { + case None => + currentGroup.addOne(elem) + case Some(lastTest) if currentTest == lastTest => + currentGroup.addOne(elem) + case Some(_) => + groups.addOne(currentGroup.result()) + currentGroup = newGroupBuilder().addOne(elem) + } + + lastTestResult = Some(currentTest) + } + + groups.addOne(currentGroup.result()).result() + } + } diff --git a/src/test/scala/scala/collection/decorators/SeqDecoratorTest.scala b/src/test/scala/scala/collection/decorators/SeqDecoratorTest.scala index 26acb32..718e537 100644 --- a/src/test/scala/scala/collection/decorators/SeqDecoratorTest.scala +++ b/src/test/scala/scala/collection/decorators/SeqDecoratorTest.scala @@ -16,6 +16,13 @@ class SeqDecoratorTest { assertEquals(List(0, 1, 2), List(1).intersperse(0, 5, 2)) } + @Test def splitWith(): Unit = { + assertEquals(List(List()), List().splitWith(_ => true)) + assertEquals(List(List()), List().splitWith(_ => false)) + assertEquals(List(List(1), List(2, 4, 6), List(7)), List(1, 2, 4, 6, 7).splitWith(i => i % 2 == 0)) + assertEquals(List(List('a', 'b'), List('C', 'D'), List('e', 'f')), List('a', 'b', 'C', 'D', 'e', 'f').splitWith(_.isUpper)) + } + // This test just checks that there is no compilation error @Test def genericDecorator(): Unit = { val vector = Vector(1, 2, 3)