Skip to content

Commit 012a66b

Browse files
Automated commit of generated code
1 parent fbcccc4 commit 012a66b

3 files changed

Lines changed: 156 additions & 10 deletions

File tree

  • core/generated-sources/src
    • main/kotlin/org/jetbrains/kotlinx/dataframe
    • test/kotlin/org/jetbrains/kotlinx/dataframe/api

core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt

Lines changed: 71 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1112,13 +1112,77 @@ public fun <T, C> MoveClause<T, C>.after(column: KProperty<*>): DataFrame<T> = a
11121112

11131113
// endregion
11141114

1115-
/* TODO: implement 'before'
1116-
fun <T, C> MoveColsClause<T, C>.before(columnPath: ColumnPath) = before { columnPath.toColumnDef() }
1117-
fun <T, C> MoveColsClause<T, C>.before(column: Column) = before { column }
1118-
fun <T, C> MoveColsClause<T, C>.before(column: KProperty<*>) = before { column.toColumnDef() }
1119-
fun <T, C> MoveColsClause<T, C>.before(column: String) = before { column.toColumnDef() }
1120-
fun <T, C> MoveColsClause<T, C>.before(column: ColumnSelector<T, *>) = afterOrBefore(column, false)
1121-
*/
1115+
// region before
1116+
1117+
/**
1118+
* Moves columns, previously selected with [move][org.jetbrains.kotlinx.dataframe.api.move] to the position before the
1119+
* specified [column][org.jetbrains.kotlinx.dataframe.api.column] within the [DataFrame][org.jetbrains.kotlinx.dataframe.DataFrame].
1120+
*
1121+
* Returns a new [DataFrame][org.jetbrains.kotlinx.dataframe.DataFrame] with updated columns.
1122+
*
1123+
* See [Selecting Columns][org.jetbrains.kotlinx.dataframe.documentation.SelectingColumns].
1124+
*
1125+
* For more information: [See `move` on the documentation website.](https://kotlin.github.io/dataframe/move.html)
1126+
*
1127+
* ### This Before Overload
1128+
* Select or express columns using the [Columns Selection DSL][org.jetbrains.kotlinx.dataframe.api.ColumnsSelectionDsl].
1129+
* (Any (combination of) [Access API][org.jetbrains.kotlinx.dataframe.documentation.AccessApi]).
1130+
*
1131+
* This DSL is initiated by a [Columns Selector][org.jetbrains.kotlinx.dataframe.ColumnsSelector] lambda,
1132+
* which operates in the context of the [Columns Selection DSL][org.jetbrains.kotlinx.dataframe.api.ColumnsSelectionDsl] and
1133+
* expects you to return a [SingleColumn][org.jetbrains.kotlinx.dataframe.columns.SingleColumn] or [ColumnSet][org.jetbrains.kotlinx.dataframe.columns.ColumnSet] (so, a [ColumnsResolver][org.jetbrains.kotlinx.dataframe.columns.ColumnsResolver]).
1134+
* This is an entity formed by calling any (combination) of the functions
1135+
* in the DSL that is or can be resolved into one or more columns.
1136+
*
1137+
* #### NOTE:
1138+
* While you can use the [String API][org.jetbrains.kotlinx.dataframe.documentation.AccessApi.StringApi] and [KProperties API][org.jetbrains.kotlinx.dataframe.documentation.AccessApi.KPropertiesApi]
1139+
* in this DSL directly with any function, they are NOT valid return types for the
1140+
* [Columns Selector][org.jetbrains.kotlinx.dataframe.ColumnsSelector] lambda. You'd need to turn them into a [ColumnReference][org.jetbrains.kotlinx.dataframe.columns.ColumnReference] first, for instance
1141+
* with a function like [`col("name")`][org.jetbrains.kotlinx.dataframe.api.ColumnsSelectionDsl.col].
1142+
*
1143+
* ### Check out: [Columns Selection DSL Grammar][org.jetbrains.kotlinx.dataframe.api.ColumnsSelectionDsl.DslGrammar]
1144+
*
1145+
* &nbsp;&nbsp;&nbsp;&nbsp;
1146+
*
1147+
* [See Column Selectors on the documentation website.](https://kotlin.github.io/dataframe/columnselectors.html)
1148+
*
1149+
* ### Examples:
1150+
* ```kotlin
1151+
* df.move { age and weight }.before { surname }
1152+
* df.move { cols(3..5) }.before { col(2) }
1153+
* ```
1154+
*
1155+
* @param [column] A [ColumnSelector] specifying the column
1156+
* before which the selected columns will be placed.
1157+
*/
1158+
@Refine
1159+
@Interpretable("MoveBefore0")
1160+
public fun <T, C> MoveClause<T, C>.before(column: ColumnSelector<T, *>): DataFrame<T> = afterOrBefore(column, false)
1161+
1162+
/**
1163+
* Moves columns, previously selected with [move][org.jetbrains.kotlinx.dataframe.api.move] to the position before the
1164+
* specified [column][org.jetbrains.kotlinx.dataframe.api.column] within the [DataFrame][org.jetbrains.kotlinx.dataframe.DataFrame].
1165+
*
1166+
* Returns a new [DataFrame][org.jetbrains.kotlinx.dataframe.DataFrame] with updated columns.
1167+
*
1168+
* See [Selecting Columns][org.jetbrains.kotlinx.dataframe.documentation.SelectingColumns].
1169+
*
1170+
* For more information: [See `move` on the documentation website.](https://kotlin.github.io/dataframe/move.html)
1171+
*
1172+
* ### This Before Overload
1173+
* Select columns using their [column names][String]
1174+
* ([String API][org.jetbrains.kotlinx.dataframe.documentation.AccessApi.StringApi]).
1175+
*
1176+
* ### Examples:
1177+
* ```kotlin
1178+
* df.move("age", "weight").before("surname")
1179+
* ```
1180+
* @param [column] The [Column Name][String] specifying the column
1181+
* before which the selected columns will be placed.
1182+
*/
1183+
public fun <T, C> MoveClause<T, C>.before(column: String): DataFrame<T> = before { column.toColumnAccessor() }
1184+
1185+
// endregion
11221186

