Skip to content

Commit 226accb

Browse files
committed
adds splitBy extension method on scala.collection.Iterable
1 parent 8f8ac74 commit 226accb

File tree

2 files changed

+84
-1
lines changed

2 files changed

+84
-1
lines changed

src/main/scala/scala/collection/decorators/IterableDecorator.scala

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,46 @@ class IterableDecorator[C, I <: IsIterable[C]](coll: C)(implicit val it: I) {
3333
def lazyFoldRight[B](z: B)(op: it.A => Either[B, B => B]): B =
3434
it(coll).iterator.lazyFoldRight(z)(op)
3535

36+
37+
/**
38+
* Constructs an iterator where consecutive elements are accumulated as
39+
* long as the output of f for each element doesn't change.
40+
* <pre>
41+
* Vector(1,2,2,3,3,3,2,2)
42+
* .splitBy(identity)
43+
* </pre>
44+
* produces
45+
* <pre>
46+
* Iterator(Vector(1),
47+
* Vector(2,2),
48+
* Vector(3,3,3),
49+
* Vector(2,2))
50+
* </pre>
51+
*
52+
* @param f the function to compute a key for an element
53+
* @tparam K the type of the computed key
54+
* @return an iterator of sequences of the consecutive elements with the
55+
* same key in the original iterator
56+
*/
57+
def splitBy[K, That, CC](f: it.A => K)(implicit bf: BuildFrom[C, it.A, That], bff: BuildFrom[C, That, CC]): CC = {
58+
val builder = bff.newBuilder(coll)
59+
60+
val iterator = it(coll).iterator
61+
var init = bf.newBuilder(coll)
62+
if (iterator.hasNext) {
63+
var ref = iterator.next();
64+
init += ref
65+
while (iterator.hasNext) {
66+
val el = iterator.next();
67+
if (f(el) != f(ref)) {
68+
builder += init.result()
69+
init = bf.newBuilder(coll)
70+
ref = el
71+
}
72+
init += el
73+
}
74+
builder += init.result()
75+
}
76+
builder.result();
77+
}
3678
}

src/test/scala/scala/collection/decorators/IterableDecoratorTest.scala

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ package scala.collection
22
package decorators
33

44
import org.junit.{Assert, Test}
5-
import scala.collection.immutable.{LazyList, List, Range, Map}
5+
6+
import scala.collection.immutable.{LazyList, List, Map, Range}
67

78
class IterableDecoratorTest {
89

@@ -36,4 +37,44 @@ class IterableDecoratorTest {
3637
Assert.assertEquals(2, result2)
3738
}
3839

40+
@Test
41+
def splitByShouldHonorEmptyIterator(): Unit = {
42+
val split = Vector.empty[Int].splitBy(identity)
43+
Assert.assertEquals(Vector.empty, split)
44+
}
45+
46+
@Test
47+
def splitByShouldReturnSingleSeqWhenSingleElement(): Unit = {
48+
val value = Vector("1")
49+
val split = value.splitBy(identity)
50+
Assert.assertEquals(Vector(value), split)
51+
}
52+
53+
@Test
54+
def splitByShouldReturnSingleSeqWhenAllElHaveTheSameKey(): Unit = {
55+
val value = Vector("1", "1", "1")
56+
val split = value.splitBy(identity)
57+
Assert.assertEquals(Vector(value), split)
58+
}
59+
60+
@Test
61+
def splitByShouldReturnVectorOfVectorOrConsecutiveElementsWithTheSameKey(): Unit = {
62+
val value = Vector("1", "2", "2", "3", "3", "3", "2", "2")
63+
val split: Vector[Vector[String]] = value.splitBy(identity)
64+
Assert.assertEquals(Vector(Vector("1"), Vector("2", "2"), Vector("3", "3", "3"), Vector("2", "2")), split)
65+
}
66+
67+
@Test
68+
def splitByShouldReturnListOfListOfConsecutiveElementsWithTheSameKey(): Unit = {
69+
val value = List("1", "2", "2", "3", "3", "3", "2", "2")
70+
val split: List[List[String]] = value.splitBy(identity)
71+
Assert.assertEquals(List(List("1"), List("2", "2"), List("3", "3", "3"), List("2", "2")), split)
72+
}
73+
74+
@Test
75+
def splitByShouldReturnSetOfSetOfConsecutiveElementsWithTheSameKey(): Unit = {
76+
val value = Set("1", "2", "2", "3", "3", "3", "2", "2")
77+
val split: Set[Set[String]] = value.splitBy(identity)
78+
Assert.assertEquals(Set(Set("1"), Set("2"), Set("3")), split)
79+
}
3980
}

0 commit comments

Comments
 (0)