Skip to content

Commit

Permalink
Merge pull request #7 from ryan-williams/rle
Browse files Browse the repository at this point in the history
add runLengthEncode api based on Ordering
  • Loading branch information
ryan-williams committed Mar 22, 2017
2 parents 0a7e7f1 + 400f06d commit e547f83
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 29 deletions.
2 changes: 1 addition & 1 deletion build.sbt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name := "iterator"

version := "1.2.0"
version := "1.2.1"

addScala212

Expand Down
7 changes: 5 additions & 2 deletions src/main/scala/org/hammerlab/iterator/RunLengthIterator.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,17 @@ import spire.implicits._
* See RunLengthIteratorTest for examples.
*/
class RunLengthIterator[K](val it: BufferedIterator[K]) {
def runLengthEncode: Iterator[(K, Int)] =
def runLengthEncode(implicit ord: Ordering[K]): Iterator[(K, Int)] =
runLengthEncode(ord.compare(_, _) == 0)

def runLengthEncode(cmpFn: (K, K) => Boolean = (_ == _)): Iterator[(K, Int)] =
new Iterator[(K, Int)] {
override def hasNext: Boolean = it.hasNext

override def next(): (K, Int) = {
val elem = it.head
var count = 0
while (it.hasNext && it.head == elem) {
while (it.hasNext && cmpFn(it.head, elem)) {
it.next()
count += 1
}
Expand Down
2 changes: 1 addition & 1 deletion src/main/scala/org/hammerlab/stats/Stats.scala
Original file line number Diff line number Diff line change
Expand Up @@ -434,7 +434,7 @@ object Stats {
var sum = 0
var i = 0
val runs = ArrayBuffer[(K, Int)]()
val runLengthIterator = it.runLengthEncode
val runLengthIterator = it.runLengthEncode()
while (i < N && runLengthIterator.hasNext) {
val (elem, count) = runLengthIterator.next()

Expand Down
80 changes: 55 additions & 25 deletions src/test/scala/org/hammerlab/iterator/RunLengthIteratorTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,39 +5,69 @@ import org.hammerlab.test.Suite

class RunLengthIteratorTest extends Suite {

def check[T](elems: T*)(expected: (T, Int)*): Unit = {
elems.iterator.runLengthEncode.toSeq should be(expected)
}
{
def check[T](elems: T*)(expected: (T, Int)*): Unit = {
elems.iterator.runLengthEncode().toSeq should be(expected)
}

test("empty") {
check()()
}
test("empty") {
check()()
}

test("single") {
check("a")("a" -> 1)
}
test("single") {
check("a")("a" 1)
}

test("one run") {
check("a", "a", "a")("a" -> 3)
}
test("one run") {
check("a", "a", "a")("a" 3)
}

test("two singletons") {
check("a", "b")("a" -> 1, "b" -> 1)
}
test("two singletons") {
check("a", "b")("a" 1, "b" 1)
}

test("run singleton") {
check("a", "a", "a", "b")("a" -> 3, "b" -> 1)
}
test("run singleton") {
check("a", "a", "a", "b")("a" 3, "b" 1)
}

test("singleton run") {
check("a", "b", "b", "b")("a" -> 1, "b" -> 3)
}
test("singleton run") {
check("a", "b", "b", "b")("a" 1, "b" 3)
}

test("single run single") {
check("z", "y", "y", "x")("z" 1, "y" 2, "x" 1)
}

test("single run single") {
check("z", "y", "y", "x")("z" -> 1, "y" -> 2, "x" -> 1)
test("two runs") {
check("a", "a", "a", "b", "b", "b", "b")("a" 3, "b" 4)
}
}

test("two runs") {
check("a", "a", "a", "b", "b", "b", "b")("a" -> 3, "b" -> 4)
{
val parityOrdering =
Ordering.by[Int, Int](_ % 2)

def check(elems: Int*)(expected: (Int, Int)*): Unit = {
elems.iterator.runLengthEncode(parityOrdering).toSeq should be(expected)
}

test("evens and odds") {
check(
1, 5, 3, 7,
2,
5, 5,
4, 6, 8,
1,
2
)(
1 4,
2 1,
5 2,
4 3,
1 1,
2 1
)
}
}

}

0 comments on commit e547f83

Please sign in to comment.