11231187
@Deprecated(TO_LEFT, ReplaceWith(TO_LEFT_REPLACE), DeprecationLevel.ERROR)
11241188
public fun <T, C> MoveClause<T, C>.toLeft(): DataFrame<T> = to(0)

core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,25 @@ import org.jetbrains.kotlinx.dataframe.DataColumn
66
import org.jetbrains.kotlinx.dataframe.DataFrame
77
import org.jetbrains.kotlinx.dataframe.api.ColumnsSelectionDsl
88
import org.jetbrains.kotlinx.dataframe.api.MoveClause
9+
import org.jetbrains.kotlinx.dataframe.api.after
10+
import org.jetbrains.kotlinx.dataframe.api.asColumnGroup
911
import org.jetbrains.kotlinx.dataframe.api.cast
1012
import org.jetbrains.kotlinx.dataframe.api.getColumn
1113
import org.jetbrains.kotlinx.dataframe.api.getColumnGroup
1214
import org.jetbrains.kotlinx.dataframe.api.getColumnWithPath
15+
import org.jetbrains.kotlinx.dataframe.api.move
1316
import org.jetbrains.kotlinx.dataframe.api.toDataFrame
1417
import org.jetbrains.kotlinx.dataframe.columns.ColumnPath
1518
import org.jetbrains.kotlinx.dataframe.columns.ColumnWithPath
1619
import org.jetbrains.kotlinx.dataframe.columns.UnresolvedColumnsPolicy
20+
import org.jetbrains.kotlinx.dataframe.columns.toColumnSet
1721
import org.jetbrains.kotlinx.dataframe.impl.DataFrameReceiver
1822
import org.jetbrains.kotlinx.dataframe.impl.asList
1923
import org.jetbrains.kotlinx.dataframe.impl.columns.toColumnWithPath
2024
import org.jetbrains.kotlinx.dataframe.impl.columns.tree.ColumnPosition
2125
import org.jetbrains.kotlinx.dataframe.impl.columns.tree.getOrPut
26+
import org.jetbrains.kotlinx.dataframe.path
2227

23-
// TODO: support 'before' mode
2428
internal fun <T, C> MoveClause<T, C>.afterOrBefore(column: ColumnSelector<T, *>, isAfter: Boolean): DataFrame<T> {
2529
val removeResult = df.removeImpl(columns = columns)
2630

@@ -35,8 +39,9 @@ internal fun <T, C> MoveClause<T, C>.afterOrBefore(column: ColumnSelector<T, *>,
3539
if (sourceSegments.size <= targetSegments.size &&
3640
sourceSegments.indices.all { targetSegments[it] == sourceSegments[it] }
3741
) {
42+
val afterOrBefore = if (isAfter) "after" else "before"
3843
throw IllegalArgumentException(
39-
"Cannot move column '${sourcePath.joinToString()}' after its own child column '${targetPath.joinToString()}'",
44+
"Cannot move column '${sourcePath.joinToString()}' $afterOrBefore its own child column '${targetPath.joinToString()}'",
4045
)
4146
}
4247
}
@@ -78,7 +83,16 @@ internal fun <T, C> MoveClause<T, C>.afterOrBefore(column: ColumnSelector<T, *>,
7883
}
7984
ColumnToInsert(path, sourceCol.data, refNode)
8085
}
81-
return removeResult.df.insertImpl(toInsert)
86+
if (isAfter) {
87+
return removeResult.df.insertImpl(toInsert)
88+
}
89+
90+
// Move the target column after the removed/inserted columns
91+
val logicOfAfter = removeResult.df.insertImpl(toInsert)
92+
val lastOfInsertedCols = toInsert.last().insertionPath
93+
val siblingsOfTargetAndTarget = removeResult.df[parentPath].asColumnGroup().columns().map { parentPath + it.path }
94+
val target = siblingsOfTargetAndTarget.filter { it.last() == targetPath.last() }
95+
return logicOfAfter.move { target.toColumnSet() }.after { lastOfInsertedCols }
8296
}
8397

