Skip to content

Conversation

CarloMariaProietti
Copy link
Contributor

About #1255
It comes with docs and a test case

@CarloMariaProietti CarloMariaProietti changed the title Move to(Start) by keeping inside group Move to(Start) keeping inside group Oct 11, 2025
@CarloMariaProietti CarloMariaProietti marked this pull request as draft October 11, 2025 12:57
@CarloMariaProietti CarloMariaProietti marked this pull request as ready for review October 12, 2025 08:11
@Jolanrensen Jolanrensen self-requested a review October 13, 2025 11:46
@Jolanrensen
Copy link
Collaborator

Oh, I see we also have these shortcut functions:

  • public fun <T> DataFrame<T>.moveTo(newColumnIndex: Int, columns: ColumnsSelector<T, *>)
  • public fun <T> DataFrame<T>.moveTo(newColumnIndex: Int, vararg columns: String): DataFrame<T>
  • moveToStart(), moveToEnd()

Could you do the same there as with .to()? :)

@CarloMariaProietti
Copy link
Contributor Author

Good Morning, I implemented a new logic as suggested in #1489 (comment).
Each test works, I'll proceed to add shortcut functions as requested in
#1489 (comment).

@CarloMariaProietti
Copy link
Contributor Author

about #1489 (comment) ,

Now there are both toStart() and toEnd(),

Oh, I see we also have these shortcut functions:

  • public fun <T> DataFrame<T>.moveTo(newColumnIndex: Int, columns: ColumnsSelector<T, *>)
  • public fun <T> DataFrame<T>.moveTo(newColumnIndex: Int, vararg columns: String): DataFrame<T>

.to() implements yet these shortcuts because it is possible to create a MoveClause with both ColumnsSelector<T, *>
and vararg String

* @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.
*/
public fun <T, C> MoveClause<T, C>.toStart(insideGroup: Boolean): DataFrame<T> = to(0, insideGroup)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh, please keep

@Refine
@Interpretable("MoveToStart0")

I will add compiler plugin for this variant of the function. It can use the same interpreter as toStart(), but it needs to be merged first and published as dev version so we can test it works in the compiler plugin in the Kotlin repo.

@Jolanrensen
Copy link
Collaborator

@CarloMariaProietti I'm not sure what you mean,
We can write

public fun <T> DataFrame<T>.moveTo(newColumnIndex: Int, columns: ColumnsSelector<T, *>): DataFrame<T> =
    move(columns).to(newColumnIndex)

public fun <T> DataFrame<T>.moveTo(newColumnIndex: Int, insideGroup: Boolean, columns: ColumnsSelector<T, *>): DataFrame<T> =
    move(columns).to(newColumnIndex, insideGroup)

and

public fun <T> DataFrame<T>.moveTo(newColumnIndex: Int, vararg columns: String): DataFrame<T> =
    moveTo(newColumnIndex) { columns.toColumnSet() }

public fun <T> DataFrame<T>.moveTo(newColumnIndex: Int, insideGroup: Boolean, vararg columns: String): DataFrame<T> =
    moveTo(newColumnIndex, insideGroup) { columns.toColumnSet() }

right?

If you want, we could also add these in another PR of course.

val parentOfFirst = columnsToMoveParents.first()
if (columnsToMoveParents.any { it != parentOfFirst }) {
throw IllegalArgumentException(
"Cannot move columns with different parent to an index",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please specify "insideGroup", as it would be fine to move them with insideGroup = false

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() }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if all columns here have the same parent... isn't their "reduced path" simply their name?

return moveTo(columnIndex)
}

// logic: remove columns to move and their siblings (from this point, sons), apply them moveTo, reinsert them
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's great you made it work! But I have a feeling this is way more complicated than it needs to be.

Copy link
Collaborator

@Jolanrensen Jolanrensen Oct 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My feeling was right, I managed to pass all your tests with this logic:

val columnsToMoveNames = columnsToMove.map { it.name() }
return df.replace { parentOfFirst.asColumnGroup() }.with {
    it.asDataFrame()
        .move { columnsToMoveNames.toColumnSet() }.to(columnIndex)
        .asColumnGroup(it.name())
}

Let me explain what's done here:

  • we take the column names of those we want to move. This is enough, because we already checked they have the same parent.
  • since we now only need to move columns around on the same level, this is just as easy as moveTo() on the top level. We can use replace { parent } to get access to just the parent group
  • then, treating the column group as a DataFrame, we can simply call move {}.to()
  • and convert it back to a column group with the right name

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the explanation, your solution is much more smooth. I ignored replace { } so i had to remove the level and reinsert it... If you agree, I'll paste this in the code.

val columnsToMoveParents = columnsToMove.map { it.path.dropLast() }
val parentOfFirst = columnsToMoveParents.first()
if (columnsToMoveParents.any { it != parentOfFirst }) {
throw IllegalArgumentException(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion from my colleague: Can you create your own exception that implements DataFrameError? that way we can catch it in the compiler plugin later :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

*/
@Refine
@Interpretable("MoveToStart1")
public fun <T> DataFrame<T>.moveToStart(columns: ColumnsSelector<T, *>, insideGroup: Boolean): DataFrame<T> = move(columns).toStart(insideGroup)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

make sure columns is the last argument, else the lambda outside parentheses doesn't work. For consistency, you can do the same in the vararg String overloads

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that way people can write:

df.moveToStart { cols }
// and
df.moveToStart(insideGroup = true) { cols }

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, I fixed also moveToEnd

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants