Skip to content

Commit 0ba7fee

Browse files
committed
fixed negative selection; inc version to 1.2.3
1 parent a7de31e commit 0ba7fee

File tree

4 files changed

+53
-26
lines changed

4 files changed

+53
-26
lines changed

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ task javadocJar(type: Jar, dependsOn: javadoc) {
5151
//group = 'de.mpicbg.scicomp'
5252
group = 'com.github.holgerbrandl'
5353
//version = '1.1.9'
54-
version = '1.2.3-SNAPSHOT'
54+
version = '1.2.3'
5555

5656

5757
artifacts {

src/main/kotlin/kscript/KscriptUtil.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import kotlin.system.exitProcess
77
*/
88

99
/**
10-
* Just used interally to prevent [stopIfNot] to quit the process when running in unit-test mode.
10+
* Just used internally to prevent [stopIfNot] to quit the process when running in unit-test mode.
1111
* It throw an IllegalArgumentException instead.
1212
*/
1313
internal var isTestMode = false

src/main/kotlin/kscript/text/Tables.kt

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -83,18 +83,33 @@ fun List<Row>.print(separator: String = "\t") = asSequence().print(separator)
8383
/** Internal representations for column selection indices. Usually not use directly but rather via [with] and [without].
8484
*/
8585
abstract class ColSelect(val indices: Array<Int> = emptyArray()) {
86+
87+
// irrespective of selection mode (positive or negative) indices must be positive in here
88+
init {
89+
stopIfNot(indices.all { it > 0 }) {
90+
"kscript.text.* is using 1-based arrays to ease awk transition, so indices must be strictly positive"
91+
}
92+
}
93+
8694
abstract fun and(column: Int): ColSelect
8795
abstract fun and(range: IntRange): ColSelect
96+
abstract fun process(lines: Sequence<Row>): Sequence<Row>
8897
}
8998

90-
class PosSelect(arrayOf: Array<Int>) : ColSelect(arrayOf) {
99+
class PosSelect(columnIndices: Array<Int>) : ColSelect(columnIndices) {
91100
override fun and(column: Int) = PosSelect(arrayOf(*indices, column))
92101
override fun and(range: IntRange) = PosSelect(arrayOf(*indices, *range.toList().toTypedArray()))
102+
103+
override fun process(lines: Sequence<Row>): Sequence<Row> = lines.map { row -> Row(indices.map { row[it] }) }
93104
}
94105

95-
class NegSelect(arrayOf: Array<Int>) : ColSelect(arrayOf) {
106+
class NegSelect(columnIndices: Array<Int>) : ColSelect(columnIndices) {
96107
override fun and(column: Int) = NegSelect(arrayOf(*indices, column))
97108
override fun and(range: IntRange) = NegSelect(arrayOf(*indices, *range.toList().toTypedArray()))
109+
110+
override fun process(lines: Sequence<Row>): Sequence<Row> = lines.map {
111+
Row(it.filterIndexed { index, _ -> !indices.contains(index+1) })
112+
}
98113
}
99114

100115
/** Starts building a column selection index. Both positive and negative indices are supported. */
@@ -116,24 +131,20 @@ fun Sequence<Row>.select(vararg colIndices: Int): Sequence<Row> {
116131
"Can not mix positive and negative selections"
117132
}
118133

119-
val selector = if (isPositive) PosSelect(arrayOf(*colIndices.toTypedArray())) else NegSelect(arrayOf(*colIndices.toTypedArray()))
134+
val selector = if (isPositive) {
135+
PosSelect(arrayOf(*colIndices.toTypedArray()))
136+
} else {
137+
NegSelect(arrayOf(*colIndices.map { -it }.toTypedArray()))
138+
}
120139

121140
return select(selector)
122141
}
123142

124-
fun Sequence<Row>.select(columns: ColSelect): Sequence<Row> {
143+
fun Sequence<Row>.select(columnSelector: ColSelect): Sequence<Row> {
125144
// more efficient but does not allow to change the order
126-
// return map { it.filterIndexed { index, _ -> retainColumn(columns, index + 1) } }
127-
128-
stopIfNot(columns.indices.all { it != 0 }) { "kscript.text.* is using 1-based arrays to ease awk transition" }
145+
// return map { it.filterIndexed { index, _ -> retainColumn(columnSelector, index + 1) } }
129146

130-
return if (columns is PosSelect) {
131-
// positive selection
132-
map { row -> Row(columns.indices.map { row[it] }) }
133-
} else {
134-
// negative selection
135-
map { Row(it.filterIndexed { index, _ -> !columns.indices.contains(index) }) }
136-
}
147+
return columnSelector.process(this)
137148
}
138149

139150

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
package kscript.test
22

33
import io.kotlintest.matchers.*
4-
import io.kotlintest.specs.StringSpec
5-
import kscript.isTestMode
6-
import kscript.text.*
4+
import kscript.text.resolveArgFile
5+
import kscript.text.select
6+
import kscript.text.split
7+
import kscript.text.with
8+
import org.junit.Test
79

810
/**
911
* @author Holger Brandl
@@ -16,12 +18,15 @@ fun flightsZipped() = resolveArgFile(arrayOf("src/test/resources/flights.tsv.gz"
1618
fun flights() = resolveArgFile(arrayOf("src/test/resources/flights.txt"))
1719

1820

19-
class SupportApiTest : StringSpec() { init {
21+
class SupportApiTest {
2022

21-
isTestMode = true
23+
init {
24+
kscript.isTestMode = true
25+
}
2226

2327

24-
"extract field with column filter" {
28+
@Test
29+
fun `extract field with column filter`() {
2530
someFlights().split().
2631
filter { it[12] == "N14228" }.
2732
map { it[13] }.
@@ -33,23 +38,34 @@ class SupportApiTest : StringSpec() { init {
3338
}
3439

3540

36-
"allow to select columsn" {
41+
@Test
42+
fun `allow to select column`() {
3743
someFlights().split()
3844
.select(with(3).and(11..13).and(1))
3945
.first().data shouldBe listOf("day", "flight", "tailnum", "origin", "year")
4046
}
4147

4248

43-
"rejeced mixed select" {
49+
@Test
50+
fun `is should perform a negative selection`() {
51+
someFlights().split()
52+
.select(1, 2, 3)
53+
.select(-2)
54+
.first().data shouldBe listOf("year", "day")
55+
}
56+
57+
58+
@Test
59+
fun `rejeced mixed select`() {
4460
shouldThrow<IllegalArgumentException> {
4561
someFlights().split().select(1, -2)
4662
}.message shouldBe "[ERROR] Can not mix positive and negative selections"
4763
}
4864

4965

50-
"compressed lines should be unzipped on the fly"{
66+
@Test
67+
fun `compressed lines should be unzipped on the fly`() {
5168
resolveArgFile(arrayOf("src/test/resources/flights.tsv.gz")).
5269
drop(1).first() should startWith("2013")
5370
}
54-
}
5571
}

0 commit comments

Comments
 (0)