From 8f73fb850cff9d4e5251857453cd8368959cc2d5 Mon Sep 17 00:00:00 2001 From: Carlo Maria Proietti Date: Thu, 9 Oct 2025 12:37:39 +0200 Subject: [PATCH 01/30] test is not working --- .../jetbrains/kotlinx/dataframe/api/move.kt | 4 +++ .../kotlinx/dataframe/impl/api/move.kt | 27 +++++++++++++++++++ .../jetbrains/kotlinx/dataframe/api/move.kt | 7 +++++ 3 files changed, 38 insertions(+) diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt index a2e761dfee..ecb3ab51c7 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt @@ -537,6 +537,10 @@ public fun MoveClause.under( @Interpretable("MoveTo") public fun MoveClause.to(columnIndex: Int): DataFrame = moveTo(columnIndex) +@Refine +@Interpretable("MoveTo") +public fun MoveClause.to(columnIndex: Int, insideGroup: Boolean): DataFrame = moveTo(columnIndex, insideGroup) + /** * Moves columns, previously selected with [move] to the top-level within the [DataFrame]. * Moved columns name can be specified via special ColumnSelectionDsl. diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt index 7d874eca3a..2a1028b722 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt @@ -8,10 +8,12 @@ import org.jetbrains.kotlinx.dataframe.api.ColumnsSelectionDsl import org.jetbrains.kotlinx.dataframe.api.MoveClause import org.jetbrains.kotlinx.dataframe.api.after import org.jetbrains.kotlinx.dataframe.api.asColumnGroup +import org.jetbrains.kotlinx.dataframe.api.before import org.jetbrains.kotlinx.dataframe.api.cast import org.jetbrains.kotlinx.dataframe.api.getColumn import org.jetbrains.kotlinx.dataframe.api.getColumnGroup import org.jetbrains.kotlinx.dataframe.api.getColumnWithPath +import org.jetbrains.kotlinx.dataframe.api.getColumns import org.jetbrains.kotlinx.dataframe.api.move import org.jetbrains.kotlinx.dataframe.api.toDataFrame import org.jetbrains.kotlinx.dataframe.columns.ColumnPath @@ -124,3 +126,28 @@ internal fun MoveClause.moveTo(columnIndex: Int): DataFrame { } return newColumnList.toDataFrame().cast() } + +internal fun MoveClause.moveTo(columnIndex: Int, insideGroup: Boolean): DataFrame { + if (!insideGroup) + return moveTo(columnIndex) + + val columnsToMove = df.getColumns(columns) + //check if columns to move have the same parent + val columnsToMoveParents = columnsToMove.map { it.path.dropLast() } + val parentOfFirst = columnsToMoveParents.first() + if (columnsToMoveParents.any { it != parentOfFirst }) { + throw IllegalArgumentException( + "Cannot move columns with different parent to an index which is associated to one only column group", + ) + } + + //if parent has at least one son + val sons = df.getColumnGroup(parentOfFirst).columns() + //if (sons.isNotEmpty()) { + val referenceSon = if (columnIndex >= sons.size) sons.last() else sons.get(columnIndex) + if (columnIndex > sons.size) + return df.move(columns).after{referenceSon} + else + return df.move(columns).before{referenceSon} + //} +} diff --git a/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt index 955410e832..50dd179480 100644 --- a/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt +++ b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt @@ -220,4 +220,11 @@ class MoveTests { grouped.move { "a"["b"] }.before { "a"["b"] } }.message shouldBe "Cannot move column 'a/b' before its own child column 'a/b'" } + + @Test + fun `experiment with move to`() { + val df = grouped.move { "b"["d"] }.to (0, true) + df.columnNames() shouldBe listOf("q", "a", "b", "w", "e", "r") + df["b"].asColumnGroup().columnNames() shouldBe listOf("d", "c") + } } From b581a927185662ea4bd2e24b42bf4b2c23474903 Mon Sep 17 00:00:00 2001 From: Carlo Maria Proietti Date: Thu, 9 Oct 2025 14:46:01 +0200 Subject: [PATCH 02/30] average case is working but only that --- .../kotlinx/dataframe/impl/api/move.kt | 14 ++++++---- .../jetbrains/kotlinx/dataframe/api/move.kt | 26 ++++++++++++++++++- 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt index 2a1028b722..a78eebeeec 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt @@ -142,12 +142,16 @@ internal fun MoveClause.moveTo(columnIndex: Int, insideGroup: Boole } //if parent has at least one son - val sons = df.getColumnGroup(parentOfFirst).columns() - //if (sons.isNotEmpty()) { - val referenceSon = if (columnIndex >= sons.size) sons.last() else sons.get(columnIndex) - if (columnIndex > sons.size) + val sons = df[parentOfFirst].asColumnGroup().columns() + if (sons.isNotEmpty()) { + val parentPath = df[parentOfFirst].path + val sonsPaths = sons.map{parentPath + it.path} + val referenceSon = if (columnIndex >= sonsPaths.size) sonsPaths.last() else sonsPaths.get(columnIndex) + if (columnIndex >= sons.size) return df.move(columns).after{referenceSon} else return df.move(columns).before{referenceSon} - //} + } + + return df } diff --git a/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt index 50dd179480..6c5ca2fe9b 100644 --- a/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt +++ b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt @@ -222,9 +222,33 @@ class MoveTests { } @Test - fun `experiment with move to`() { + fun `move a single nested column to the start remaining inside the group`() { val df = grouped.move { "b"["d"] }.to (0, true) df.columnNames() shouldBe listOf("q", "a", "b", "w", "e", "r") df["b"].asColumnGroup().columnNames() shouldBe listOf("d", "c") } + + @Test + fun `move a single nested column to the end remaining inside the group`() { + val df = grouped.move { "b"["c"] }.to (2, true) + df.columnNames() shouldBe listOf("q", "a", "b", "w", "e", "r") + df["b"].asColumnGroup().columnNames() shouldBe listOf("d", "c") + } + + //not working area + @Test + fun `move a single nested column to the end remaining inside the group 2`() { + val df = grouped.move { "b"["c"] }.to (1, true) + df.columnNames() shouldBe listOf("q", "a", "b", "w", "e", "r") + df["b"].asColumnGroup().columnNames() shouldBe listOf("d", "c") + } + + @Test + fun `move a single nested column to the end while it is to the end yet and remaining inside the group`() { + val df = grouped.move { "b"["d"] }.to (1, true) + df.columnNames() shouldBe listOf("q", "a", "b", "w", "e", "r") + df["b"].asColumnGroup().columnNames() shouldBe listOf("c", "d") + } + } + From cebda41e548a957bd529b2a02636e3e5fc66f4e0 Mon Sep 17 00:00:00 2001 From: Carlo Maria Proietti Date: Thu, 9 Oct 2025 15:12:09 +0200 Subject: [PATCH 03/30] ready to fix case when columnIndex==colPosition --- .../org/jetbrains/kotlinx/dataframe/impl/api/move.kt | 6 +++--- .../kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt | 10 ++++++++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt index a78eebeeec..ebd39d0e16 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt @@ -141,13 +141,13 @@ internal fun MoveClause.moveTo(columnIndex: Int, insideGroup: Boole ) } - //if parent has at least one son + //if parent has at least one son and columnIndex is different to the current index of the column val sons = df[parentOfFirst].asColumnGroup().columns() if (sons.isNotEmpty()) { val parentPath = df[parentOfFirst].path val sonsPaths = sons.map{parentPath + it.path} - val referenceSon = if (columnIndex >= sonsPaths.size) sonsPaths.last() else sonsPaths.get(columnIndex) - if (columnIndex >= sons.size) + val referenceSon = if (columnIndex >= sonsPaths.size - 1) sonsPaths.last() else sonsPaths.get(columnIndex) + if (columnIndex >= sons.size - 1) return df.move(columns).after{referenceSon} else return df.move(columns).before{referenceSon} diff --git a/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt index 6c5ca2fe9b..3c5b54b842 100644 --- a/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt +++ b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt @@ -235,14 +235,14 @@ class MoveTests { df["b"].asColumnGroup().columnNames() shouldBe listOf("d", "c") } - //not working area @Test - fun `move a single nested column to the end remaining inside the group 2`() { + fun `move a single nested column to the end remaining inside the group, index is the position of current end col`() { val df = grouped.move { "b"["c"] }.to (1, true) df.columnNames() shouldBe listOf("q", "a", "b", "w", "e", "r") df["b"].asColumnGroup().columnNames() shouldBe listOf("d", "c") } + //not working area : when i want to move the col to the index where it is yet @Test fun `move a single nested column to the end while it is to the end yet and remaining inside the group`() { val df = grouped.move { "b"["d"] }.to (1, true) @@ -250,5 +250,11 @@ class MoveTests { df["b"].asColumnGroup().columnNames() shouldBe listOf("c", "d") } + @Test + fun `check move behavior`() { + val df = grouped.move ( "q" ).to (0) + df.columnNames() shouldBe listOf("q", "a", "b", "w", "e", "r") + } + } From ccf9ba2eb5f064c63ab1350681b20cb9f70c6c20 Mon Sep 17 00:00:00 2001 From: Carlo Maria Proietti Date: Thu, 9 Oct 2025 22:05:30 +0200 Subject: [PATCH 04/30] each test works --- .../jetbrains/kotlinx/dataframe/impl/api/move.kt | 14 +++++++++----- .../org/jetbrains/kotlinx/dataframe/api/move.kt | 5 ++--- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt index ebd39d0e16..e1a5965169 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt @@ -22,6 +22,7 @@ import org.jetbrains.kotlinx.dataframe.columns.UnresolvedColumnsPolicy import org.jetbrains.kotlinx.dataframe.columns.toColumnSet import org.jetbrains.kotlinx.dataframe.impl.DataFrameReceiver import org.jetbrains.kotlinx.dataframe.impl.asList +import org.jetbrains.kotlinx.dataframe.impl.columns.toColumnSet import org.jetbrains.kotlinx.dataframe.impl.columns.toColumnWithPath import org.jetbrains.kotlinx.dataframe.impl.columns.tree.ColumnPosition import org.jetbrains.kotlinx.dataframe.impl.columns.tree.getOrPut @@ -141,16 +142,19 @@ internal fun MoveClause.moveTo(columnIndex: Int, insideGroup: Boole ) } - //if parent has at least one son and columnIndex is different to the current index of the column + //if parent has at least one son val sons = df[parentOfFirst].asColumnGroup().columns() if (sons.isNotEmpty()) { val parentPath = df[parentOfFirst].path val sonsPaths = sons.map{parentPath + it.path} val referenceSon = if (columnIndex >= sonsPaths.size - 1) sonsPaths.last() else sonsPaths.get(columnIndex) - if (columnIndex >= sons.size - 1) - return df.move(columns).after{referenceSon} - else - return df.move(columns).before{referenceSon} + + //if in 'columns' there are columns equal to reference, remove them + val effectiveColumns = columnsToMove.filter { it.path.last() != referenceSon.path.last() } + if (columnIndex >= sons.size - 1 && effectiveColumns.isNotEmpty()) + return df.move{effectiveColumns.toColumnSet()}.after{referenceSon} + else if (effectiveColumns.isNotEmpty()) + return df.move{effectiveColumns.toColumnSet()}.before{referenceSon} } return df diff --git a/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt index 3c5b54b842..4333a68a06 100644 --- a/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt +++ b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt @@ -242,7 +242,6 @@ class MoveTests { df["b"].asColumnGroup().columnNames() shouldBe listOf("d", "c") } - //not working area : when i want to move the col to the index where it is yet @Test fun `move a single nested column to the end while it is to the end yet and remaining inside the group`() { val df = grouped.move { "b"["d"] }.to (1, true) @@ -252,8 +251,8 @@ class MoveTests { @Test fun `check move behavior`() { - val df = grouped.move ( "q" ).to (0) - df.columnNames() shouldBe listOf("q", "a", "b", "w", "e", "r") + val df = grouped.move ( "q", "b" ).to (0) + df.columnNames() shouldBe listOf("q", "b", "a", "w", "e", "r") } } From e5137aed14bea3d8b39198d176cd32efbf3a1d58 Mon Sep 17 00:00:00 2001 From: Carlo Maria Proietti Date: Fri, 10 Oct 2025 08:20:09 +0200 Subject: [PATCH 05/30] improving tests --- .../kotlinx/dataframe/impl/api/move.kt | 2 +- .../jetbrains/kotlinx/dataframe/api/move.kt | 22 ++++++++++++++++--- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt index e1a5965169..37de78eda6 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt @@ -138,7 +138,7 @@ internal fun MoveClause.moveTo(columnIndex: Int, insideGroup: Boole val parentOfFirst = columnsToMoveParents.first() if (columnsToMoveParents.any { it != parentOfFirst }) { throw IllegalArgumentException( - "Cannot move columns with different parent to an index which is associated to one only column group", + "Cannot move columns with different parent to an index", ) } diff --git a/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt index 4333a68a06..f7f681a9e4 100644 --- a/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt +++ b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt @@ -243,16 +243,32 @@ class MoveTests { } @Test - fun `move a single nested column to the end while it is to the end yet and remaining inside the group`() { + fun `move a single nested column to current index of the column itself`() { val df = grouped.move { "b"["d"] }.to (1, true) df.columnNames() shouldBe listOf("q", "a", "b", "w", "e", "r") df["b"].asColumnGroup().columnNames() shouldBe listOf("c", "d") } + //this is not working + @Test + fun `move multiple columns to an index`() { + val df = grouped.move ("w", "e").to (0, true) + df.columnNames() shouldBe listOf("w", "e", "q", "a", "b", "r") + df["e"].asColumnGroup().columnNames() shouldBe listOf("f") + } + + @Test + fun `should throw when moving columns of different groups`() { + shouldThrow { + grouped.move { "a"["b"] and "b"["c"] }.to (0, true) + }.message shouldBe "Cannot move columns with different parent to an index" + } + @Test fun `check move behavior`() { - val df = grouped.move ( "q", "b" ).to (0) - df.columnNames() shouldBe listOf("q", "b", "a", "w", "e", "r") + val df = grouped.move ( "q", "e" ).to (0) + df.columnNames() shouldBe listOf("q", "e", "a", "b", "w", "r") + df["e"].asColumnGroup().columnNames() shouldBe listOf("f") } } From 6fc8e98592ba66b1163084547cb5a49031bf835b Mon Sep 17 00:00:00 2001 From: Carlo Maria Proietti Date: Fri, 10 Oct 2025 17:33:59 +0200 Subject: [PATCH 06/30] each test works --- .../org/jetbrains/kotlinx/dataframe/impl/api/move.kt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt index 37de78eda6..0b8df95494 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt @@ -143,13 +143,16 @@ internal fun MoveClause.moveTo(columnIndex: Int, insideGroup: Boole } //if parent has at least one son - val sons = df[parentOfFirst].asColumnGroup().columns() + //val sons = df[parentOfFirst].asColumnGroup().columns() + if (parentOfFirst.isEmpty()) + return moveTo(columnIndex) + val sons = if (parentOfFirst.isEmpty()) df.columns() else df[parentOfFirst].asColumnGroup().columns() if (sons.isNotEmpty()) { val parentPath = df[parentOfFirst].path val sonsPaths = sons.map{parentPath + it.path} val referenceSon = if (columnIndex >= sonsPaths.size - 1) sonsPaths.last() else sonsPaths.get(columnIndex) - //if in 'columns' there are columns equal to reference, remove them + //if in 'columns' there is anyone equal to reference, remove them val effectiveColumns = columnsToMove.filter { it.path.last() != referenceSon.path.last() } if (columnIndex >= sons.size - 1 && effectiveColumns.isNotEmpty()) return df.move{effectiveColumns.toColumnSet()}.after{referenceSon} From cbf53e7140112acf4083cb6f10671a982ef2633c Mon Sep 17 00:00:00 2001 From: Carlo Maria Proietti Date: Fri, 10 Oct 2025 18:57:37 +0200 Subject: [PATCH 07/30] cleaning + logic improvement --- .../kotlinx/dataframe/impl/api/move.kt | 35 +++++++++---------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt index 0b8df95494..c3a546ddfe 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt @@ -129,10 +129,8 @@ internal fun MoveClause.moveTo(columnIndex: Int): DataFrame { } internal fun MoveClause.moveTo(columnIndex: Int, insideGroup: Boolean): DataFrame { - if (!insideGroup) - return moveTo(columnIndex) - val columnsToMove = df.getColumns(columns) + //check if columns to move have the same parent val columnsToMoveParents = columnsToMove.map { it.path.dropLast() } val parentOfFirst = columnsToMoveParents.first() @@ -142,23 +140,22 @@ internal fun MoveClause.moveTo(columnIndex: Int, insideGroup: Boole ) } - //if parent has at least one son - //val sons = df[parentOfFirst].asColumnGroup().columns() - if (parentOfFirst.isEmpty()) + //if columns will be moved to top level or columns to move are at top level + if (!insideGroup || parentOfFirst.isEmpty()) return moveTo(columnIndex) - val sons = if (parentOfFirst.isEmpty()) df.columns() else df[parentOfFirst].asColumnGroup().columns() - if (sons.isNotEmpty()) { - val parentPath = df[parentOfFirst].path - val sonsPaths = sons.map{parentPath + it.path} - val referenceSon = if (columnIndex >= sonsPaths.size - 1) sonsPaths.last() else sonsPaths.get(columnIndex) - - //if in 'columns' there is anyone equal to reference, remove them - val effectiveColumns = columnsToMove.filter { it.path.last() != referenceSon.path.last() } - if (columnIndex >= sons.size - 1 && effectiveColumns.isNotEmpty()) - return df.move{effectiveColumns.toColumnSet()}.after{referenceSon} - else if (effectiveColumns.isNotEmpty()) - return df.move{effectiveColumns.toColumnSet()}.before{referenceSon} - } + //columns are nested and will be eventually moved inside group + val parentPath = df[parentOfFirst].path + val referenceAndSiblings = df[parentOfFirst].asColumnGroup().columns() + val referenceAndSiblingsPaths = referenceAndSiblings.map{parentPath + it.path} + val reference = if (columnIndex >= referenceAndSiblingsPaths.size - 1) referenceAndSiblingsPaths.last() else referenceAndSiblingsPaths.get(columnIndex) + //if there is any column equal to reference, don't move it + val effectiveColumns = columnsToMove.filter { it.path.last() != reference.path.last() } + if (columnIndex >= referenceAndSiblings.size - 1 && effectiveColumns.isNotEmpty()) + return df.move{effectiveColumns.toColumnSet()}.after{reference} + else if (effectiveColumns.isNotEmpty()) + return df.move{effectiveColumns.toColumnSet()}.before{reference} + + //if it is not needed to move any of the nested columns return df } From 26db065001e8f1e82d787d0c87257e481b9033e9 Mon Sep 17 00:00:00 2001 From: Carlo Maria Proietti Date: Fri, 10 Oct 2025 19:14:18 +0200 Subject: [PATCH 08/30] adding test + cleaning --- .../kotlinx/dataframe/impl/api/move.kt | 33 +++++++++------ .../jetbrains/kotlinx/dataframe/api/move.kt | 42 ++++++++++++------- 2 files changed, 48 insertions(+), 27 deletions(-) diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt index c3a546ddfe..d80ee0dd4e 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt @@ -22,7 +22,6 @@ import org.jetbrains.kotlinx.dataframe.columns.UnresolvedColumnsPolicy import org.jetbrains.kotlinx.dataframe.columns.toColumnSet import org.jetbrains.kotlinx.dataframe.impl.DataFrameReceiver import org.jetbrains.kotlinx.dataframe.impl.asList -import org.jetbrains.kotlinx.dataframe.impl.columns.toColumnSet import org.jetbrains.kotlinx.dataframe.impl.columns.toColumnWithPath import org.jetbrains.kotlinx.dataframe.impl.columns.tree.ColumnPosition import org.jetbrains.kotlinx.dataframe.impl.columns.tree.getOrPut @@ -131,7 +130,7 @@ internal fun MoveClause.moveTo(columnIndex: Int): DataFrame { internal fun MoveClause.moveTo(columnIndex: Int, insideGroup: Boolean): DataFrame { val columnsToMove = df.getColumns(columns) - //check if columns to move have the same parent + // check if columns to move have the same parent val columnsToMoveParents = columnsToMove.map { it.path.dropLast() } val parentOfFirst = columnsToMoveParents.first() if (columnsToMoveParents.any { it != parentOfFirst }) { @@ -140,22 +139,30 @@ internal fun MoveClause.moveTo(columnIndex: Int, insideGroup: Boole ) } - //if columns will be moved to top level or columns to move are at top level - if (!insideGroup || parentOfFirst.isEmpty()) + // if columns will be moved to top level or columns to move are at top level + if (!insideGroup || parentOfFirst.isEmpty()) { return moveTo(columnIndex) + } - //columns are nested and will be eventually moved inside group + // columns are nested and will be eventually moved inside group val parentPath = df[parentOfFirst].path val referenceAndSiblings = df[parentOfFirst].asColumnGroup().columns() - val referenceAndSiblingsPaths = referenceAndSiblings.map{parentPath + it.path} - val reference = if (columnIndex >= referenceAndSiblingsPaths.size - 1) referenceAndSiblingsPaths.last() else referenceAndSiblingsPaths.get(columnIndex) - //if there is any column equal to reference, don't move it + val referenceAndSiblingsPaths = referenceAndSiblings.map { parentPath + it.path } + val reference = if (columnIndex >= + referenceAndSiblingsPaths.size - 1 + ) { + referenceAndSiblingsPaths.last() + } else { + referenceAndSiblingsPaths.get(columnIndex) + } + // if there is any column equal to reference, don't move it val effectiveColumns = columnsToMove.filter { it.path.last() != reference.path.last() } - if (columnIndex >= referenceAndSiblings.size - 1 && effectiveColumns.isNotEmpty()) - return df.move{effectiveColumns.toColumnSet()}.after{reference} - else if (effectiveColumns.isNotEmpty()) - return df.move{effectiveColumns.toColumnSet()}.before{reference} + if (columnIndex >= referenceAndSiblings.size - 1 && effectiveColumns.isNotEmpty()) { + return df.move { effectiveColumns.toColumnSet() }.after { reference } + } else if (effectiveColumns.isNotEmpty()) { + return df.move { effectiveColumns.toColumnSet() }.before { reference } + } - //if it is not needed to move any of the nested columns + // if it is not needed to move any of the nested columns return df } diff --git a/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt index f7f681a9e4..942121b1c4 100644 --- a/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt +++ b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt @@ -222,37 +222,53 @@ class MoveTests { } @Test - fun `move a single nested column to the start remaining inside the group`() { - val df = grouped.move { "b"["d"] }.to (0, true) + fun `move single nested column to the start remaining inside the group`() { + val df = grouped.move { "b"["d"] }.to(0, true) df.columnNames() shouldBe listOf("q", "a", "b", "w", "e", "r") df["b"].asColumnGroup().columnNames() shouldBe listOf("d", "c") } @Test - fun `move a single nested column to the end remaining inside the group`() { - val df = grouped.move { "b"["c"] }.to (2, true) + fun `move single nested column to the end remaining inside the group`() { + val df = grouped.move { "b"["c"] }.to(2, true) df.columnNames() shouldBe listOf("q", "a", "b", "w", "e", "r") df["b"].asColumnGroup().columnNames() shouldBe listOf("d", "c") } @Test - fun `move a single nested column to the end remaining inside the group, index is the position of current end col`() { - val df = grouped.move { "b"["c"] }.to (1, true) + fun `move single nested column to the end remaining inside the group, index is the position of current end col`() { + val df = grouped.move { "b"["c"] }.to(1, true) df.columnNames() shouldBe listOf("q", "a", "b", "w", "e", "r") df["b"].asColumnGroup().columnNames() shouldBe listOf("d", "c") } @Test - fun `move a single nested column to current index of the column itself`() { + fun `move single nested column to current index of the column itself`() { val df = grouped.move { "b"["d"] }.to (1, true) df.columnNames() shouldBe listOf("q", "a", "b", "w", "e", "r") df["b"].asColumnGroup().columnNames() shouldBe listOf("c", "d") } - //this is not working @Test - fun `move multiple columns to an index`() { - val df = grouped.move ("w", "e").to (0, true) + fun `move multiple nested columns to an index`() { + val groupedModified = grouped.move ("r").before { "b"["c"] } + groupedModified["b"].asColumnGroup().columnNames() shouldBe listOf("r", "c", "d") + + val df = groupedModified.move { "b"["c"] and "b"["d"] }.to(0, true) + df.columnNames() shouldBe listOf("q", "a", "b", "w", "e") + df["b"].asColumnGroup().columnNames() shouldBe listOf("c", "d", "r") + } + + @Test + fun `move single top level column to an index`() { + val df = grouped.move("w", "e").to(0, true) + df.columnNames() shouldBe listOf("w", "e", "q", "a", "b", "r") + df["e"].asColumnGroup().columnNames() shouldBe listOf("f") + } + + @Test + fun `move multiple top level columns to an index`() { + val df = grouped.move("w", "e").to(0, true) df.columnNames() shouldBe listOf("w", "e", "q", "a", "b", "r") df["e"].asColumnGroup().columnNames() shouldBe listOf("f") } @@ -260,16 +276,14 @@ class MoveTests { @Test fun `should throw when moving columns of different groups`() { shouldThrow { - grouped.move { "a"["b"] and "b"["c"] }.to (0, true) + grouped.move { "a"["b"] and "b"["c"] }.to(0, true) }.message shouldBe "Cannot move columns with different parent to an index" } @Test fun `check move behavior`() { - val df = grouped.move ( "q", "e" ).to (0) + val df = grouped.move("q", "e").to(0) df.columnNames() shouldBe listOf("q", "e", "a", "b", "w", "r") df["e"].asColumnGroup().columnNames() shouldBe listOf("f") } - } - From 2aeb956c45f49cd29d81bbaf145c4615fd0c90a5 Mon Sep 17 00:00:00 2001 From: Carlo Maria Proietti Date: Fri, 10 Oct 2025 19:24:35 +0200 Subject: [PATCH 09/30] add toStart and add docs --- .../jetbrains/kotlinx/dataframe/api/move.kt | 35 +++++++++++++++++++ .../jetbrains/kotlinx/dataframe/api/move.kt | 11 ++---- 2 files changed, 37 insertions(+), 9 deletions(-) diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt index ecb3ab51c7..0c4775fd03 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt @@ -537,6 +537,23 @@ public fun MoveClause.under( @Interpretable("MoveTo") public fun MoveClause.to(columnIndex: Int): DataFrame = moveTo(columnIndex) +/** + * Moves columns, previously selected with [move] to a new position specified + * by [columnIndex] within the columns group (remaining inside the group). + * + * Returns a new [DataFrame] with updated columns structure. + * + * For more information: {@include [DocumentationUrls.Move]} + * + * ### Examples: + * ```kotlin + * df.move { age and weight }.to(0) + * df.move("age", "weight").to(2) + * ``` + * + * @param [columnIndex] The index specifying the position in the [DataFrame] columns + * * where the selected columns will be moved. + */ @Refine @Interpretable("MoveTo") public fun MoveClause.to(columnIndex: Int, insideGroup: Boolean): DataFrame = moveTo(columnIndex, insideGroup) @@ -695,6 +712,24 @@ public fun MoveClause.toLeft(): DataFrame = to(0) @Interpretable("MoveToStart0") public fun MoveClause.toStart(): DataFrame = to(0) +/** + * Moves columns, previously selected with [move] to the start of their group (remaining inside the group). + * + * Returns a new [DataFrame] with updated columns. + * + * For more information: {@include [DocumentationUrls.Move]} + * + * ### Examples: + * ```kotlin + * df.move { age and weight }.toStart() + * df.move { colsOf() }.toStart() + * df.move("age", "weight").toStart() + * ``` + */ +@Refine +@Interpretable("MoveToStart0") +public fun MoveClause.toStart(insideGroup: Boolean): DataFrame = to(0, insideGroup) + @Deprecated(TO_RIGHT, ReplaceWith(TO_RIGHT_REPLACE), DeprecationLevel.ERROR) public fun MoveClause.toRight(): DataFrame = to(df.ncol) diff --git a/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt index 942121b1c4..401dbd65ee 100644 --- a/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt +++ b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt @@ -244,14 +244,14 @@ class MoveTests { @Test fun `move single nested column to current index of the column itself`() { - val df = grouped.move { "b"["d"] }.to (1, true) + val df = grouped.move { "b"["d"] }.to(1, true) df.columnNames() shouldBe listOf("q", "a", "b", "w", "e", "r") df["b"].asColumnGroup().columnNames() shouldBe listOf("c", "d") } @Test fun `move multiple nested columns to an index`() { - val groupedModified = grouped.move ("r").before { "b"["c"] } + val groupedModified = grouped.move("r").before { "b"["c"] } groupedModified["b"].asColumnGroup().columnNames() shouldBe listOf("r", "c", "d") val df = groupedModified.move { "b"["c"] and "b"["d"] }.to(0, true) @@ -279,11 +279,4 @@ class MoveTests { grouped.move { "a"["b"] and "b"["c"] }.to(0, true) }.message shouldBe "Cannot move columns with different parent to an index" } - - @Test - fun `check move behavior`() { - val df = grouped.move("q", "e").to(0) - df.columnNames() shouldBe listOf("q", "e", "a", "b", "w", "r") - df["e"].asColumnGroup().columnNames() shouldBe listOf("f") - } } From eed89bc2b6c4d21b08a513fd77e663dc9b308bc6 Mon Sep 17 00:00:00 2001 From: Carlo Maria Proietti Date: Sat, 11 Oct 2025 12:50:46 +0200 Subject: [PATCH 10/30] cleaning + add a new test that DONT work --- .../jetbrains/kotlinx/dataframe/api/move.kt | 6 ++-- .../kotlinx/dataframe/impl/api/move.kt | 6 ++-- .../jetbrains/kotlinx/dataframe/api/move.kt | 31 +++++++++++++------ 3 files changed, 27 insertions(+), 16 deletions(-) diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt index 0c4775fd03..5515e61e1f 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt @@ -539,7 +539,7 @@ public fun MoveClause.to(columnIndex: Int): DataFrame = moveTo(c /** * Moves columns, previously selected with [move] to a new position specified - * by [columnIndex] within the columns group (remaining inside the group). + * by [columnIndex] within the column group (remaining inside the group). * * Returns a new [DataFrame] with updated columns structure. * @@ -551,7 +551,7 @@ public fun MoveClause.to(columnIndex: Int): DataFrame = moveTo(c * df.move("age", "weight").to(2) * ``` * - * @param [columnIndex] The index specifying the position in the [DataFrame] columns + * @param [columnIndex] The index specifying the position in the [ColumnGroup] columns * * where the selected columns will be moved. */ @Refine @@ -713,7 +713,7 @@ public fun MoveClause.toLeft(): DataFrame = to(0) public fun MoveClause.toStart(): DataFrame = to(0) /** - * Moves columns, previously selected with [move] to the start of their group (remaining inside the group). + * Moves columns, previously selected with [move] to the start of their [ColumnGroup] (remaining inside the group). * * Returns a new [DataFrame] with updated columns. * diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt index d80ee0dd4e..42ef462f83 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt @@ -144,13 +144,11 @@ internal fun MoveClause.moveTo(columnIndex: Int, insideGroup: Boole return moveTo(columnIndex) } - // columns are nested and will be eventually moved inside group + // columns are nested and will be eventually moved inside their own group val parentPath = df[parentOfFirst].path val referenceAndSiblings = df[parentOfFirst].asColumnGroup().columns() val referenceAndSiblingsPaths = referenceAndSiblings.map { parentPath + it.path } - val reference = if (columnIndex >= - referenceAndSiblingsPaths.size - 1 - ) { + val reference = if (columnIndex >= referenceAndSiblingsPaths.size - 1){ referenceAndSiblingsPaths.last() } else { referenceAndSiblingsPaths.get(columnIndex) diff --git a/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt index 401dbd65ee..872894a9cb 100644 --- a/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt +++ b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt @@ -235,8 +235,20 @@ class MoveTests { df["b"].asColumnGroup().columnNames() shouldBe listOf("d", "c") } + //not working @Test - fun `move single nested column to the end remaining inside the group, index is the position of current end col`() { + fun `move single nested column between columns`() { + // creating an appropriate df for the test + val groupedModified = grouped.move("r").before { "b"["c"] } + groupedModified["b"].asColumnGroup().columnNames() shouldBe listOf("r", "c", "d") + // test itself + val df = groupedModified.move { "b"["r"] }.to(1, true) + df.columnNames() shouldBe listOf("q", "a", "b", "w", "e") + df["b"].asColumnGroup().columnNames() shouldBe listOf("c", "r", "d") + } + + @Test + fun `move single nested column to the end remaining inside the group, need to switch group's columns`() { val df = grouped.move { "b"["c"] }.to(1, true) df.columnNames() shouldBe listOf("q", "a", "b", "w", "e", "r") df["b"].asColumnGroup().columnNames() shouldBe listOf("d", "c") @@ -250,26 +262,27 @@ class MoveTests { } @Test - fun `move multiple nested columns to an index`() { + fun `move multiple nested columns to the start`() { + // creating an appropriate df for the test val groupedModified = grouped.move("r").before { "b"["c"] } groupedModified["b"].asColumnGroup().columnNames() shouldBe listOf("r", "c", "d") - + // test itself val df = groupedModified.move { "b"["c"] and "b"["d"] }.to(0, true) df.columnNames() shouldBe listOf("q", "a", "b", "w", "e") df["b"].asColumnGroup().columnNames() shouldBe listOf("c", "d", "r") } @Test - fun `move single top level column to an index`() { - val df = grouped.move("w", "e").to(0, true) - df.columnNames() shouldBe listOf("w", "e", "q", "a", "b", "r") + fun `move single top level column to the start`() { + val df = grouped.move("e").to(0, true) + df.columnNames() shouldBe listOf("e", "q", "a", "b", "w", "r") df["e"].asColumnGroup().columnNames() shouldBe listOf("f") } @Test - fun `move multiple top level columns to an index`() { - val df = grouped.move("w", "e").to(0, true) - df.columnNames() shouldBe listOf("w", "e", "q", "a", "b", "r") + fun `move multiple top level columns between columns`() { + val df = grouped.move("w", "e").to(1, true) + df.columnNames() shouldBe listOf("q", "w", "e", "a", "b", "r") df["e"].asColumnGroup().columnNames() shouldBe listOf("f") } From e212f363fc39f6bb929a0abd3e5f38da3519888a Mon Sep 17 00:00:00 2001 From: Carlo Maria Proietti Date: Sat, 11 Oct 2025 19:53:36 +0200 Subject: [PATCH 11/30] strategy of before/after reference not working --- .../kotlinx/dataframe/impl/api/move.kt | 33 +++++++++++++++---- .../jetbrains/kotlinx/dataframe/api/move.kt | 2 +- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt index 42ef462f83..9260735bf1 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt @@ -15,6 +15,7 @@ import org.jetbrains.kotlinx.dataframe.api.getColumnGroup import org.jetbrains.kotlinx.dataframe.api.getColumnWithPath import org.jetbrains.kotlinx.dataframe.api.getColumns import org.jetbrains.kotlinx.dataframe.api.move +import org.jetbrains.kotlinx.dataframe.api.toColumnAccessor import org.jetbrains.kotlinx.dataframe.api.toDataFrame import org.jetbrains.kotlinx.dataframe.columns.ColumnPath import org.jetbrains.kotlinx.dataframe.columns.ColumnWithPath @@ -25,6 +26,7 @@ import org.jetbrains.kotlinx.dataframe.impl.asList import org.jetbrains.kotlinx.dataframe.impl.columns.toColumnWithPath import org.jetbrains.kotlinx.dataframe.impl.columns.tree.ColumnPosition import org.jetbrains.kotlinx.dataframe.impl.columns.tree.getOrPut +import org.jetbrains.kotlinx.dataframe.indices import org.jetbrains.kotlinx.dataframe.path internal fun MoveClause.afterOrBefore(column: ColumnSelector, isAfter: Boolean): DataFrame { @@ -145,21 +147,38 @@ internal fun MoveClause.moveTo(columnIndex: Int, insideGroup: Boole } // columns are nested and will be eventually moved inside their own group + // finding reference column val parentPath = df[parentOfFirst].path val referenceAndSiblings = df[parentOfFirst].asColumnGroup().columns() val referenceAndSiblingsPaths = referenceAndSiblings.map { parentPath + it.path } - val reference = if (columnIndex >= referenceAndSiblingsPaths.size - 1){ + val reference = if (columnIndex >= referenceAndSiblingsPaths.size){ referenceAndSiblingsPaths.last() } else { referenceAndSiblingsPaths.get(columnIndex) } - // if there is any column equal to reference, don't move it - val effectiveColumns = columnsToMove.filter { it.path.last() != reference.path.last() } - if (columnIndex >= referenceAndSiblings.size - 1 && effectiveColumns.isNotEmpty()) { - return df.move { effectiveColumns.toColumnSet() }.after { reference } - } else if (effectiveColumns.isNotEmpty()) { - return df.move { effectiveColumns.toColumnSet() }.before { reference } + + // two cases : columns are before, or after, the reference + val columnsBeforeReference = columnsToMove.subList(0, columnIndex) + if (columnIndex >= referenceAndSiblingsPaths.size){ + val effectiveColsBeforeRef = columnsBeforeReference.filter { it.path.last() != reference.path.last() } + return df.move { effectiveColsBeforeRef.toColumnSet() }.after { reference } + } + val columnsAfterReference = columnsToMove.subList(columnIndex +1, columnsToMove.size) + + //don't move reference + val effectiveColsBeforeRef = columnsBeforeReference.filter { it.path.last() != reference.path.last() } + val effectiveColsAfterRef = columnsAfterReference.filter { it.path.last() != reference.path.last() } + + //move cols before reference after reference ; move cols after reference before reference + if (effectiveColsBeforeRef.isNotEmpty() && effectiveColsAfterRef.isNotEmpty()) { + val intermediateDf = df.move { effectiveColsBeforeRef.toColumnSet() }.after { reference } + val finalDf = intermediateDf.move { effectiveColsAfterRef.toColumnSet() }.before { reference } + return finalDf } + if (effectiveColsBeforeRef.isNotEmpty()) + return df.move { effectiveColsBeforeRef.toColumnSet() }.after { reference } + if (columnsAfterReference.isNotEmpty()) + return df.move { effectiveColsAfterRef.toColumnSet() }.before { reference } // if it is not needed to move any of the nested columns return df diff --git a/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt index 872894a9cb..16fc61bc60 100644 --- a/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt +++ b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt @@ -237,7 +237,7 @@ class MoveTests { //not working @Test - fun `move single nested column between columns`() { + fun `move single nested column between columns remaining inside the group`() { // creating an appropriate df for the test val groupedModified = grouped.move("r").before { "b"["c"] } groupedModified["b"].asColumnGroup().columnNames() shouldBe listOf("r", "c", "d") From a19d52399ba5661f9faf372ff00bd2a3931f22da Mon Sep 17 00:00:00 2001 From: Carlo Maria Proietti Date: Sun, 12 Oct 2025 09:30:33 +0200 Subject: [PATCH 12/30] each test is working! --- .../kotlinx/dataframe/impl/api/move.kt | 40 ++++++++++++------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt index 9260735bf1..3dafcb3451 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt @@ -158,27 +158,37 @@ internal fun MoveClause.moveTo(columnIndex: Int, insideGroup: Boole } // two cases : columns are before, or after, the reference - val columnsBeforeReference = columnsToMove.subList(0, columnIndex) - if (columnIndex >= referenceAndSiblingsPaths.size){ - val effectiveColsBeforeRef = columnsBeforeReference.filter { it.path.last() != reference.path.last() } - return df.move { effectiveColsBeforeRef.toColumnSet() }.after { reference } + val columnsBeforeReferenceToMove = mutableListOf() + val columnsAfterReferenceToMove = mutableListOf() + val columnsToMovePath = columnsToMove.map { it.path } + var referenceWasIterated = false + referenceAndSiblingsPaths.forEach { + if (it.path.last() == reference.path.last()) + referenceWasIterated = true + else { + if (columnsToMovePath.contains(it)){ + if (referenceWasIterated) + columnsAfterReferenceToMove.add(it) + else + columnsBeforeReferenceToMove.add(it) + } + } } - val columnsAfterReference = columnsToMove.subList(columnIndex +1, columnsToMove.size) //don't move reference - val effectiveColsBeforeRef = columnsBeforeReference.filter { it.path.last() != reference.path.last() } - val effectiveColsAfterRef = columnsAfterReference.filter { it.path.last() != reference.path.last() } + //val effectiveColsBeforeRef = columnsBeforeReference.filter { it.path.last() != reference.path.last() } + //val effectiveColsAfterRef = columnsAfterReference.filter { it.path.last() != reference.path.last() } - //move cols before reference after reference ; move cols after reference before reference - if (effectiveColsBeforeRef.isNotEmpty() && effectiveColsAfterRef.isNotEmpty()) { - val intermediateDf = df.move { effectiveColsBeforeRef.toColumnSet() }.after { reference } - val finalDf = intermediateDf.move { effectiveColsAfterRef.toColumnSet() }.before { reference } + //move cols before reference after reference itself ; move cols after reference before reference itself + if (columnsBeforeReferenceToMove.isNotEmpty() && columnsAfterReferenceToMove.isNotEmpty()) { + val intermediateDf = df.move { columnsBeforeReferenceToMove.toColumnSet() }.after { reference } + val finalDf = intermediateDf.move { columnsAfterReferenceToMove.toColumnSet() }.before { reference } return finalDf } - if (effectiveColsBeforeRef.isNotEmpty()) - return df.move { effectiveColsBeforeRef.toColumnSet() }.after { reference } - if (columnsAfterReference.isNotEmpty()) - return df.move { effectiveColsAfterRef.toColumnSet() }.before { reference } + if (columnsBeforeReferenceToMove.isNotEmpty()) + return df.move { columnsBeforeReferenceToMove.toColumnSet() }.after { reference } + if (columnsAfterReferenceToMove.isNotEmpty()) + return df.move { columnsAfterReferenceToMove.toColumnSet() }.before { reference } // if it is not needed to move any of the nested columns return df From 4627c60d3ad069d345dd57d8c42cad511600b04e Mon Sep 17 00:00:00 2001 From: Carlo Maria Proietti Date: Sun, 12 Oct 2025 10:09:35 +0200 Subject: [PATCH 13/30] new test --- .../org/jetbrains/kotlinx/dataframe/impl/api/move.kt | 11 ++++------- .../org/jetbrains/kotlinx/dataframe/api/move.kt | 11 +++++++++++ 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt index 3dafcb3451..674d470b7e 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt @@ -157,7 +157,7 @@ internal fun MoveClause.moveTo(columnIndex: Int, insideGroup: Boole referenceAndSiblingsPaths.get(columnIndex) } - // two cases : columns are before, or after, the reference + // two cases : columns to move are before, or after, the reference val columnsBeforeReferenceToMove = mutableListOf() val columnsAfterReferenceToMove = mutableListOf() val columnsToMovePath = columnsToMove.map { it.path } @@ -175,14 +175,11 @@ internal fun MoveClause.moveTo(columnIndex: Int, insideGroup: Boole } } - //don't move reference - //val effectiveColsBeforeRef = columnsBeforeReference.filter { it.path.last() != reference.path.last() } - //val effectiveColsAfterRef = columnsAfterReference.filter { it.path.last() != reference.path.last() } - - //move cols before reference after reference itself ; move cols after reference before reference itself + // move columns if (columnsBeforeReferenceToMove.isNotEmpty() && columnsAfterReferenceToMove.isNotEmpty()) { val intermediateDf = df.move { columnsBeforeReferenceToMove.toColumnSet() }.after { reference } - val finalDf = intermediateDf.move { columnsAfterReferenceToMove.toColumnSet() }.before { reference } + val newReference = columnsBeforeReferenceToMove.last() + val finalDf = intermediateDf.move { columnsAfterReferenceToMove.toColumnSet() }.after { newReference } return finalDf } if (columnsBeforeReferenceToMove.isNotEmpty()) diff --git a/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt index 16fc61bc60..f90104aa58 100644 --- a/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt +++ b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt @@ -272,6 +272,17 @@ class MoveTests { df["b"].asColumnGroup().columnNames() shouldBe listOf("c", "d", "r") } + @Test + fun `move multiple non bordering nested columns`() { + // creating an appropriate df for the test + val groupedModified = grouped.move("r" , "q").before { "b"["c"] } + groupedModified["b"].asColumnGroup().columnNames() shouldBe listOf("r", "q", "c", "d") + // test itself + val df = groupedModified.move { "b"["r"] and "b"["d"] }.to(1, true) + df.columnNames() shouldBe listOf("a", "b", "w", "e") + df["b"].asColumnGroup().columnNames() shouldBe listOf("q", "r", "d", "c") + } + @Test fun `move single top level column to the start`() { val df = grouped.move("e").to(0, true) From 8b4ca9e254d6ec4a83642cd7c34bebcb1a3d1ac1 Mon Sep 17 00:00:00 2001 From: Carlo Maria Proietti Date: Mon, 13 Oct 2025 18:28:08 +0200 Subject: [PATCH 14/30] fixing doc --- .../jetbrains/kotlinx/dataframe/api/move.kt | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt index 5515e61e1f..229ce1c60c 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt @@ -539,7 +539,8 @@ public fun MoveClause.to(columnIndex: Int): DataFrame = moveTo(c /** * Moves columns, previously selected with [move] to a new position specified - * by [columnIndex] within the column group (remaining inside the group). + * by [columnIndex]. If insideGroup is true, selected columns will be moved remaining within their [ColumnGroup], + * else they will be moved on top level. * * Returns a new [DataFrame] with updated columns structure. * @@ -547,12 +548,15 @@ public fun MoveClause.to(columnIndex: Int): DataFrame = moveTo(c * * ### Examples: * ```kotlin - * df.move { age and weight }.to(0) - * df.move("age", "weight").to(2) + * df.move { age and weight }.to(0, true) + * df.move("age", "weight").to(2, false) * ``` * * @param [columnIndex] The index specifying the position in the [ColumnGroup] columns - * * where the selected columns will be moved. + * where the selected columns will be moved. + * + * @param [insideGroup] If true, selected columns will be moved remaining inside their group, + * else they will be moved on top level. */ @Refine @Interpretable("MoveTo") @@ -713,7 +717,8 @@ public fun MoveClause.toLeft(): DataFrame = to(0) public fun MoveClause.toStart(): DataFrame = to(0) /** - * Moves columns, previously selected with [move] to the start of their [ColumnGroup] (remaining inside the group). + * If insideGroup is true, moves columns previously selected with [move] to the start of their [ColumnGroup]. + * Else, selected columns will be moved to the start of their [DataFrame] (on top-level). * * Returns a new [DataFrame] with updated columns. * @@ -721,10 +726,13 @@ public fun MoveClause.toStart(): DataFrame = to(0) * * ### Examples: * ```kotlin - * df.move { age and weight }.toStart() - * df.move { colsOf() }.toStart() - * df.move("age", "weight").toStart() + * df.move { age and weight }.toStart(true) + * df.move { colsOf() }.toStart(true) + * df.move("age", "weight").toStart(false) * ``` + * + * @param [insideGroup] If true, selected columns will be moved to the start remaining inside their group, + * else they will be moved to the start on top level. */ @Refine @Interpretable("MoveToStart0") From 98cac425468748d87bc4d3e0d417fdacfe3d16c4 Mon Sep 17 00:00:00 2001 From: Carlo Maria Proietti Date: Mon, 13 Oct 2025 18:57:55 +0200 Subject: [PATCH 15/30] change fun name + linting + improve logic --- .../jetbrains/kotlinx/dataframe/api/move.kt | 4 ++- .../kotlinx/dataframe/impl/api/move.kt | 29 ++++++++++++------- .../jetbrains/kotlinx/dataframe/api/move.kt | 4 +-- 3 files changed, 23 insertions(+), 14 deletions(-) diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt index 229ce1c60c..d7da2da47d 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt @@ -22,6 +22,7 @@ import org.jetbrains.kotlinx.dataframe.documentation.SelectingColumns import org.jetbrains.kotlinx.dataframe.impl.api.afterOrBefore import org.jetbrains.kotlinx.dataframe.impl.api.moveImpl import org.jetbrains.kotlinx.dataframe.impl.api.moveTo +import org.jetbrains.kotlinx.dataframe.impl.api.moveToImpl import org.jetbrains.kotlinx.dataframe.ncol import org.jetbrains.kotlinx.dataframe.util.DEPRECATED_ACCESS_API import org.jetbrains.kotlinx.dataframe.util.MOVE_TO_LEFT @@ -560,7 +561,8 @@ public fun MoveClause.to(columnIndex: Int): DataFrame = moveTo(c */ @Refine @Interpretable("MoveTo") -public fun MoveClause.to(columnIndex: Int, insideGroup: Boolean): DataFrame = moveTo(columnIndex, insideGroup) +public fun MoveClause.to(columnIndex: Int, insideGroup: Boolean): DataFrame = + moveToImpl(columnIndex, insideGroup) /** * Moves columns, previously selected with [move] to the top-level within the [DataFrame]. diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt index 674d470b7e..e9d69d24d1 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt @@ -129,7 +129,11 @@ internal fun MoveClause.moveTo(columnIndex: Int): DataFrame { return newColumnList.toDataFrame().cast() } -internal fun MoveClause.moveTo(columnIndex: Int, insideGroup: Boolean): DataFrame { +internal fun MoveClause.moveToImpl(columnIndex: Int, insideGroup: Boolean): DataFrame { + if (!insideGroup) { + return moveTo(columnIndex) + } + val columnsToMove = df.getColumns(columns) // check if columns to move have the same parent @@ -142,7 +146,7 @@ internal fun MoveClause.moveTo(columnIndex: Int, insideGroup: Boole } // if columns will be moved to top level or columns to move are at top level - if (!insideGroup || parentOfFirst.isEmpty()) { + if (parentOfFirst.isEmpty()) { return moveTo(columnIndex) } @@ -151,10 +155,10 @@ internal fun MoveClause.moveTo(columnIndex: Int, insideGroup: Boole val parentPath = df[parentOfFirst].path val referenceAndSiblings = df[parentOfFirst].asColumnGroup().columns() val referenceAndSiblingsPaths = referenceAndSiblings.map { parentPath + it.path } - val reference = if (columnIndex >= referenceAndSiblingsPaths.size){ + val reference = if (columnIndex >= referenceAndSiblingsPaths.size) { referenceAndSiblingsPaths.last() } else { - referenceAndSiblingsPaths.get(columnIndex) + referenceAndSiblingsPaths[columnIndex] } // two cases : columns to move are before, or after, the reference @@ -163,14 +167,15 @@ internal fun MoveClause.moveTo(columnIndex: Int, insideGroup: Boole val columnsToMovePath = columnsToMove.map { it.path } var referenceWasIterated = false referenceAndSiblingsPaths.forEach { - if (it.path.last() == reference.path.last()) + if (it.path.last() == reference.path.last()) { referenceWasIterated = true - else { - if (columnsToMovePath.contains(it)){ - if (referenceWasIterated) + } else { + if (columnsToMovePath.contains(it)) { + if (referenceWasIterated) { columnsAfterReferenceToMove.add(it) - else + } else { columnsBeforeReferenceToMove.add(it) + } } } } @@ -182,10 +187,12 @@ internal fun MoveClause.moveTo(columnIndex: Int, insideGroup: Boole val finalDf = intermediateDf.move { columnsAfterReferenceToMove.toColumnSet() }.after { newReference } return finalDf } - if (columnsBeforeReferenceToMove.isNotEmpty()) + if (columnsBeforeReferenceToMove.isNotEmpty()) { return df.move { columnsBeforeReferenceToMove.toColumnSet() }.after { reference } - if (columnsAfterReferenceToMove.isNotEmpty()) + } + if (columnsAfterReferenceToMove.isNotEmpty()) { return df.move { columnsAfterReferenceToMove.toColumnSet() }.before { reference } + } // if it is not needed to move any of the nested columns return df diff --git a/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt index f90104aa58..ddd0c70d89 100644 --- a/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt +++ b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt @@ -235,7 +235,7 @@ class MoveTests { df["b"].asColumnGroup().columnNames() shouldBe listOf("d", "c") } - //not working + // not working @Test fun `move single nested column between columns remaining inside the group`() { // creating an appropriate df for the test @@ -275,7 +275,7 @@ class MoveTests { @Test fun `move multiple non bordering nested columns`() { // creating an appropriate df for the test - val groupedModified = grouped.move("r" , "q").before { "b"["c"] } + val groupedModified = grouped.move("r", "q").before { "b"["c"] } groupedModified["b"].asColumnGroup().columnNames() shouldBe listOf("r", "q", "c", "d") // test itself val df = groupedModified.move { "b"["r"] and "b"["d"] }.to(1, true) From 3c0a96c44305e5fb5315d908a14c2a7aa2b1a20e Mon Sep 17 00:00:00 2001 From: Carlo Maria Proietti Date: Mon, 13 Oct 2025 19:27:40 +0200 Subject: [PATCH 16/30] improve syntax --- .../kotlinx/dataframe/impl/api/move.kt | 34 ++++++++++--------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt index e9d69d24d1..ea96cb47d6 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt @@ -15,7 +15,6 @@ import org.jetbrains.kotlinx.dataframe.api.getColumnGroup import org.jetbrains.kotlinx.dataframe.api.getColumnWithPath import org.jetbrains.kotlinx.dataframe.api.getColumns import org.jetbrains.kotlinx.dataframe.api.move -import org.jetbrains.kotlinx.dataframe.api.toColumnAccessor import org.jetbrains.kotlinx.dataframe.api.toDataFrame import org.jetbrains.kotlinx.dataframe.columns.ColumnPath import org.jetbrains.kotlinx.dataframe.columns.ColumnWithPath @@ -26,7 +25,6 @@ import org.jetbrains.kotlinx.dataframe.impl.asList import org.jetbrains.kotlinx.dataframe.impl.columns.toColumnWithPath import org.jetbrains.kotlinx.dataframe.impl.columns.tree.ColumnPosition import org.jetbrains.kotlinx.dataframe.impl.columns.tree.getOrPut -import org.jetbrains.kotlinx.dataframe.indices import org.jetbrains.kotlinx.dataframe.path internal fun MoveClause.afterOrBefore(column: ColumnSelector, isAfter: Boolean): DataFrame { @@ -181,19 +179,23 @@ internal fun MoveClause.moveToImpl(columnIndex: Int, insideGroup: B } // move columns - if (columnsBeforeReferenceToMove.isNotEmpty() && columnsAfterReferenceToMove.isNotEmpty()) { - val intermediateDf = df.move { columnsBeforeReferenceToMove.toColumnSet() }.after { reference } - val newReference = columnsBeforeReferenceToMove.last() - val finalDf = intermediateDf.move { columnsAfterReferenceToMove.toColumnSet() }.after { newReference } - return finalDf - } - if (columnsBeforeReferenceToMove.isNotEmpty()) { - return df.move { columnsBeforeReferenceToMove.toColumnSet() }.after { reference } - } - if (columnsAfterReferenceToMove.isNotEmpty()) { - return df.move { columnsAfterReferenceToMove.toColumnSet() }.before { reference } - } + when { + columnsBeforeReferenceToMove.isNotEmpty() && columnsAfterReferenceToMove.isNotEmpty() -> { + val intermediateDf = df.move { columnsBeforeReferenceToMove.toColumnSet() }.after { reference } + val newReference = columnsBeforeReferenceToMove.last() + val finalDf = intermediateDf.move { columnsAfterReferenceToMove.toColumnSet() }.after { newReference } + return finalDf + } - // if it is not needed to move any of the nested columns - return df + columnsBeforeReferenceToMove.isNotEmpty() -> { + return df.move { columnsBeforeReferenceToMove.toColumnSet() }.after { reference } + } + + columnsAfterReferenceToMove.isNotEmpty() -> { + return df.move { columnsAfterReferenceToMove.toColumnSet() }.before { reference } + } + + // if it is not needed to move any of the nested columns + else -> return df + } } From b07bad9d839f41ed9467ebffba708e7be07bef03 Mon Sep 17 00:00:00 2001 From: Carlo Maria Proietti Date: Mon, 13 Oct 2025 19:40:06 +0200 Subject: [PATCH 17/30] improving tests --- .../jetbrains/kotlinx/dataframe/api/move.kt | 32 ++++++++++++------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt index ddd0c70d89..04068d628d 100644 --- a/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt +++ b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt @@ -284,17 +284,27 @@ class MoveTests { } @Test - fun `move single top level column to the start`() { - val df = grouped.move("e").to(0, true) - df.columnNames() shouldBe listOf("e", "q", "a", "b", "w", "r") - df["e"].asColumnGroup().columnNames() shouldBe listOf("f") - } - - @Test - fun `move multiple top level columns between columns`() { - val df = grouped.move("w", "e").to(1, true) - df.columnNames() shouldBe listOf("q", "w", "e", "a", "b", "r") - df["e"].asColumnGroup().columnNames() shouldBe listOf("f") + fun `move single top level column to the start, insideGroup should make no difference`() { + // insideGroup is true + val dfInsideGroupIsTrue = grouped.move("e").to(0, true) + dfInsideGroupIsTrue.columnNames() shouldBe listOf("e", "q", "a", "b", "w", "r") + dfInsideGroupIsTrue["e"].asColumnGroup().columnNames() shouldBe listOf("f") + // insideGroup is false + val dfInsideGroupIsFalse = grouped.move("e").to(0, false) + dfInsideGroupIsFalse.columnNames() shouldBe listOf("e", "q", "a", "b", "w", "r") + dfInsideGroupIsFalse["e"].asColumnGroup().columnNames() shouldBe listOf("f") + } + + @Test + fun `move multiple top level columns between columns, insideGroup should make no difference`() { + // insideGroup is true + val dfInsideGroupIsTrue = grouped.move("w", "e").to(1, true) + dfInsideGroupIsTrue.columnNames() shouldBe listOf("q", "w", "e", "a", "b", "r") + dfInsideGroupIsTrue["e"].asColumnGroup().columnNames() shouldBe listOf("f") + // insideGroup is false + val dfInsideGroupIsFalse = grouped.move("w", "e").to(1, false) + dfInsideGroupIsFalse.columnNames() shouldBe listOf("q", "w", "e", "a", "b", "r") + dfInsideGroupIsFalse["e"].asColumnGroup().columnNames() shouldBe listOf("f") } @Test From 20a765252f557d0a6acf6f3d5683ad25881ebeb0 Mon Sep 17 00:00:00 2001 From: Carlo Maria Proietti Date: Tue, 14 Oct 2025 15:49:51 +0200 Subject: [PATCH 18/30] improve doc --- .../main/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt index d7da2da47d..7a29e830cd 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt @@ -540,7 +540,7 @@ public fun MoveClause.to(columnIndex: Int): DataFrame = moveTo(c /** * Moves columns, previously selected with [move] to a new position specified - * by [columnIndex]. If insideGroup is true, selected columns will be moved remaining within their [ColumnGroup], + * by [columnIndex]. If [insideGroup] is true, selected columns will be moved remaining within their [ColumnGroup], * else they will be moved on top level. * * Returns a new [DataFrame] with updated columns structure. @@ -557,7 +557,7 @@ public fun MoveClause.to(columnIndex: Int): DataFrame = moveTo(c * where the selected columns will be moved. * * @param [insideGroup] If true, selected columns will be moved remaining inside their group, - * else they will be moved on top level. + * else they will be moved to the top level. */ @Refine @Interpretable("MoveTo") From 49dd6ac81a3e41b08e77a53b5744f6f51bf9e1bb Mon Sep 17 00:00:00 2001 From: Carlo Maria Proietti Date: Tue, 14 Oct 2025 16:59:07 +0200 Subject: [PATCH 19/30] NEW APPROACH : WORK IN PROGRESS --- .../kotlinx/dataframe/impl/api/move.kt | 65 +++++-------------- 1 file changed, 16 insertions(+), 49 deletions(-) diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt index ea96cb47d6..3377843f6d 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt @@ -8,6 +8,7 @@ import org.jetbrains.kotlinx.dataframe.api.ColumnsSelectionDsl import org.jetbrains.kotlinx.dataframe.api.MoveClause import org.jetbrains.kotlinx.dataframe.api.after import org.jetbrains.kotlinx.dataframe.api.asColumnGroup +import org.jetbrains.kotlinx.dataframe.api.asDataFrame import org.jetbrains.kotlinx.dataframe.api.before import org.jetbrains.kotlinx.dataframe.api.cast import org.jetbrains.kotlinx.dataframe.api.getColumn @@ -15,16 +16,21 @@ import org.jetbrains.kotlinx.dataframe.api.getColumnGroup import org.jetbrains.kotlinx.dataframe.api.getColumnWithPath import org.jetbrains.kotlinx.dataframe.api.getColumns import org.jetbrains.kotlinx.dataframe.api.move +import org.jetbrains.kotlinx.dataframe.api.to import org.jetbrains.kotlinx.dataframe.api.toDataFrame +import org.jetbrains.kotlinx.dataframe.api.toPath import org.jetbrains.kotlinx.dataframe.columns.ColumnPath import org.jetbrains.kotlinx.dataframe.columns.ColumnWithPath import org.jetbrains.kotlinx.dataframe.columns.UnresolvedColumnsPolicy import org.jetbrains.kotlinx.dataframe.columns.toColumnSet import org.jetbrains.kotlinx.dataframe.impl.DataFrameReceiver +import org.jetbrains.kotlinx.dataframe.impl.aggregation.toColumns import org.jetbrains.kotlinx.dataframe.impl.asList +import org.jetbrains.kotlinx.dataframe.impl.columns.toColumnSet import org.jetbrains.kotlinx.dataframe.impl.columns.toColumnWithPath import org.jetbrains.kotlinx.dataframe.impl.columns.tree.ColumnPosition import org.jetbrains.kotlinx.dataframe.impl.columns.tree.getOrPut +import org.jetbrains.kotlinx.dataframe.impl.last import org.jetbrains.kotlinx.dataframe.path internal fun MoveClause.afterOrBefore(column: ColumnSelector, isAfter: Boolean): DataFrame { @@ -148,54 +154,15 @@ internal fun MoveClause.moveToImpl(columnIndex: Int, insideGroup: B return moveTo(columnIndex) } - // columns are nested and will be eventually moved inside their own group - // finding reference column + //idea: remove columns to move and brothers (sons), apply moveTo to sons, insert sons val parentPath = df[parentOfFirst].path - val referenceAndSiblings = df[parentOfFirst].asColumnGroup().columns() - val referenceAndSiblingsPaths = referenceAndSiblings.map { parentPath + it.path } - val reference = if (columnIndex >= referenceAndSiblingsPaths.size) { - referenceAndSiblingsPaths.last() - } else { - referenceAndSiblingsPaths[columnIndex] - } - - // two cases : columns to move are before, or after, the reference - val columnsBeforeReferenceToMove = mutableListOf() - val columnsAfterReferenceToMove = mutableListOf() - val columnsToMovePath = columnsToMove.map { it.path } - var referenceWasIterated = false - referenceAndSiblingsPaths.forEach { - if (it.path.last() == reference.path.last()) { - referenceWasIterated = true - } else { - if (columnsToMovePath.contains(it)) { - if (referenceWasIterated) { - columnsAfterReferenceToMove.add(it) - } else { - columnsBeforeReferenceToMove.add(it) - } - } - } - } - - // move columns - when { - columnsBeforeReferenceToMove.isNotEmpty() && columnsAfterReferenceToMove.isNotEmpty() -> { - val intermediateDf = df.move { columnsBeforeReferenceToMove.toColumnSet() }.after { reference } - val newReference = columnsBeforeReferenceToMove.last() - val finalDf = intermediateDf.move { columnsAfterReferenceToMove.toColumnSet() }.after { newReference } - return finalDf - } - - columnsBeforeReferenceToMove.isNotEmpty() -> { - return df.move { columnsBeforeReferenceToMove.toColumnSet() }.after { reference } - } - - columnsAfterReferenceToMove.isNotEmpty() -> { - return df.move { columnsAfterReferenceToMove.toColumnSet() }.before { reference } - } - - // if it is not needed to move any of the nested columns - else -> return df - } + val sons = df[parentOfFirst].asColumnGroup() + // remove columns to move and their siblings + val sonsPaths = sons.columns().map { parentPath + it.path } + val intermediateDf = df.removeImpl { sonsPaths.toColumnSet() } + // move columns and insert back + val columnsToMoveWithReducedPath = columnsToMove.map { it.path.last(it.path.size - parentPath.size).toPath() } + val columnsMoved = sons.asDataFrame().move { columnsToMoveWithReducedPath.toColumnSet() }.to(columnIndex).columns() + val columnsMovedPaths = columnsMoved.map { ColumnToInsert(parentPath + it.path, it ) } + return intermediateDf.df.insertImpl(columnsMovedPaths) } From 800ebf97253cb45a33b77c20af71e65f856ce374 Mon Sep 17 00:00:00 2001 From: Carlo Maria Proietti Date: Thu, 16 Oct 2025 09:48:29 +0200 Subject: [PATCH 20/30] new logic is implemented, each test works --- .../kotlinx/dataframe/impl/api/move.kt | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt index 3377843f6d..6ea2f83906 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt @@ -32,6 +32,7 @@ import org.jetbrains.kotlinx.dataframe.impl.columns.tree.ColumnPosition import org.jetbrains.kotlinx.dataframe.impl.columns.tree.getOrPut import org.jetbrains.kotlinx.dataframe.impl.last import org.jetbrains.kotlinx.dataframe.path +import kotlin.collections.first internal fun MoveClause.afterOrBefore(column: ColumnSelector, isAfter: Boolean): DataFrame { val removeResult = df.removeImpl(columns = columns) @@ -154,15 +155,22 @@ internal fun MoveClause.moveToImpl(columnIndex: Int, insideGroup: B return moveTo(columnIndex) } - //idea: remove columns to move and brothers (sons), apply moveTo to sons, insert sons + // logic: remove columns to move and their siblings (from this point, sons), apply them moveTo, reinsert them val parentPath = df[parentOfFirst].path val sons = df[parentOfFirst].asColumnGroup() - // remove columns to move and their siblings - val sonsPaths = sons.columns().map { parentPath + it.path } - val intermediateDf = df.removeImpl { sonsPaths.toColumnSet() } - // move columns and insert back + // remove sons + val sonsWithFullPaths = sons.columns().map { parentPath + it.path } + val intermediateDf = df.removeImpl { sonsWithFullPaths.toColumnSet() } + // move sons and reinsert them val columnsToMoveWithReducedPath = columnsToMove.map { it.path.last(it.path.size - parentPath.size).toPath() } - val columnsMoved = sons.asDataFrame().move { columnsToMoveWithReducedPath.toColumnSet() }.to(columnIndex).columns() - val columnsMovedPaths = columnsMoved.map { ColumnToInsert(parentPath + it.path, it ) } - return intermediateDf.df.insertImpl(columnsMovedPaths) + val sonsHaveBeenMoved = sons.asDataFrame().move { + columnsToMoveWithReducedPath.toColumnSet() + }.to(columnIndex).columns() + val sonsToInsert = sonsHaveBeenMoved.map { ColumnToInsert(parentPath + it.path, it) } + val secondIntermediateDf = intermediateDf.df.insertImpl(sonsToInsert) + // nested level is good but order of top level is changed -> need to fix it + val rootOfColumnsToMove = df[listOf(parentPath.first()).toPath()] + val indexOfRootOfColumnsToMove = df.columns().indexOf(rootOfColumnsToMove) + val finalDf = secondIntermediateDf.move { listOf(parentPath.first()).toPath() }.to(indexOfRootOfColumnsToMove) + return finalDf } From 1b47bfd3d60d126d2b5ffd99c370aa86dac57820 Mon Sep 17 00:00:00 2001 From: Carlo Maria Proietti Date: Thu, 16 Oct 2025 09:52:45 +0200 Subject: [PATCH 21/30] remove old comment --- core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt index 04068d628d..fafcd1bce2 100644 --- a/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt +++ b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt @@ -235,7 +235,6 @@ class MoveTests { df["b"].asColumnGroup().columnNames() shouldBe listOf("d", "c") } - // not working @Test fun `move single nested column between columns remaining inside the group`() { // creating an appropriate df for the test From b76dacb778ad737c1c28a2a962561ab1c93b2987 Mon Sep 17 00:00:00 2001 From: Carlo Maria Proietti Date: Thu, 16 Oct 2025 10:08:28 +0200 Subject: [PATCH 22/30] add shortcut --- .../jetbrains/kotlinx/dataframe/api/move.kt | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt index 7a29e830cd..0afd8693fd 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt @@ -541,7 +541,7 @@ public fun MoveClause.to(columnIndex: Int): DataFrame = moveTo(c /** * Moves columns, previously selected with [move] to a new position specified * by [columnIndex]. If [insideGroup] is true, selected columns will be moved remaining within their [ColumnGroup], - * else they will be moved on top level. + * else they will be moved to the top level. * * Returns a new [DataFrame] with updated columns structure. * @@ -720,7 +720,7 @@ public fun MoveClause.toStart(): DataFrame = to(0) /** * If insideGroup is true, moves columns previously selected with [move] to the start of their [ColumnGroup]. - * Else, selected columns will be moved to the start of their [DataFrame] (on top-level). + * Else, selected columns will be moved to the start of their [DataFrame] (to the top-level). * * Returns a new [DataFrame] with updated columns. * @@ -761,6 +761,28 @@ public fun MoveClause.toRight(): DataFrame = to(df.ncol) @Interpretable("MoveToEnd0") public fun MoveClause.toEnd(): DataFrame = to(df.ncol) +/** + * If insideGroup is true, moves columns previously selected with [move] to the end of their [ColumnGroup]. + * Else, selected columns will be moved to the end of their [DataFrame] (to the top-level). + * + * Returns a new [DataFrame] with updated columns. + * + * For more information: {@include [DocumentationUrls.Move]} + * + * ### Examples: + * ```kotlin + * df.move { age and weight }.toEnd(true) + * df.move { colsOf() }.toEnd(true) + * df.move("age", "weight").toEnd(false) + * ``` + * + * @param [insideGroup] If true, selected columns will be moved to the end remaining inside their group, + * else they will be moved to the end on top level. + */ +@Refine +@Interpretable("MoveToEnd0") +public fun MoveClause.toEnd(insideGroup: Boolean): DataFrame = to(df.ncol, insideGroup) + /** * An intermediate class used in the [move] operation. * From 906a83898b6be815bd2d8f76b47383adb37aa26e Mon Sep 17 00:00:00 2001 From: Carlo Maria Proietti Date: Thu, 16 Oct 2025 13:54:29 +0200 Subject: [PATCH 23/30] remove compiler plugin support --- .../main/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt | 2 -- .../kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt | 3 --- 2 files changed, 5 deletions(-) diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt index 0afd8693fd..507b788a4e 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt @@ -736,8 +736,6 @@ public fun MoveClause.toStart(): DataFrame = to(0) * @param [insideGroup] If true, selected columns will be moved to the start remaining inside their group, * else they will be moved to the start on top level. */ -@Refine -@Interpretable("MoveToStart0") public fun MoveClause.toStart(insideGroup: Boolean): DataFrame = to(0, insideGroup) @Deprecated(TO_RIGHT, ReplaceWith(TO_RIGHT_REPLACE), DeprecationLevel.ERROR) diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt index 6ea2f83906..61365104b6 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt @@ -9,7 +9,6 @@ import org.jetbrains.kotlinx.dataframe.api.MoveClause import org.jetbrains.kotlinx.dataframe.api.after import org.jetbrains.kotlinx.dataframe.api.asColumnGroup import org.jetbrains.kotlinx.dataframe.api.asDataFrame -import org.jetbrains.kotlinx.dataframe.api.before import org.jetbrains.kotlinx.dataframe.api.cast import org.jetbrains.kotlinx.dataframe.api.getColumn import org.jetbrains.kotlinx.dataframe.api.getColumnGroup @@ -24,9 +23,7 @@ import org.jetbrains.kotlinx.dataframe.columns.ColumnWithPath import org.jetbrains.kotlinx.dataframe.columns.UnresolvedColumnsPolicy import org.jetbrains.kotlinx.dataframe.columns.toColumnSet import org.jetbrains.kotlinx.dataframe.impl.DataFrameReceiver -import org.jetbrains.kotlinx.dataframe.impl.aggregation.toColumns import org.jetbrains.kotlinx.dataframe.impl.asList -import org.jetbrains.kotlinx.dataframe.impl.columns.toColumnSet import org.jetbrains.kotlinx.dataframe.impl.columns.toColumnWithPath import org.jetbrains.kotlinx.dataframe.impl.columns.tree.ColumnPosition import org.jetbrains.kotlinx.dataframe.impl.columns.tree.getOrPut From 004031561143dfaa09c0e48a03ee4ea7fd2822c2 Mon Sep 17 00:00:00 2001 From: Carlo Maria Proietti Date: Thu, 16 Oct 2025 19:54:57 +0200 Subject: [PATCH 24/30] new logic + new excpeption class --- .../jetbrains/kotlinx/dataframe/api/move.kt | 2 ++ .../ColumnsWithDifferentParentException.kt | 9 ++++++ .../kotlinx/dataframe/impl/api/move.kt | 29 +++++++------------ .../jetbrains/kotlinx/dataframe/api/move.kt | 2 +- 4 files changed, 22 insertions(+), 20 deletions(-) create mode 100644 core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/exceptions/ColumnsWithDifferentParentException.kt diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt index 507b788a4e..0afd8693fd 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt @@ -736,6 +736,8 @@ public fun MoveClause.toStart(): DataFrame = to(0) * @param [insideGroup] If true, selected columns will be moved to the start remaining inside their group, * else they will be moved to the start on top level. */ +@Refine +@Interpretable("MoveToStart0") public fun MoveClause.toStart(insideGroup: Boolean): DataFrame = to(0, insideGroup) @Deprecated(TO_RIGHT, ReplaceWith(TO_RIGHT_REPLACE), DeprecationLevel.ERROR) diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/exceptions/ColumnsWithDifferentParentException.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/exceptions/ColumnsWithDifferentParentException.kt new file mode 100644 index 0000000000..28a76b61dc --- /dev/null +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/exceptions/ColumnsWithDifferentParentException.kt @@ -0,0 +1,9 @@ +package org.jetbrains.kotlinx.dataframe.exceptions + +public class ColumnsWithDifferentParentException() : + IllegalArgumentException(), + DataFrameError { + + override val message: String + get() = "Cannot move columns to an index remaining inside group if they have different parent" +} diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt index 61365104b6..a656371bb6 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt @@ -15,9 +15,11 @@ import org.jetbrains.kotlinx.dataframe.api.getColumnGroup import org.jetbrains.kotlinx.dataframe.api.getColumnWithPath import org.jetbrains.kotlinx.dataframe.api.getColumns import org.jetbrains.kotlinx.dataframe.api.move +import org.jetbrains.kotlinx.dataframe.api.replace import org.jetbrains.kotlinx.dataframe.api.to import org.jetbrains.kotlinx.dataframe.api.toDataFrame import org.jetbrains.kotlinx.dataframe.api.toPath +import org.jetbrains.kotlinx.dataframe.api.with import org.jetbrains.kotlinx.dataframe.columns.ColumnPath import org.jetbrains.kotlinx.dataframe.columns.ColumnWithPath import org.jetbrains.kotlinx.dataframe.columns.UnresolvedColumnsPolicy @@ -143,7 +145,7 @@ internal fun MoveClause.moveToImpl(columnIndex: Int, insideGroup: B val parentOfFirst = columnsToMoveParents.first() if (columnsToMoveParents.any { it != parentOfFirst }) { throw IllegalArgumentException( - "Cannot move columns with different parent to an index", + "Cannot move columns to an index remaining inside group if they have different parent", ) } @@ -152,22 +154,11 @@ internal fun MoveClause.moveToImpl(columnIndex: Int, insideGroup: B return moveTo(columnIndex) } - // logic: remove columns to move and their siblings (from this point, sons), apply them moveTo, reinsert them - val parentPath = df[parentOfFirst].path - val sons = df[parentOfFirst].asColumnGroup() - // remove sons - val sonsWithFullPaths = sons.columns().map { parentPath + it.path } - val intermediateDf = df.removeImpl { sonsWithFullPaths.toColumnSet() } - // move sons and reinsert them - val columnsToMoveWithReducedPath = columnsToMove.map { it.path.last(it.path.size - parentPath.size).toPath() } - val sonsHaveBeenMoved = sons.asDataFrame().move { - columnsToMoveWithReducedPath.toColumnSet() - }.to(columnIndex).columns() - val sonsToInsert = sonsHaveBeenMoved.map { ColumnToInsert(parentPath + it.path, it) } - val secondIntermediateDf = intermediateDf.df.insertImpl(sonsToInsert) - // nested level is good but order of top level is changed -> need to fix it - val rootOfColumnsToMove = df[listOf(parentPath.first()).toPath()] - val indexOfRootOfColumnsToMove = df.columns().indexOf(rootOfColumnsToMove) - val finalDf = secondIntermediateDf.move { listOf(parentPath.first()).toPath() }.to(indexOfRootOfColumnsToMove) - return finalDf + // replace the level where columns to move are with a new one where columns are moved + val columnsToMoveNames = columnsToMove.map { it.name() } + return df.replace { parentOfFirst.asColumnGroup() }.with { + it.asDataFrame() + .move { columnsToMoveNames.toColumnSet() }.to(columnIndex) + .asColumnGroup(it.name()) + } } diff --git a/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt index fafcd1bce2..b64229d578 100644 --- a/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt +++ b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt @@ -310,6 +310,6 @@ class MoveTests { fun `should throw when moving columns of different groups`() { shouldThrow { grouped.move { "a"["b"] and "b"["c"] }.to(0, true) - }.message shouldBe "Cannot move columns with different parent to an index" + }.message shouldBe "Cannot move columns to an index remaining inside group if they have different parent" } } From dc57748bbef54fe1a36db90a6ddc7d1f0d8286cb Mon Sep 17 00:00:00 2001 From: Carlo Maria Proietti Date: Thu, 16 Oct 2025 20:13:22 +0200 Subject: [PATCH 25/30] using exception class --- .../exceptions/ColumnsWithDifferentParentException.kt | 5 ++--- .../kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/exceptions/ColumnsWithDifferentParentException.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/exceptions/ColumnsWithDifferentParentException.kt index 28a76b61dc..4e5d4e17f1 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/exceptions/ColumnsWithDifferentParentException.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/exceptions/ColumnsWithDifferentParentException.kt @@ -1,9 +1,8 @@ package org.jetbrains.kotlinx.dataframe.exceptions -public class ColumnsWithDifferentParentException() : +public class ColumnsWithDifferentParentException(message: String) : IllegalArgumentException(), DataFrameError { - override val message: String - get() = "Cannot move columns to an index remaining inside group if they have different parent" + override val message: String = message } diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt index a656371bb6..f9b6a20298 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/move.kt @@ -18,18 +18,17 @@ import org.jetbrains.kotlinx.dataframe.api.move import org.jetbrains.kotlinx.dataframe.api.replace import org.jetbrains.kotlinx.dataframe.api.to import org.jetbrains.kotlinx.dataframe.api.toDataFrame -import org.jetbrains.kotlinx.dataframe.api.toPath import org.jetbrains.kotlinx.dataframe.api.with import org.jetbrains.kotlinx.dataframe.columns.ColumnPath import org.jetbrains.kotlinx.dataframe.columns.ColumnWithPath import org.jetbrains.kotlinx.dataframe.columns.UnresolvedColumnsPolicy import org.jetbrains.kotlinx.dataframe.columns.toColumnSet +import org.jetbrains.kotlinx.dataframe.exceptions.ColumnsWithDifferentParentException import org.jetbrains.kotlinx.dataframe.impl.DataFrameReceiver import org.jetbrains.kotlinx.dataframe.impl.asList import org.jetbrains.kotlinx.dataframe.impl.columns.toColumnWithPath import org.jetbrains.kotlinx.dataframe.impl.columns.tree.ColumnPosition import org.jetbrains.kotlinx.dataframe.impl.columns.tree.getOrPut -import org.jetbrains.kotlinx.dataframe.impl.last import org.jetbrains.kotlinx.dataframe.path import kotlin.collections.first @@ -144,7 +143,7 @@ internal fun MoveClause.moveToImpl(columnIndex: Int, insideGroup: B val columnsToMoveParents = columnsToMove.map { it.path.dropLast() } val parentOfFirst = columnsToMoveParents.first() if (columnsToMoveParents.any { it != parentOfFirst }) { - throw IllegalArgumentException( + throw ColumnsWithDifferentParentException( "Cannot move columns to an index remaining inside group if they have different parent", ) } From 5bae854158f700b731290ef1911f4266907788ba Mon Sep 17 00:00:00 2001 From: Carlo Maria Proietti Date: Fri, 17 Oct 2025 08:01:52 +0200 Subject: [PATCH 26/30] add shortcuts --- .../jetbrains/kotlinx/dataframe/api/move.kt | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt index 0afd8693fd..f223bd6859 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt @@ -220,6 +220,53 @@ public fun DataFrame.moveTo(newColumnIndex: Int, vararg columns: AnyColum public fun DataFrame.moveTo(newColumnIndex: Int, vararg columns: KProperty<*>): DataFrame = moveTo(newColumnIndex) { columns.toColumnSet() } +/** + * Moves the specified [columns\] to a new position specified + * by [columnIndex]. If [insideGroup] is true selected columns + * will be moved remaining within their [ColumnGroup], + * else they will be moved to the top level. + * + * @include [CommonMoveToDocs] + * @include [SelectingColumns.Dsl] {@include [SetMoveToOperationArg]} + * ### Examples: + * ```kotlin + * df.moveTo(0, true) { length and age } + * df.moveTo(2, false) { cols(1..5) } + * ``` + * @param [newColumnIndex] The index specifying the position in the [DataFrame] columns + * where the selected columns will be moved. + * @param [insideGroup] If true, selected columns will be moved remaining inside their group, + * else they will be moved to the top level. + * @param [columns\] The [Columns Selector][ColumnsSelector] used to select the columns of this [DataFrame] to move. + */ +public fun DataFrame.moveTo( + newColumnIndex: Int, + insideGroup: Boolean, + columns: ColumnsSelector, +): DataFrame = move(columns).to(newColumnIndex, insideGroup) + +/** + * * Moves the specified [columns\] to a new position specified + * * by [columnIndex]. If [insideGroup] is true selected columns + * * will be moved remaining within their [ColumnGroup], + * * else they will be moved to the top level. + * + * @include [CommonMoveToDocs] + * @include [SelectingColumns.ColumnNames] {@include [SetMoveToOperationArg]} + * ### Examples: + * ```kotlin + * df.moveTo(0, true) { length and age } + * df.moveTo(2, false) { cols(1..5) } + * ``` + * @param [newColumnIndex] The index specifying the position in the [DataFrame] columns + * where the selected columns will be moved. + * @param [insideGroup] If true, selected columns will be moved remaining inside their group, + * else they will be moved to the top level. + * @param [columns\] The [Columns Selector][ColumnsSelector] used to select the columns of this [DataFrame] to move. + */ +public fun DataFrame.moveTo(newColumnIndex: Int, insideGroup: Boolean, vararg columns: String): DataFrame = + moveTo(newColumnIndex, insideGroup) { columns.toColumnSet() } + // endregion // region moveToStart From 0f6b8bea3d65699ef4b3ba82f25b9f4fcf2fe65b Mon Sep 17 00:00:00 2001 From: Carlo Maria Proietti Date: Fri, 17 Oct 2025 08:02:49 +0200 Subject: [PATCH 27/30] cleaning --- .../kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt index f223bd6859..16256784d9 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt @@ -246,10 +246,10 @@ public fun DataFrame.moveTo( ): DataFrame = move(columns).to(newColumnIndex, insideGroup) /** - * * Moves the specified [columns\] to a new position specified - * * by [columnIndex]. If [insideGroup] is true selected columns - * * will be moved remaining within their [ColumnGroup], - * * else they will be moved to the top level. + * Moves the specified [columns\] to a new position specified + * by [columnIndex]. If [insideGroup] is true selected columns + * will be moved remaining within their [ColumnGroup], + * else they will be moved to the top level. * * @include [CommonMoveToDocs] * @include [SelectingColumns.ColumnNames] {@include [SetMoveToOperationArg]} From a306223ce54837d3e8174ef378d80ba77e23ad5e Mon Sep 17 00:00:00 2001 From: Carlo Maria Proietti Date: Fri, 17 Oct 2025 08:16:42 +0200 Subject: [PATCH 28/30] add shortcuts --- .../jetbrains/kotlinx/dataframe/api/move.kt | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt index 16256784d9..9398c7a7a7 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt @@ -312,6 +312,17 @@ public fun DataFrame.moveToLeft(columns: ColumnsSelector): DataFram @Interpretable("MoveToStart1") public fun DataFrame.moveToStart(columns: ColumnsSelector): DataFrame = move(columns).toStart() +/** + * @include [CommonMoveToStartDocs] + * @include [SelectingColumns.Dsl.WithExample] {@include [SetMoveToStartOperationArg]} + * @param [columns\] The [Columns Selector][ColumnsSelector] used to select the columns of this [DataFrame] to move. + * @param [insideGroup] If true, selected columns will be moved to the start remaining inside their group, + * else they will be moved to the start of the top level. + */ +@Refine +@Interpretable("MoveToStart1") +public fun DataFrame.moveToStart(columns: ColumnsSelector, insideGroup: Boolean): DataFrame = move(columns).toStart(insideGroup) + @Deprecated(MOVE_TO_LEFT, ReplaceWith(MOVE_TO_LEFT_REPLACE), DeprecationLevel.ERROR) public fun DataFrame.moveToLeft(vararg columns: String): DataFrame = moveToStart { columns.toColumnSet() } @@ -322,6 +333,15 @@ public fun DataFrame.moveToLeft(vararg columns: String): DataFrame = m */ public fun DataFrame.moveToStart(vararg columns: String): DataFrame = moveToStart { columns.toColumnSet() } +/** + * @include [CommonMoveToStartDocs] + * @include [SelectingColumns.ColumnNames.WithExample] {@include [SetMoveToStartOperationArg]} + * @param [columns\] The [Columns Selector][ColumnsSelector] used to select the columns of this [DataFrame] to move. + * @param [insideGroup] If true, selected columns will be moved to the start remaining inside their group, + * else they will be moved to the start of the top level. + */ +public fun DataFrame.moveToStart(vararg columns: String, insideGroup: Boolean): DataFrame = moveToStart ({ columns.toColumnSet() }, insideGroup) + @Deprecated(MOVE_TO_LEFT, ReplaceWith(MOVE_TO_LEFT_REPLACE), DeprecationLevel.ERROR) @AccessApiOverload public fun DataFrame.moveToLeft(vararg columns: AnyColumnReference): DataFrame = @@ -387,6 +407,17 @@ public fun DataFrame.moveToRight(columns: ColumnsSelector): DataFra @Interpretable("MoveToEnd1") public fun DataFrame.moveToEnd(columns: ColumnsSelector): DataFrame = move(columns).toEnd() +/** + * @include [CommonMoveToEndDocs] + * @include [SelectingColumns.Dsl.WithExample] {@include [SetMoveToEndOperationArg]} + * @param [columns\] The [Columns Selector][ColumnsSelector] used to select the columns of this [DataFrame] to move. + * @param [insideGroup] If true, selected columns will be moved to the end remaining inside their group, + * else they will be moved to the end of the top level. + */ +@Refine +@Interpretable("MoveToEnd1") +public fun DataFrame.moveToEnd(columns: ColumnsSelector, insideGroup: Boolean): DataFrame = move(columns).toEnd(insideGroup) + @Deprecated(MOVE_TO_RIGHT, ReplaceWith(MOVE_TO_RIGHT_REPLACE), DeprecationLevel.ERROR) public fun DataFrame.moveToRight(vararg columns: String): DataFrame = moveToEnd { columns.toColumnSet() } @@ -397,6 +428,15 @@ public fun DataFrame.moveToRight(vararg columns: String): DataFrame = */ public fun DataFrame.moveToEnd(vararg columns: String): DataFrame = moveToEnd { columns.toColumnSet() } +/** + * @include [CommonMoveToEndDocs] + * @include [SelectingColumns.ColumnNames.WithExample] {@include [SetMoveToEndOperationArg]} + * @param [columns\] The [Columns Selector][ColumnsSelector] used to select the columns of this [DataFrame] to move. + * @param [insideGroup] If true, selected columns will be moved to the end remaining inside their group, + * else they will be moved to the end of the top level. + */ +public fun DataFrame.moveToEnd(vararg columns: String, insideGroup: Boolean): DataFrame = moveToEnd ({ columns.toColumnSet() }, insideGroup) + @Deprecated(MOVE_TO_RIGHT, ReplaceWith(MOVE_TO_RIGHT_REPLACE), DeprecationLevel.ERROR) @AccessApiOverload public fun DataFrame.moveToRight(vararg columns: AnyColumnReference): DataFrame = From de0ff10de3b7fce5c53c413b178a1ac54c796f52 Mon Sep 17 00:00:00 2001 From: Carlo Maria Proietti Date: Fri, 17 Oct 2025 15:44:16 +0200 Subject: [PATCH 29/30] lambda is last arg --- .../org/jetbrains/kotlinx/dataframe/api/move.kt | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt index 9398c7a7a7..2c025d4046 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt @@ -321,7 +321,8 @@ public fun DataFrame.moveToStart(columns: ColumnsSelector): DataFra */ @Refine @Interpretable("MoveToStart1") -public fun DataFrame.moveToStart(columns: ColumnsSelector, insideGroup: Boolean): DataFrame = move(columns).toStart(insideGroup) +public fun DataFrame.moveToStart(insideGroup: Boolean, columns: ColumnsSelector): DataFrame = + move(columns).toStart(insideGroup) @Deprecated(MOVE_TO_LEFT, ReplaceWith(MOVE_TO_LEFT_REPLACE), DeprecationLevel.ERROR) public fun DataFrame.moveToLeft(vararg columns: String): DataFrame = moveToStart { columns.toColumnSet() } @@ -340,7 +341,8 @@ public fun DataFrame.moveToStart(vararg columns: String): DataFrame = * @param [insideGroup] If true, selected columns will be moved to the start remaining inside their group, * else they will be moved to the start of the top level. */ -public fun DataFrame.moveToStart(vararg columns: String, insideGroup: Boolean): DataFrame = moveToStart ({ columns.toColumnSet() }, insideGroup) +public fun DataFrame.moveToStart(vararg columns: String, insideGroup: Boolean): DataFrame = + moveToStart(insideGroup) { columns.toColumnSet() } @Deprecated(MOVE_TO_LEFT, ReplaceWith(MOVE_TO_LEFT_REPLACE), DeprecationLevel.ERROR) @AccessApiOverload @@ -416,7 +418,8 @@ public fun DataFrame.moveToEnd(columns: ColumnsSelector): DataFrame */ @Refine @Interpretable("MoveToEnd1") -public fun DataFrame.moveToEnd(columns: ColumnsSelector, insideGroup: Boolean): DataFrame = move(columns).toEnd(insideGroup) +public fun DataFrame.moveToEnd(insideGroup: Boolean, columns: ColumnsSelector): DataFrame = + move(columns).toEnd(insideGroup) @Deprecated(MOVE_TO_RIGHT, ReplaceWith(MOVE_TO_RIGHT_REPLACE), DeprecationLevel.ERROR) public fun DataFrame.moveToRight(vararg columns: String): DataFrame = moveToEnd { columns.toColumnSet() } @@ -435,7 +438,8 @@ public fun DataFrame.moveToEnd(vararg columns: String): DataFrame = mo * @param [insideGroup] If true, selected columns will be moved to the end remaining inside their group, * else they will be moved to the end of the top level. */ -public fun DataFrame.moveToEnd(vararg columns: String, insideGroup: Boolean): DataFrame = moveToEnd ({ columns.toColumnSet() }, insideGroup) +public fun DataFrame.moveToEnd(vararg columns: String, insideGroup: Boolean): DataFrame = + moveToEnd(insideGroup) { columns.toColumnSet() } @Deprecated(MOVE_TO_RIGHT, ReplaceWith(MOVE_TO_RIGHT_REPLACE), DeprecationLevel.ERROR) @AccessApiOverload From 07a71d141313c07de030c07bf82fbb70cac6f8cd Mon Sep 17 00:00:00 2001 From: Carlo Maria Proietti Date: Fri, 17 Oct 2025 16:01:31 +0200 Subject: [PATCH 30/30] same for vararg string overload --- .../main/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt index 2c025d4046..c18a191dd7 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt @@ -341,7 +341,7 @@ public fun DataFrame.moveToStart(vararg columns: String): DataFrame = * @param [insideGroup] If true, selected columns will be moved to the start remaining inside their group, * else they will be moved to the start of the top level. */ -public fun DataFrame.moveToStart(vararg columns: String, insideGroup: Boolean): DataFrame = +public fun DataFrame.moveToStart(insideGroup: Boolean, vararg columns: String): DataFrame = moveToStart(insideGroup) { columns.toColumnSet() } @Deprecated(MOVE_TO_LEFT, ReplaceWith(MOVE_TO_LEFT_REPLACE), DeprecationLevel.ERROR) @@ -438,7 +438,7 @@ public fun DataFrame.moveToEnd(vararg columns: String): DataFrame = mo * @param [insideGroup] If true, selected columns will be moved to the end remaining inside their group, * else they will be moved to the end of the top level. */ -public fun DataFrame.moveToEnd(vararg columns: String, insideGroup: Boolean): DataFrame = +public fun DataFrame.moveToEnd(insideGroup: Boolean, vararg columns: String): DataFrame = moveToEnd(insideGroup) { columns.toColumnSet() } @Deprecated(MOVE_TO_RIGHT, ReplaceWith(MOVE_TO_RIGHT_REPLACE), DeprecationLevel.ERROR)