8498
internal fun <T, C> MoveClause<T, C>.moveImpl(

core/generated-sources/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,4 +152,72 @@ class MoveTests {
152152
grouped.move { "a"["b"] }.after { "a"["b"] }
153153
}.message shouldBe "Cannot move column 'a/b' after its own child column 'a/b'"
154154
}
155+
156+
@Test
157+
fun `move before first`() {
158+
val df = dataFrameOf("1", "2")(1, 2)
159+
shouldNotThrowAny {
160+
df.move("2").before("1") shouldBe dataFrameOf("2", "1")(2, 1)
161+
}
162+
}
163+
164+
@Test
165+
fun `move before in nested structure`() {
166+
val df = grouped.move { "a"["b"] }
167+
.before { "a"["c"]["d"] }
168+
df.columnNames() shouldBe listOf("q", "a", "b", "w", "e", "r")
169+
df["a"].asColumnGroup().columnNames() shouldBe listOf("c")
170+
df["a"]["c"].asColumnGroup().columnNames() shouldBe listOf("b", "d")
171+
}
172+
173+
@Test
174+
fun `move before multiple columns`() {
175+
val df = grouped.move { "a"["b"] and "b"["c"] }
176+
.before { "a"["c"]["d"] }
177+
df.columnNames() shouldBe listOf("q", "a", "b", "w", "e", "r")
178+
df["a"].asColumnGroup().columnNames() shouldBe listOf("c")
179+
df["a"]["c"].asColumnGroup().columnNames() shouldBe listOf("b", "c", "d")
180+
df["b"].asColumnGroup().columnNames() shouldBe listOf("d")
181+
}
182+
183+
@Test
184+
fun `move before with column selector`() {
185+
val df = grouped.move { colsAtAnyDepth().filter { it.name == "r" || it.name == "w" } }
186+
.before { "a"["c"]["d"] }
187+
df.columnNames() shouldBe listOf("q", "a", "b", "e")
188+
df["a"]["c"].asColumnGroup().columnNames() shouldBe listOf("w", "r", "d")
189+
}
190+
191+
@Test
192+
fun `move before between groups`() {
193+
val df = grouped.move { "a"["b"] }.before { "b"["d"] }
194+
df.columnNames() shouldBe listOf("q", "a", "b", "w", "e", "r")
195+
df["a"].asColumnGroup().columnNames() shouldBe listOf("c")
196+
df["b"].asColumnGroup().columnNames() shouldBe listOf("c", "b", "d")
197+
}
198+
199+
@Test
200+
fun `should throw when moving parent before child`() {
201+
// Simple case: direct parent-child relationship
202+
shouldThrow<IllegalArgumentException> {
203+
grouped.move("a").before { "a"["b"] }
204+
}.message shouldBe "Cannot move column 'a' before its own child column 'a/b'"
205+
206+
// Nested case: deeper parent-child relationship
207+
shouldThrow<IllegalArgumentException> {
208+
grouped.move("a").before { "a"["c"]["d"] }
209+
}.message shouldBe "Cannot move column 'a' before its own child column 'a/c/d'"
210+
211+
// Group case: moving group after its nested column
212+
shouldThrow<IllegalArgumentException> {
213+
grouped.move { "a"["c"] }.before { "a"["c"]["d"] }
214+
}.message shouldBe "Cannot move column 'a/c' before its own child column 'a/c/d'"
215+
}
216+
217+
@Test
218+
fun `should throw when moving column before itself`() {
219+
shouldThrow<IllegalArgumentException> {
220+
grouped.move { "a"["b"] }.before { "a"["b"] }
221+
}.message shouldBe "Cannot move column 'a/b' before its own child column 'a/b'"
222+
}
155223
}

0 commit comments

Comments
 (0)