diff --git a/core/api/core.api b/core/api/core.api index 9a0e9adbe6..462f6799f6 100644 --- a/core/api/core.api +++ b/core/api/core.api @@ -3424,10 +3424,14 @@ public final class org/jetbrains/kotlinx/dataframe/api/MoveKt { public static final fun move (Lorg/jetbrains/kotlinx/dataframe/DataFrame;[Lkotlin/reflect/KProperty;)Lorg/jetbrains/kotlinx/dataframe/api/MoveClause; public static final fun move (Lorg/jetbrains/kotlinx/dataframe/DataFrame;[Lorg/jetbrains/kotlinx/dataframe/columns/ColumnReference;)Lorg/jetbrains/kotlinx/dataframe/api/MoveClause; public static final fun moveTo (Lorg/jetbrains/kotlinx/dataframe/DataFrame;ILkotlin/jvm/functions/Function2;)Lorg/jetbrains/kotlinx/dataframe/DataFrame; + public static final fun moveTo (Lorg/jetbrains/kotlinx/dataframe/DataFrame;IZLkotlin/jvm/functions/Function2;)Lorg/jetbrains/kotlinx/dataframe/DataFrame; + public static final fun moveTo (Lorg/jetbrains/kotlinx/dataframe/DataFrame;IZ[Ljava/lang/String;)Lorg/jetbrains/kotlinx/dataframe/DataFrame; public static final fun moveTo (Lorg/jetbrains/kotlinx/dataframe/DataFrame;I[Ljava/lang/String;)Lorg/jetbrains/kotlinx/dataframe/DataFrame; public static final fun moveTo (Lorg/jetbrains/kotlinx/dataframe/DataFrame;I[Lkotlin/reflect/KProperty;)Lorg/jetbrains/kotlinx/dataframe/DataFrame; public static final fun moveTo (Lorg/jetbrains/kotlinx/dataframe/DataFrame;I[Lorg/jetbrains/kotlinx/dataframe/columns/ColumnReference;)Lorg/jetbrains/kotlinx/dataframe/DataFrame; public static final fun moveToEnd (Lorg/jetbrains/kotlinx/dataframe/DataFrame;Lkotlin/jvm/functions/Function2;)Lorg/jetbrains/kotlinx/dataframe/DataFrame; + public static final fun moveToEnd (Lorg/jetbrains/kotlinx/dataframe/DataFrame;ZLkotlin/jvm/functions/Function2;)Lorg/jetbrains/kotlinx/dataframe/DataFrame; + public static final fun moveToEnd (Lorg/jetbrains/kotlinx/dataframe/DataFrame;Z[Ljava/lang/String;)Lorg/jetbrains/kotlinx/dataframe/DataFrame; public static final fun moveToEnd (Lorg/jetbrains/kotlinx/dataframe/DataFrame;[Ljava/lang/String;)Lorg/jetbrains/kotlinx/dataframe/DataFrame; public static final fun moveToEnd (Lorg/jetbrains/kotlinx/dataframe/DataFrame;[Lkotlin/reflect/KProperty;)Lorg/jetbrains/kotlinx/dataframe/DataFrame; public static final fun moveToEnd (Lorg/jetbrains/kotlinx/dataframe/DataFrame;[Lorg/jetbrains/kotlinx/dataframe/columns/ColumnReference;)Lorg/jetbrains/kotlinx/dataframe/DataFrame; @@ -3440,14 +3444,19 @@ public final class org/jetbrains/kotlinx/dataframe/api/MoveKt { public static final fun moveToRight (Lorg/jetbrains/kotlinx/dataframe/DataFrame;[Lkotlin/reflect/KProperty;)Lorg/jetbrains/kotlinx/dataframe/DataFrame; public static final fun moveToRight (Lorg/jetbrains/kotlinx/dataframe/DataFrame;[Lorg/jetbrains/kotlinx/dataframe/columns/ColumnReference;)Lorg/jetbrains/kotlinx/dataframe/DataFrame; public static final fun moveToStart (Lorg/jetbrains/kotlinx/dataframe/DataFrame;Lkotlin/jvm/functions/Function2;)Lorg/jetbrains/kotlinx/dataframe/DataFrame; + public static final fun moveToStart (Lorg/jetbrains/kotlinx/dataframe/DataFrame;ZLkotlin/jvm/functions/Function2;)Lorg/jetbrains/kotlinx/dataframe/DataFrame; + public static final fun moveToStart (Lorg/jetbrains/kotlinx/dataframe/DataFrame;Z[Ljava/lang/String;)Lorg/jetbrains/kotlinx/dataframe/DataFrame; public static final fun moveToStart (Lorg/jetbrains/kotlinx/dataframe/DataFrame;[Ljava/lang/String;)Lorg/jetbrains/kotlinx/dataframe/DataFrame; public static final fun moveToStart (Lorg/jetbrains/kotlinx/dataframe/DataFrame;[Lkotlin/reflect/KProperty;)Lorg/jetbrains/kotlinx/dataframe/DataFrame; public static final fun moveToStart (Lorg/jetbrains/kotlinx/dataframe/DataFrame;[Lorg/jetbrains/kotlinx/dataframe/columns/ColumnReference;)Lorg/jetbrains/kotlinx/dataframe/DataFrame; public static final fun to (Lorg/jetbrains/kotlinx/dataframe/api/MoveClause;I)Lorg/jetbrains/kotlinx/dataframe/DataFrame; + public static final fun to (Lorg/jetbrains/kotlinx/dataframe/api/MoveClause;IZ)Lorg/jetbrains/kotlinx/dataframe/DataFrame; public static final fun toEnd (Lorg/jetbrains/kotlinx/dataframe/api/MoveClause;)Lorg/jetbrains/kotlinx/dataframe/DataFrame; + public static final fun toEnd (Lorg/jetbrains/kotlinx/dataframe/api/MoveClause;Z)Lorg/jetbrains/kotlinx/dataframe/DataFrame; public static final fun toLeft (Lorg/jetbrains/kotlinx/dataframe/api/MoveClause;)Lorg/jetbrains/kotlinx/dataframe/DataFrame; public static final fun toRight (Lorg/jetbrains/kotlinx/dataframe/api/MoveClause;)Lorg/jetbrains/kotlinx/dataframe/DataFrame; public static final fun toStart (Lorg/jetbrains/kotlinx/dataframe/api/MoveClause;)Lorg/jetbrains/kotlinx/dataframe/DataFrame; + public static final fun toStart (Lorg/jetbrains/kotlinx/dataframe/api/MoveClause;Z)Lorg/jetbrains/kotlinx/dataframe/DataFrame; public static final fun toTop (Lorg/jetbrains/kotlinx/dataframe/api/MoveClause;Lkotlin/jvm/functions/Function2;)Lorg/jetbrains/kotlinx/dataframe/DataFrame; public static synthetic fun toTop$default (Lorg/jetbrains/kotlinx/dataframe/api/MoveClause;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lorg/jetbrains/kotlinx/dataframe/DataFrame; public static final fun under (Lorg/jetbrains/kotlinx/dataframe/api/MoveClause;Ljava/lang/String;)Lorg/jetbrains/kotlinx/dataframe/DataFrame; @@ -5591,6 +5600,11 @@ public final class org/jetbrains/kotlinx/dataframe/exceptions/ColumnTypeMismatch public final fun getColumn ()Lorg/jetbrains/kotlinx/dataframe/DataColumn; } +public final class org/jetbrains/kotlinx/dataframe/exceptions/ColumnsWithDifferentParentException : java/lang/IllegalArgumentException, org/jetbrains/kotlinx/dataframe/exceptions/DataFrameError { + public fun (Ljava/lang/String;)V + public fun getMessage ()Ljava/lang/String; +} + public abstract interface class org/jetbrains/kotlinx/dataframe/exceptions/DataFrameError { public abstract fun getMessage ()Ljava/lang/String; } 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..c50a59827f 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 @@ -219,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 [newColumnIndex]. 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 @@ -264,6 +312,18 @@ 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(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() } @@ -274,6 +334,16 @@ 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(insideGroup: Boolean, vararg columns: String): DataFrame = + moveToStart(insideGroup) { columns.toColumnSet() } + @Deprecated(MOVE_TO_LEFT, ReplaceWith(MOVE_TO_LEFT_REPLACE), DeprecationLevel.ERROR) @AccessApiOverload public fun DataFrame.moveToLeft(vararg columns: AnyColumnReference): DataFrame = @@ -339,6 +409,18 @@ 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(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() } @@ -349,6 +431,16 @@ 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(insideGroup: Boolean, vararg columns: String): DataFrame = + moveToEnd(insideGroup) { columns.toColumnSet() } + @Deprecated(MOVE_TO_RIGHT, ReplaceWith(MOVE_TO_RIGHT_REPLACE), DeprecationLevel.ERROR) @AccessApiOverload public fun DataFrame.moveToRight(vararg columns: AnyColumnReference): DataFrame = @@ -537,6 +629,32 @@ 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]. If [insideGroup] is true, selected columns will be moved remaining within their [ColumnGroup], + * else they will be moved to the top level. + * + * Returns a new [DataFrame] with updated columns structure. + * + * For more information: {@include [DocumentationUrls.Move]} + * + * ### Examples: + * ```kotlin + * 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. + * + * @param [insideGroup] If true, selected columns will be moved remaining inside their group, + * else they will be moved to the top level. + */ +@Refine +@Interpretable("MoveTo") +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]. * Moved columns name can be specified via special ColumnSelectionDsl. @@ -691,6 +809,28 @@ public fun MoveClause.toLeft(): DataFrame = to(0) @Interpretable("MoveToStart0") 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] (to the top-level). + * + * Returns a new [DataFrame] with updated columns. + * + * For more information: {@include [DocumentationUrls.Move]} + * + * ### Examples: + * ```kotlin + * 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") +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) @@ -712,6 +852,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. * 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..4e5d4e17f1 --- /dev/null +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/exceptions/ColumnsWithDifferentParentException.kt @@ -0,0 +1,8 @@ +package org.jetbrains.kotlinx.dataframe.exceptions + +public class ColumnsWithDifferentParentException(message: String) : + IllegalArgumentException(), + DataFrameError { + + 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 7d874eca3a..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 @@ -8,22 +8,29 @@ 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.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.replace +import org.jetbrains.kotlinx.dataframe.api.to import org.jetbrains.kotlinx.dataframe.api.toDataFrame +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.path +import kotlin.collections.first internal fun MoveClause.afterOrBefore(column: ColumnSelector, isAfter: Boolean): DataFrame { val removeResult = df.removeImpl(columns = columns) @@ -124,3 +131,33 @@ internal fun MoveClause.moveTo(columnIndex: Int): DataFrame { } return newColumnList.toDataFrame().cast() } + +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 + val columnsToMoveParents = columnsToMove.map { it.path.dropLast() } + val parentOfFirst = columnsToMoveParents.first() + if (columnsToMoveParents.any { it != parentOfFirst }) { + throw ColumnsWithDifferentParentException( + "Cannot move columns to an index remaining inside group if they have different parent", + ) + } + + // if columns will be moved to top level or columns to move are at top level + if (parentOfFirst.isEmpty()) { + return moveTo(columnIndex) + } + + // 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 955410e832..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 @@ -220,4 +220,96 @@ 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 `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 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 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") + // 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") + } + + @Test + 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") + } + + @Test + 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 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, 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 + 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 to an index remaining inside group if they have different parent" + } }