From ba50892841f16377f8ea38fbcc077ab090b7113c Mon Sep 17 00:00:00 2001 From: Sergey Kozlov Date: Mon, 5 Aug 2024 21:00:56 +0200 Subject: [PATCH] Slice improvements --- .../skozlov/commons/collection/Slice.scala | 48 ++++++++++- .../collection/WritableSliceTest.scala | 81 +++++++++++++++++++ 2 files changed, 128 insertions(+), 1 deletion(-) diff --git a/src/main/scala/com/github/skozlov/commons/collection/Slice.scala b/src/main/scala/com/github/skozlov/commons/collection/Slice.scala index 1b24c83..135f4f5 100644 --- a/src/main/scala/com/github/skozlov/commons/collection/Slice.scala +++ b/src/main/scala/com/github/skozlov/commons/collection/Slice.scala @@ -12,7 +12,7 @@ trait SliceOps[ ] extends IndexedSeqView[A] { this: SubSlice => - import SliceOps._ + import SliceOps.* def underlying: Underlying def from: Int @@ -59,6 +59,52 @@ trait SliceOps[ override def takeRight(n: Int): SubSlice = drop(length - math.max(n, 0)) override def dropRight(n: Int): SubSlice = take(length - math.max(n, 0)) + + override def tail: SubSlice = { + if (isEmpty) { + throw new UnsupportedOperationException("Empty Slice doesn't have .tail") + } + drop(1) + } + + override def init: SubSlice = { + if (isEmpty) { + throw new UnsupportedOperationException("Empty Slice doesn't have .init") + } + dropRight(1) + } + + override def tails: Iterator[SubSlice] = { + for (numDrop <- Iterator.range(0, length + 1)) yield drop(numDrop) + } + + override def inits: Iterator[SubSlice] = { + for (numDrop <- Iterator.range(0, length + 1)) yield dropRight(numDrop) + } + + override def sliding(size: Int, step: Int): Iterator[SubSlice] = { + require(size >= 1, s"Too small size: $size") + require(step >= 1, s"Too small step: $step") + Iterator + .iterate(0 until size) { range => + (range.start + step) until (range.end + step) + } + .takeWhile { _.start < length } + .map { range => + this.slice( + from = range.start, + until = math.min(range.end, length), + ) + } + } + + override def sliding(size: Int): Iterator[SubSlice] = { + sliding(size, step = 1) + } + + override def grouped(size: Int): Iterator[SubSlice] = { + sliding(size, step = size) + } } object SliceOps { diff --git a/src/test/scala/com/github/skozlov/commons/collection/WritableSliceTest.scala b/src/test/scala/com/github/skozlov/commons/collection/WritableSliceTest.scala index 5fe8c4e..7cb9cdb 100644 --- a/src/test/scala/com/github/skozlov/commons/collection/WritableSliceTest.scala +++ b/src/test/scala/com/github/skozlov/commons/collection/WritableSliceTest.scala @@ -128,4 +128,85 @@ class WritableSliceTest extends Test { (slice dropRight 2).toSeq shouldBe Seq() (slice dropRight 3).toSeq shouldBe Seq() } + + test("tail") { + val underlying = Array(1, 2, 3, 4, 5) + the[UnsupportedOperationException] thrownBy { + WritableSlice(underlying)(from = 1, until = 1).tail + } should have message "Empty Slice doesn't have .tail" + WritableSlice(underlying)(from = 1, until = 2).tail.toSeq shouldBe Seq() + WritableSlice(underlying)(from = 1, until = 4).tail.toSeq shouldBe Seq(3, 4) + } + + test("init") { + val underlying = Array(1, 2, 3, 4, 5) + the[UnsupportedOperationException] thrownBy { + WritableSlice(underlying)(from = 1, until = 1).init + } should have message "Empty Slice doesn't have .init" + WritableSlice(underlying)(from = 1, until = 2).init.toSeq shouldBe Seq() + WritableSlice(underlying)(from = 1, until = 4).init.toSeq shouldBe Seq(2, 3) + } + + test("tails") { + val underlying = Array(1, 2, 3, 4) + (WritableSlice(underlying)(from = 1, until = 1).tails map { _.toSeq }).toSeq + .shouldBe(Seq(Seq())) + (WritableSlice(underlying)(from = 1, until = 3).tails map { _.toSeq }).toSeq + .shouldBe(Seq(Seq(2, 3), Seq(3), Seq())) + } + + test("inits") { + val underlying = Array(1, 2, 3, 4) + (WritableSlice(underlying)(from = 1, until = 1).inits map { _.toSeq }).toSeq + .shouldBe(Seq(Seq())) + (WritableSlice(underlying)(from = 1, until = 3).inits map { _.toSeq }).toSeq + .shouldBe(Seq(Seq(2, 3), Seq(2), Seq())) + } + + test("sliding") { + val slice = WritableSlice(Array(1, 2, 3, 4, 5, 6, 7))(from = 1, until = 6) + slice.toSeq shouldBe Seq(2, 3, 4, 5, 6) + slice.slice(from = 0, until = 0).sliding(size = 1).toSeq shouldBe Seq() + (slice.sliding(size = 1) map { _.toSeq }).toSeq shouldBe Seq( + Seq(2), + Seq(3), + Seq(4), + Seq(5), + Seq(6), + ) + (slice.sliding(size = 2) map { _.toSeq }).toSeq shouldBe Seq( + Seq(2, 3), + Seq(3, 4), + Seq(4, 5), + Seq(5, 6), + Seq(6), + ) + (slice.sliding(size = 2, step = 2) map { _.toSeq }).toSeq shouldBe Seq( + Seq(2, 3), + Seq(4, 5), + Seq(6), + ) + (slice.sliding(size = 2, step = 3) map { _.toSeq }).toSeq shouldBe Seq( + Seq(2, 3), + Seq(5, 6), + ) + } + + test("grouped") { + val slice = WritableSlice(Array(1, 2, 3, 4, 5, 6, 7))(from = 1, until = 6) + slice.toSeq shouldBe Seq(2, 3, 4, 5, 6) + slice.slice(from = 0, until = 0).grouped(size = 1).toSeq shouldBe Seq() + (slice.grouped(size = 1) map { _.toSeq }).toSeq shouldBe Seq( + Seq(2), + Seq(3), + Seq(4), + Seq(5), + Seq(6), + ) + (slice.grouped(size = 2) map { _.toSeq }).toSeq shouldBe Seq( + Seq(2, 3), + Seq(4, 5), + Seq(6), + ) + } }