diff --git a/core/api/core.api b/core/api/core.api index 04c213434e..27fda8b80b 100644 --- a/core/api/core.api +++ b/core/api/core.api @@ -9410,6 +9410,9 @@ public abstract interface class org/jetbrains/kotlinx/dataframe/documentation/Ds public abstract interface class org/jetbrains/kotlinx/dataframe/documentation/DslGrammarTemplateColumnsSelectionDsl$DslGrammarTemplate$COLUMN_SET_PART { } +public abstract interface class org/jetbrains/kotlinx/dataframe/documentation/DslGrammarTemplateColumnsSelectionDsl$DslGrammarTemplate$ColumNameDef { +} + public abstract interface class org/jetbrains/kotlinx/dataframe/documentation/DslGrammarTemplateColumnsSelectionDsl$DslGrammarTemplate$ColumnDef { } @@ -9437,10 +9440,13 @@ public abstract interface class org/jetbrains/kotlinx/dataframe/documentation/Ds public abstract interface class org/jetbrains/kotlinx/dataframe/documentation/DslGrammarTemplateColumnsSelectionDsl$DslGrammarTemplate$ColumnKindRef { } -public abstract interface class org/jetbrains/kotlinx/dataframe/documentation/DslGrammarTemplateColumnsSelectionDsl$DslGrammarTemplate$ColumnNoAccessorDef { +public abstract interface class org/jetbrains/kotlinx/dataframe/documentation/DslGrammarTemplateColumnsSelectionDsl$DslGrammarTemplate$ColumnNameRef { +} + +public abstract interface class org/jetbrains/kotlinx/dataframe/documentation/DslGrammarTemplateColumnsSelectionDsl$DslGrammarTemplate$ColumnNoPathDef { } -public abstract interface class org/jetbrains/kotlinx/dataframe/documentation/DslGrammarTemplateColumnsSelectionDsl$DslGrammarTemplate$ColumnNoAccessorRef { +public abstract interface class org/jetbrains/kotlinx/dataframe/documentation/DslGrammarTemplateColumnsSelectionDsl$DslGrammarTemplate$ColumnNoPathRef { } public abstract interface class org/jetbrains/kotlinx/dataframe/documentation/DslGrammarTemplateColumnsSelectionDsl$DslGrammarTemplate$ColumnOrColumnSetDef { diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/ColumnsSelectionDsl.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/ColumnsSelectionDsl.kt index d0af31c05e..4d3b576ab7 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/ColumnsSelectionDsl.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/ColumnsSelectionDsl.kt @@ -128,7 +128,9 @@ public interface ColumnsSelectionDsl : // SingleColumn> * * {@include [DslGrammarTemplate.ColumnGroupDef]} * - * {@include [DslGrammarTemplate.ColumnNoAccessorDef]} + * {@include [DslGrammarTemplate.ColumNameDef]} + * + * {@include [DslGrammarTemplate.ColumnNoPathDef]} * * {@include [DslGrammarTemplate.ColumnOrColumnSetDef]} * @@ -180,7 +182,7 @@ public interface ColumnsSelectionDsl : // SingleColumn> * * `| `{@include [AllExceptColumnsSelectionDsl.Grammar.PlainDslName]}**` { `**{@include [DslGrammarTemplate.ColumnsSelectorRef]}**` \}`** * - * `| `{@include [AllExceptColumnsSelectionDsl.Grammar.PlainDslName]}**`(`**{@include [DslGrammarTemplate.ColumnRef]}**`,`**` ..`**`)`** + * `| `{@include [AllExceptColumnsSelectionDsl.Grammar.PlainDslName]}**`(`**{@include [DslGrammarTemplate.ColumnNoPathRef]}**`,`**` ..`**`)`** * * `| `{@include [DslGrammarTemplate.ColumnOrColumnSetRef]}` `{@include [AndColumnsSelectionDsl.Grammar.InfixName]}` [ `**`{`**` ] `{@include [DslGrammarTemplate.ColumnOrColumnSetRef]}` [ `**`\}`**` ] ` * @@ -307,7 +309,7 @@ public interface ColumnsSelectionDsl : // SingleColumn> * * {@include [Indent]}`| `{@include [AllExceptColumnsSelectionDsl.Grammar.ColumnGroupName]}**` { `**{@include [DslGrammarTemplate.ColumnsSelectorRef]}**` \} `** * - * {@include [Indent]}`| `{@include [AllExceptColumnsSelectionDsl.Grammar.ColumnGroupName]}**`(`**{@include [DslGrammarTemplate.ColumnNoAccessorRef]}**`,`**` ..`**`)`** + * {@include [Indent]}`| `{@include [AllExceptColumnsSelectionDsl.Grammar.ColumnGroupName]}**`(`**{@include [DslGrammarTemplate.ColumnNameRef]}**`,`**` ..`**`)`** * * {@include [Indent]}`| `{@include [AndColumnsSelectionDsl.Grammar.Name]}**` (`**`|`**`{ `**{@include [DslGrammarTemplate.ColumnOrColumnSetRef]}**` \}`**`|`**`)`** * diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/allExcept.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/allExcept.kt index 9129fa06c5..747ff8c8a9 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/allExcept.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/allExcept.kt @@ -15,11 +15,13 @@ import org.jetbrains.kotlinx.dataframe.documentation.DslGrammarTemplateColumnsSe import org.jetbrains.kotlinx.dataframe.documentation.Indent import org.jetbrains.kotlinx.dataframe.documentation.LineBreak import org.jetbrains.kotlinx.dataframe.impl.aggregation.toColumns -import org.jetbrains.kotlinx.dataframe.impl.columns.allColumnsExceptKeepingStructure import org.jetbrains.kotlinx.dataframe.impl.columns.createColumnSet +import org.jetbrains.kotlinx.dataframe.impl.columns.removeAll import org.jetbrains.kotlinx.dataframe.util.ALL_COLS_EXCEPT +import org.jetbrains.kotlinx.dataframe.util.ALL_COLS_EXCEPT_COLUMN_PATH import org.jetbrains.kotlinx.dataframe.util.ALL_COLS_REPLACE import org.jetbrains.kotlinx.dataframe.util.ALL_COLS_REPLACE_VARARG +import org.jetbrains.kotlinx.dataframe.util.ALL_EXCEPT_COLUMN_PATH import kotlin.reflect.KProperty // region ColumnsSelectionDsl @@ -45,7 +47,9 @@ public interface AllExceptColumnsSelectionDsl { * {@include [LineBreak]} * {@include [DslGrammarTemplate.ColumnDef]} * {@include [LineBreak]} - * {@include [DslGrammarTemplate.ColumnNoAccessorDef]} + * {@include [DslGrammarTemplate.ColumNameDef]} + * {@include [LineBreak]} + * {@include [DslGrammarTemplate.ColumnNoPathDef]} * {@include [LineBreak]} * {@include [DslGrammarTemplate.ColumnsResolverDef]} * } @@ -53,7 +57,7 @@ public interface AllExceptColumnsSelectionDsl { * {@set [DslGrammarTemplate.PLAIN_DSL_FUNCTIONS] * {@include [PlainDslName]}**` { `**{@include [DslGrammarTemplate.ColumnsSelectorRef]}**` \}`** * - * `| `{@include [PlainDslName]}**`(`**{@include [DslGrammarTemplate.ColumnRef]}**`,`**` ..`**`)`** + * `| `{@include [PlainDslName]}**`(`**{@include [DslGrammarTemplate.ColumnNoPathRef]}**`,`**` ..`**`)`** * } * {@set [DslGrammarTemplate.COLUMN_SET_FUNCTIONS] * {@include [Indent]}{@include [ColumnSetName]}` [`**` { `**`] `{@include [DslGrammarTemplate.ColumnsResolverRef]}` [`**` \} `**`]` @@ -65,7 +69,7 @@ public interface AllExceptColumnsSelectionDsl { * {@set [DslGrammarTemplate.COLUMN_GROUP_FUNCTIONS] * {@include [Indent]}{@include [ColumnGroupName]}**` { `**{@include [DslGrammarTemplate.ColumnsSelectorRef]}**` \} `** * - * {@include [Indent]}`| `{@include [ColumnGroupName]}**`(`**{@include [DslGrammarTemplate.ColumnNoAccessorRef]}**`,`**` ..`**`)`** + * {@include [Indent]}`| `{@include [ColumnGroupName]}**`(`**{@include [DslGrammarTemplate.ColumnNameRef]}**`,`**` ..`**`)`** * } */ public interface Grammar { @@ -99,7 +103,8 @@ public interface AllExceptColumnsSelectionDsl { * which will 'subtract' the [ColumnSet] created by `age `[and][ColumnsSelectionDsl.and]` height` from the [ColumnSet] created by [colsOf][colsOf]`<`[Int][Int]`>()`. * * {@include [LineBreak]} - * This operation can also be used to exclude columns from [Column Groups][ColumnGroup]. + * This operation can also be used to exclude columns originating from [Column Groups][ColumnGroup], although + * they need to be directly included in the [ColumnSet], like when using [`colsAtAnyDepth`][ColumnsSelectionDsl.colsAtAnyDepth]. * * For instance: * @@ -110,12 +115,11 @@ public interface AllExceptColumnsSelectionDsl { * scope. Use the {@include [ExtensionPropertiesApiLink]} to prevent scoping issues if possible. * {@include [LineBreak]} * Special case: If a column that needs to be removed appears multiple times in the [ColumnSet], it is excepted - * each time it is encountered (including inside [ColumnGroups][ColumnGroup]). You could say the receiver [ColumnSet] - * is [simplified][ColumnsSelectionDsl.simplify] before the operation is performed: + * each time it is encountered (including inside [ColumnGroups][ColumnGroup]): * * [cols][ColumnsSelectionDsl.cols]`(a, a, a.b, a.b).`[except][ColumnSet.except]`(a.b)` * - * `== `[cols][ColumnsSelectionDsl.cols]`(a).`[except][ColumnSet.except]`(a.b)` + * `== `[cols][ColumnsSelectionDsl.cols]`(a, a, a.b).`[except][ColumnSet.except]`(a.b)` * * ### In the [ColumnsSelectionDsl][ColumnsSelectionDsl] * Instead of having to write [all][ColumnsSelectionDsl.all]`() `[except][ColumnsSelectionDsl.except]` { ... }` in the DSL, @@ -123,7 +127,7 @@ public interface AllExceptColumnsSelectionDsl { * * For example: * - * `df.`[select][DataFrame.select]` { `[allExcept][ColumnsSelectionDsl.allExcept]` { userData.age `[and][ColumnsSelectionDsl.and]` height } }` + * `df.`[select][DataFrame.select]` { `[allExcept][ColumnsSelectionDsl.allExcept]` { userData `[and][ColumnsSelectionDsl.and]` height } }` * * ### On [ColumnGroups][ColumnGroup] * The variant of this function on [ColumnGroups][ColumnGroup] is a bit different as it changes the scope relative to @@ -142,7 +146,7 @@ public interface AllExceptColumnsSelectionDsl { * `df.`[select][DataFrame.select]` { myColGroup.`[allCols][ColumnsSelectionDsl.allCols]`() `[except][ColumnSet.except]` { myColGroup.colA `[and][ColumnsSelectionDsl.and]` myColGroup.colB } }` * {@include [LineBreak]} * Also note the name change, similar to [allCols][ColumnsSelectionDsl.allCols], this makes it clearer that you're selecting - * columns inside the group, 'lifting' them out. + * columns inside the group, not the group itself. * * ### Examples for this overload * {@get [EXAMPLE]} @@ -170,9 +174,9 @@ public interface AllExceptColumnsSelectionDsl { /** * @include [CommonExceptDocs] * {@set [CommonExceptDocs.EXAMPLE] - * `df.`[select][ColumnsSelectionDsl.select]` { `[colsOf][ColumnsSelectionDsl.colsOf]`<`[Number][Number]`>() `[except][ColumnSet.except]` `{@get [ARGUMENT_1]}` \}` + * `df.`[select][ColumnsSelectionDsl.select]` { `[colsOf][ColumnsSelectionDsl.colsOf]`<`[Number][Number]`>() `[except][ColumnSet.except]` {@get [ARGUMENT_1]} \}` * - * `df.`[select][ColumnsSelectionDsl.select]` { `[cols][ColumnsSelectionDsl.cols]`(name, age) `[except][ColumnSet.except]` `{@get [ARGUMENT_2]}` \}` + * `df.`[select][ColumnsSelectionDsl.select]` { `[cols][ColumnsSelectionDsl.cols]`(name, age) `[except][ColumnSet.except]` {@get [ARGUMENT_2]} \}` * } */ private interface ColumnSetInfixDocs { @@ -187,9 +191,9 @@ public interface AllExceptColumnsSelectionDsl { /** * @include [CommonExceptDocs] * {@set [CommonExceptDocs.EXAMPLE] - * `df.`[select][ColumnsSelectionDsl.select]` { `[colsOf][ColumnsSelectionDsl.colsOf]`<`[Number][Number]`>().`[except][ColumnSet.except]{@get [ARGUMENT_1]}` \}` + * `df.`[select][ColumnsSelectionDsl.select]` { `[colsOf][ColumnsSelectionDsl.colsOf]`<`[Number][Number]`>().`[except][ColumnSet.except]`{@get [ARGUMENT_1]} \}` * - * `df.`[select][ColumnsSelectionDsl.select]` { `[cols][ColumnsSelectionDsl.cols]`(name, age).`[except][ColumnSet.except]{@get [ARGUMENT_2]}` \}` + * `df.`[select][ColumnsSelectionDsl.select]` { `[cols][ColumnsSelectionDsl.cols]`(name, age).`[except][ColumnSet.except]`{@get [ARGUMENT_2]} \}` * } */ private interface ColumnSetVarargDocs { @@ -205,8 +209,8 @@ public interface AllExceptColumnsSelectionDsl { * @include [ColumnSetInfixDocs] * @set [CommonExceptDocs.PARAM] @param [selector\] A lambda in which you specify the columns that need to be * excluded from the [ColumnSet]. The scope of the selector is the same as the outer scope. - * @set [ColumnSetInfixDocs.ARGUMENT_1] `{ "age" `[and][ColumnsSelectionDsl.and]` height }` - * @set [ColumnSetInfixDocs.ARGUMENT_2] `{ name.firstName }` + * @set [ColumnSetInfixDocs.ARGUMENT_1] { "age" `[and][ColumnsSelectionDsl.and]` height } + * @set [ColumnSetInfixDocs.ARGUMENT_2] { name.firstName } */ public infix fun ColumnSet.except(selector: () -> ColumnsResolver<*>): ColumnSet = except(selector()) @@ -214,8 +218,8 @@ public interface AllExceptColumnsSelectionDsl { * @include [ColumnSetInfixDocs] * @set [CommonExceptDocs.PARAM] @param [other\] A [ColumnsResolver] containing the columns that need to be * excluded from the [ColumnSet]. - * @set [ColumnSetInfixDocs.ARGUMENT_1] `"age" `[and][ColumnsSelectionDsl.and]` height` - * @set [ColumnSetInfixDocs.ARGUMENT_2] `name.firstName` + * @set [ColumnSetInfixDocs.ARGUMENT_1] "age" `[and][ColumnsSelectionDsl.and]` height + * @set [ColumnSetInfixDocs.ARGUMENT_2] name.firstName */ public infix fun ColumnSet.except(other: ColumnsResolver<*>): ColumnSet = exceptInternal(other) @@ -223,8 +227,8 @@ public interface AllExceptColumnsSelectionDsl { * @include [ColumnSetVarargDocs] * @set [CommonExceptDocs.PARAM] @param [others\] Any number of [ColumnsResolvers][ColumnsResolver] containing * the columns that need to be excluded from the [ColumnSet]. - * @set [ColumnSetVarargDocs.ARGUMENT_1] `(age, userData.height)` - * @set [ColumnSetVarargDocs.ARGUMENT_2] `(name.firstName, name.middleName)` + * @set [ColumnSetVarargDocs.ARGUMENT_1] (age, userData.height) + * @set [ColumnSetVarargDocs.ARGUMENT_2] (name.firstName, name.middleName) */ public fun ColumnSet.except(vararg others: ColumnsResolver<*>): ColumnSet = except(others.toColumnSet()) @@ -232,8 +236,8 @@ public interface AllExceptColumnsSelectionDsl { * @include [ColumnSetInfixDocs] * @set [CommonExceptDocs.PARAM] @param [other\] A [String] referring to * the column (relative to the current scope) that needs to be excluded from the [ColumnSet]. - * @set [ColumnSetInfixDocs.ARGUMENT_1] `"age"` - * @set [ColumnSetInfixDocs.ARGUMENT_2] `"name"` + * @set [ColumnSetInfixDocs.ARGUMENT_1] "age" + * @set [ColumnSetInfixDocs.ARGUMENT_2] "name" */ public infix fun ColumnSet.except(other: String): ColumnSet = except(column(other)) @@ -241,8 +245,8 @@ public interface AllExceptColumnsSelectionDsl { * @include [ColumnSetVarargDocs] * @set [CommonExceptDocs.PARAM] @param [others\] Any number of [Strings][String] referring to * the columns (relative to the current scope) that need to be excluded from the [ColumnSet]. - * @set [ColumnSetVarargDocs.ARGUMENT_1] `("age", "height")` - * @set [ColumnSetVarargDocs.ARGUMENT_2] `("name")` + * @set [ColumnSetVarargDocs.ARGUMENT_1] ("age", "height") + * @set [ColumnSetVarargDocs.ARGUMENT_2] ("name") */ public fun ColumnSet.except(vararg others: String): ColumnSet = except(others.toColumnSet()) @@ -250,8 +254,8 @@ public interface AllExceptColumnsSelectionDsl { * @include [ColumnSetInfixDocs] * @set [CommonExceptDocs.PARAM] @param [other\] A [KProperty] referring to * the column (relative to the current scope) that needs to be excluded from the [ColumnSet]. - * @set [ColumnSetInfixDocs.ARGUMENT_1] `Person::age` - * @set [ColumnSetInfixDocs.ARGUMENT_2] `Person::name` + * @set [ColumnSetInfixDocs.ARGUMENT_1] Person::age + * @set [ColumnSetInfixDocs.ARGUMENT_2] Person::name */ public infix fun ColumnSet.except(other: KProperty): ColumnSet = except(column(other)) @@ -259,8 +263,8 @@ public interface AllExceptColumnsSelectionDsl { * @include [ColumnSetVarargDocs] * @set [CommonExceptDocs.PARAM] @param [others\] Any number of [KProperties][KProperty] referring to * the columns (relative to the current scope) that need to be excluded from the [ColumnSet]. - * @set [ColumnSetVarargDocs.ARGUMENT_1] `(Person::age, Person::height)` - * @set [ColumnSetVarargDocs.ARGUMENT_2] `(Person::name)` + * @set [ColumnSetVarargDocs.ARGUMENT_1] (Person::age, Person::height) + * @set [ColumnSetVarargDocs.ARGUMENT_2] (Person::name) */ public fun ColumnSet.except(vararg others: KProperty): ColumnSet = except(others.toColumnSet()) @@ -268,8 +272,8 @@ public interface AllExceptColumnsSelectionDsl { * @include [ColumnSetInfixDocs] * @set [CommonExceptDocs.PARAM] @param [other\] A [ColumnPath] referring to * the column (relative to the current scope) that needs to be excluded from the [ColumnSet]. - * @set [ColumnSetInfixDocs.ARGUMENT_1] `"userdata"["age"]` - * @set [ColumnSetInfixDocs.ARGUMENT_2] `pathOf("name", "firstName")` + * @set [ColumnSetInfixDocs.ARGUMENT_1] "userdata"["age"] + * @set [ColumnSetInfixDocs.ARGUMENT_2] pathOf("name", "firstName") */ public infix fun ColumnSet.except(other: ColumnPath): ColumnSet = except(column(other)) @@ -277,8 +281,8 @@ public interface AllExceptColumnsSelectionDsl { * @include [ColumnSetVarargDocs] * @set [CommonExceptDocs.PARAM] @param [others\] Any number of [ColumnPaths][ColumnPath] referring to * the columns (relative to the current scope) that need to be excluded from the [ColumnSet]. - * @set [ColumnSetVarargDocs.ARGUMENT_1] `(pathOf("age"), "userdata"["height"])` - * @set [ColumnSetVarargDocs.ARGUMENT_2] `("name"["firstName"], "name"["middleName"])` + * @set [ColumnSetVarargDocs.ARGUMENT_1] (pathOf("age"), "userdata"["height"]) + * @set [ColumnSetVarargDocs.ARGUMENT_2] ("name"["firstName"], "name"["middleName"]) */ public fun ColumnSet.except(vararg others: ColumnPath): ColumnSet = except(others.toColumnSet()) @@ -289,9 +293,9 @@ public interface AllExceptColumnsSelectionDsl { /** * @include [CommonExceptDocs] * @set [CommonExceptDocs.EXAMPLE] - * `df.`[select][ColumnsSelectionDsl.select]` { `[allExcept][ColumnsSelectionDsl.allExcept]{@get [ARGUMENT_1]}` \}` + * `df.`[select][ColumnsSelectionDsl.select]` { `[allExcept][ColumnsSelectionDsl.allExcept]`{@get [ARGUMENT_1]} \}` * - * `df.`[select][ColumnsSelectionDsl.select]` { `[allExcept][ColumnsSelectionDsl.allExcept]{@get [ARGUMENT_2]}` \}` + * `df.`[select][ColumnsSelectionDsl.select]` { `[allExcept][ColumnsSelectionDsl.allExcept]`{@get [ARGUMENT_2]} \}` */ private interface ColumnsSelectionDslDocs { @@ -306,8 +310,8 @@ public interface AllExceptColumnsSelectionDsl { * @include [ColumnsSelectionDslDocs] * @set [CommonExceptDocs.PARAM] @param [selector\] A lambda in which you specify the columns that need to be * excluded from the current selection. The scope of the selector is the same as the outer scope. - * @set [ColumnsSelectionDslDocs.ARGUMENT_1] ` { "age" `[and][ColumnsSelectionDsl.and]` height }` - * @set [ColumnsSelectionDslDocs.ARGUMENT_2] ` { name.firstName }` + * @set [ColumnsSelectionDslDocs.ARGUMENT_1] \ { "age" `[and][ColumnsSelectionDsl.and]` height } + * @set [ColumnsSelectionDslDocs.ARGUMENT_2] \ { name.firstName } */ public fun ColumnsSelectionDsl.allExcept(selector: ColumnsSelector): ColumnSet<*> = this.asSingleColumn().allColsExcept(selector) @@ -317,8 +321,8 @@ public interface AllExceptColumnsSelectionDsl { * @include [ColumnsSelectionDslDocs] * @set [CommonExceptDocs.PARAM] @param [others\] A [ColumnsResolver] containing the columns that need to be * excluded from the current selection. - * @set [ColumnsSelectionDslDocs.ARGUMENT_1] `(age, height)` - * @set [ColumnsSelectionDslDocs.ARGUMENT_2] `(name.firstName, name.middleName)` + * @set [ColumnsSelectionDslDocs.ARGUMENT_1] (age, height) + * @set [ColumnsSelectionDslDocs.ARGUMENT_2] (name.firstName, name.middleName) */ public fun ColumnsSelectionDsl<*>.allExcept(vararg others: ColumnsResolver<*>): ColumnSet<*> = asSingleColumn().allColsExceptInternal(others.toColumnSet()) @@ -327,8 +331,8 @@ public interface AllExceptColumnsSelectionDsl { * @include [ColumnsSelectionDslDocs] * @set [CommonExceptDocs.PARAM] @param [others\] Any number of [Strings][String] referring to * the columns (relative to the current scope) that need to be excluded from the current selection. - * @set [ColumnsSelectionDslDocs.ARGUMENT_1] `("age", "height")` - * @set [ColumnsSelectionDslDocs.ARGUMENT_2] `("name")` + * @set [ColumnsSelectionDslDocs.ARGUMENT_1] ("age", "height") + * @set [ColumnsSelectionDslDocs.ARGUMENT_2] ("name") */ public fun ColumnsSelectionDsl<*>.allExcept(vararg others: String): ColumnSet<*> = asSingleColumn().allColsExceptInternal(others.toColumnSet()) @@ -337,19 +341,13 @@ public interface AllExceptColumnsSelectionDsl { * @include [ColumnsSelectionDslDocs] * @set [CommonExceptDocs.PARAM] @param [others\] Any number of [KProperties][KProperty] referring to * the columns (relative to the current scope) that need to be excluded from the current selection. - * @set [ColumnsSelectionDslDocs.ARGUMENT_1] `(Person::age, Person::height)` - * @set [ColumnsSelectionDslDocs.ARGUMENT_2] `(Person::name)` + * @set [ColumnsSelectionDslDocs.ARGUMENT_1] (Person::age, Person::height) + * @set [ColumnsSelectionDslDocs.ARGUMENT_2] (Person::name) */ public fun ColumnsSelectionDsl<*>.allExcept(vararg others: KProperty<*>): ColumnSet<*> = asSingleColumn().allColsExceptInternal(others.toColumnSet()) - /** - * @include [ColumnsSelectionDslDocs] - * @set [CommonExceptDocs.PARAM] @param [others\] Any number of [ColumnPaths][ColumnPath] referring to - * the columns (relative to the current scope) that need to be excluded from the current selection. - * @set [ColumnsSelectionDslDocs.ARGUMENT_1] `(pathOf("age"), "userdata"["height"])` - * @set [ColumnsSelectionDslDocs.ARGUMENT_2] `("name"["firstName"], "name"["middleName"])` - */ + @Deprecated(message = ALL_EXCEPT_COLUMN_PATH, level = DeprecationLevel.ERROR) public fun ColumnsSelectionDsl<*>.allExcept(vararg others: ColumnPath): ColumnSet<*> = asSingleColumn().allColsExceptInternal(others.toColumnSet()) @@ -436,15 +434,6 @@ public interface AllExceptColumnsSelectionDsl { * @set [ColumnGroupDocs.ARGUMENT_2] `(Person::firstName, Person::middleName)` */ interface KPropertyArgs - - /** - * @set [CommonExceptDocs.PARAM] @param [others\] Any number of [ColumnPaths][ColumnPath] referring to - * the columns (relative to the column group) that need to be excluded from the current selection in [this\] - * column group. The other columns will be included in the selection by default. - * @set [ColumnGroupDocs.ARGUMENT_1] `(pathOf("age"), "extraData"["item1"])` - * @set [ColumnGroupDocs.ARGUMENT_2] `(pathOf("firstName"), "middleNames"["first"])` - */ - interface ColumnPathArgs } /** @@ -487,11 +476,7 @@ public interface AllExceptColumnsSelectionDsl { public fun SingleColumn>.allColsExcept(vararg others: KProperty<*>): ColumnSet<*> = allColsExceptInternal(others.toColumnSet()) - /** - * @include [ColumnGroupDocs] - * @include [ColumnGroupDocs.SingleColumnReceiverArgs] - * @include [ColumnGroupDocs.ColumnPathArgs] - */ + @Deprecated(message = ALL_COLS_EXCEPT_COLUMN_PATH, level = DeprecationLevel.ERROR) public fun SingleColumn>.allColsExcept(vararg other: ColumnPath): ColumnSet<*> = allColsExceptInternal(other.toColumnSet()) @@ -539,11 +524,7 @@ public interface AllExceptColumnsSelectionDsl { public fun String.allColsExcept(vararg others: KProperty<*>): ColumnSet<*> = columnGroup(this).allColsExceptInternal(others.toColumnSet()) - /** - * @include [ColumnGroupDocs] - * @include [ColumnGroupDocs.StringReceiverArgs] - * @include [ColumnGroupDocs.ColumnPathArgs] - */ + @Deprecated(message = ALL_COLS_EXCEPT_COLUMN_PATH, level = DeprecationLevel.ERROR) public fun String.allColsExcept(vararg others: ColumnPath): ColumnSet<*> = columnGroup(this).allColsExceptInternal(others.toColumnSet()) @@ -591,11 +572,7 @@ public interface AllExceptColumnsSelectionDsl { public fun KProperty<*>.allColsExcept(vararg others: KProperty<*>): ColumnSet<*> = columnGroup(this).allColsExceptInternal(others.toColumnSet()) - /** - * @include [ColumnGroupDocs] - * @include [ColumnGroupDocs.KPropertyReceiverArgs] - * @include [ColumnGroupDocs.ColumnPathArgs] - */ + @Deprecated(message = ALL_COLS_EXCEPT_COLUMN_PATH, level = DeprecationLevel.ERROR) public fun KProperty<*>.allColsExcept(vararg others: ColumnPath): ColumnSet<*> = columnGroup(this).allColsExceptInternal(others.toColumnSet()) @@ -643,11 +620,7 @@ public interface AllExceptColumnsSelectionDsl { public fun ColumnPath.allColsExcept(vararg others: KProperty<*>): ColumnSet<*> = columnGroup(this).allColsExceptInternal(others.toColumnSet()) - /** - * @include [ColumnGroupDocs] - * @include [ColumnGroupDocs.ColumnPathReceiverArgs] - * @include [ColumnGroupDocs.ColumnPathArgs] - */ + @Deprecated(message = ALL_COLS_EXCEPT_COLUMN_PATH, level = DeprecationLevel.ERROR) public fun ColumnPath.allColsExcept(vararg others: ColumnPath): ColumnSet<*> = columnGroup(this).allColsExceptInternal(others.toColumnSet()) @@ -655,7 +628,7 @@ public interface AllExceptColumnsSelectionDsl { } /** - * Removes the columns in the "other" ColumnsResolver from the current ColumnSet while keeping the structure intact. + * Removes the columns in the "other" ColumnsResolver from the current ColumnSet. * Returns a new ColumnSet with the remaining columns. * * @param other The ColumnsResolver containing the columns to be removed. @@ -666,7 +639,7 @@ internal fun ColumnSet.exceptInternal(other: ColumnsResolver<*>): ColumnS createColumnSet { context -> val resolvedCols = this.resolve(context) val resolvedColsToExcept = other.resolve(context) - resolvedCols.allColumnsExceptKeepingStructure(resolvedColsToExcept) + resolvedCols.removeAll(resolvedColsToExcept) } as ColumnSet /** diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/groupBy.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/groupBy.kt index eb5be20565..55e4c15f19 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/groupBy.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/groupBy.kt @@ -61,7 +61,9 @@ public fun Pivot.groupBy(vararg columns: KProperty<*>): PivotGroupBy = public fun Pivot.groupByOther(): PivotGroupBy { val impl = this as PivotImpl val pivotColumns = df.getPivotColumnPaths(columns).toColumnSet() - return impl.toGroupedPivot(moveToTop = false) { allExcept(pivotColumns) } + return impl.toGroupedPivot(moveToTop = false) { + (this as DataFrame).remove { pivotColumns }.columns().toColumnSet() + } } // endregion diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/columns/ColumnSet.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/columns/ColumnSet.kt index 046b9c2bd4..88e65f429b 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/columns/ColumnSet.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/columns/ColumnSet.kt @@ -9,6 +9,7 @@ import org.jetbrains.kotlinx.dataframe.api.asSingleColumn * ## ColumnSet * * Entity that can be resolved into a list of [columns][DataColumn]. + * Unlike an actual "set", repeated columns are allowed. * Just like [SingleColumn], this is a [ColumnsResolver]. * * @see [SingleColumn] diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/DslGrammarTemplateColumnsSelectionDsl.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/DslGrammarTemplateColumnsSelectionDsl.kt index fec386d1a6..4d88541ff6 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/DslGrammarTemplateColumnsSelectionDsl.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/DslGrammarTemplateColumnsSelectionDsl.kt @@ -115,8 +115,11 @@ public interface DslGrammarTemplateColumnsSelectionDsl { */ public interface ColumnGroupDef - /** `columnNoAccessor: `[`String`][String]` | `[`KProperty`][kotlin.reflect.KProperty]`<*> | `[`ColumnPath`][org.jetbrains.kotlinx.dataframe.columns.ColumnPath] */ - public interface ColumnNoAccessorDef + /** `columnName: `[`String`][String]` | `[`KProperty`][kotlin.reflect.KProperty]`<*>` */ + public interface ColumNameDef + + /** `columnNoPath: `[`ColumnAccessor`][org.jetbrains.kotlinx.dataframe.columns.ColumnAccessor]` | `[`String`][String]` | `[`KProperty`][kotlin.reflect.KProperty]`<*>` */ + public interface ColumnNoPathDef /** `columnOrSet: `{@include [ColumnRef]}` | `{@include [ColumnSetRef]} */ public interface ColumnOrColumnSetDef @@ -190,8 +193,11 @@ public interface DslGrammarTemplateColumnsSelectionDsl { /** [`columnGroup`][ColumnGroupDef] */ public interface ColumnGroupRef - /** [`columnNoAccessor`][ColumnNoAccessorDef] */ - public interface ColumnNoAccessorRef + /** [`columnName`][ColumNameDef] */ + public interface ColumnNameRef + + /** [`columnNoPath`][ColumnNoPathDef] */ + public interface ColumnNoPathRef /** [`columnOrSet`][ColumnOrColumnSetDef] */ public interface ColumnOrColumnSetRef diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/aggregation/PivotGroupByImpl.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/aggregation/PivotGroupByImpl.kt index 7e7168e864..843292ed93 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/aggregation/PivotGroupByImpl.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/aggregation/PivotGroupByImpl.kt @@ -1,5 +1,6 @@ package org.jetbrains.kotlinx.dataframe.impl.aggregation +import org.jetbrains.kotlinx.dataframe.AnyFrame import org.jetbrains.kotlinx.dataframe.ColumnsSelector import org.jetbrains.kotlinx.dataframe.DataFrame import org.jetbrains.kotlinx.dataframe.aggregation.AggregateBody @@ -9,6 +10,7 @@ import org.jetbrains.kotlinx.dataframe.api.PivotGroupBy import org.jetbrains.kotlinx.dataframe.api.aggregate import org.jetbrains.kotlinx.dataframe.api.cast import org.jetbrains.kotlinx.dataframe.api.firstOrNull +import org.jetbrains.kotlinx.dataframe.api.remove import org.jetbrains.kotlinx.dataframe.columns.toColumnSet import org.jetbrains.kotlinx.dataframe.impl.GroupByImpl import org.jetbrains.kotlinx.dataframe.impl.api.aggregatePivot @@ -33,10 +35,11 @@ internal data class PivotGroupByImpl( df.groups.firstOrNull() ?.getPivotColumnPaths(columns).orEmpty() .let { pivotPaths -> - { - all().except( - pivotPaths.toColumnSet() and (df as GroupByImpl).keyColumnsInGroups.toColumnSet(), - ) + return@let { + (this as AnyFrame) + .remove { pivotPaths.toColumnSet() and (df as GroupByImpl).keyColumnsInGroups.toColumnSet() } + .columns() + .toColumnSet() } } } diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/implode.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/implode.kt index 988f7bc3b9..581e7f9848 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/implode.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/implode.kt @@ -8,16 +8,20 @@ import org.jetbrains.kotlinx.dataframe.api.concat import org.jetbrains.kotlinx.dataframe.api.dropNA import org.jetbrains.kotlinx.dataframe.api.groupBy import org.jetbrains.kotlinx.dataframe.api.map +import org.jetbrains.kotlinx.dataframe.api.remove import org.jetbrains.kotlinx.dataframe.api.replace import org.jetbrains.kotlinx.dataframe.api.with import org.jetbrains.kotlinx.dataframe.columns.ColumnKind +import org.jetbrains.kotlinx.dataframe.columns.toColumnSet import org.jetbrains.kotlinx.dataframe.impl.columns.asAnyFrameColumn import org.jetbrains.kotlinx.dataframe.impl.columns.extractDataFrame import org.jetbrains.kotlinx.dataframe.impl.getListType import kotlin.reflect.typeOf internal fun DataFrame.implodeImpl(dropNA: Boolean = false, columns: ColumnsSelector): DataFrame = - groupBy { allExcept(columns) }.updateGroups { + groupBy { + (this as DataFrame).remove(columns).columns().toColumnSet() + }.updateGroups { replace(columns).with { column -> val (value, type) = when (column.kind()) { ColumnKind.Value -> (if (dropNA) column.dropNA() else column).toList() to getListType(column.type()) diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/columns/ColumnAccessorImpl.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/columns/ColumnAccessorImpl.kt index fe5d44d861..73ed90b1b8 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/columns/ColumnAccessorImpl.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/columns/ColumnAccessorImpl.kt @@ -24,8 +24,8 @@ internal class ColumnAccessorImpl(val path: ColumnPath) : ColumnAccessor { val col = df.getColumn(colName, context.unresolvedColumnsPolicy) ?: return null if (!col.isColumnGroup()) { error( - "Cannot resolve column '${path.subList(0, i + 2).joinToString(".")}': " + - "Column '${path.subList(0, i + 1).joinToString(".")}' is not a column group.", + "Cannot resolve column '${path.subList(0, i + 2).joinToString("/")}': " + + "Column '${path.subList(0, i + 1).joinToString("/")}' is not a column group.", ) } else { col diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/columns/Utils.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/columns/Utils.kt index 4eac81874f..ce6045cdb4 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/columns/Utils.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/columns/Utils.kt @@ -7,13 +7,9 @@ import org.jetbrains.kotlinx.dataframe.AnyRow import org.jetbrains.kotlinx.dataframe.ColumnsContainer import org.jetbrains.kotlinx.dataframe.DataColumn import org.jetbrains.kotlinx.dataframe.DataFrame -import org.jetbrains.kotlinx.dataframe.api.asColumnGroup import org.jetbrains.kotlinx.dataframe.api.cast import org.jetbrains.kotlinx.dataframe.api.name import org.jetbrains.kotlinx.dataframe.api.pathOf -import org.jetbrains.kotlinx.dataframe.api.remove -import org.jetbrains.kotlinx.dataframe.api.replace -import org.jetbrains.kotlinx.dataframe.api.with import org.jetbrains.kotlinx.dataframe.columns.BaseColumn import org.jetbrains.kotlinx.dataframe.columns.ColumnAccessor import org.jetbrains.kotlinx.dataframe.columns.ColumnGroup @@ -30,12 +26,11 @@ import org.jetbrains.kotlinx.dataframe.columns.UnresolvedColumnsPolicy import org.jetbrains.kotlinx.dataframe.columns.ValueColumn import org.jetbrains.kotlinx.dataframe.columns.values import org.jetbrains.kotlinx.dataframe.impl.DataFrameImpl -import org.jetbrains.kotlinx.dataframe.impl.asNullable import org.jetbrains.kotlinx.dataframe.impl.columns.missing.MissingDataColumn import org.jetbrains.kotlinx.dataframe.impl.columns.tree.ColumnPosition import org.jetbrains.kotlinx.dataframe.impl.columns.tree.TreeNode import org.jetbrains.kotlinx.dataframe.impl.columns.tree.collectTree -import org.jetbrains.kotlinx.dataframe.impl.columns.tree.getOrPut +import org.jetbrains.kotlinx.dataframe.impl.columns.tree.contains import org.jetbrains.kotlinx.dataframe.impl.columns.tree.put import org.jetbrains.kotlinx.dataframe.impl.columns.tree.topmostChildren import org.jetbrains.kotlinx.dataframe.impl.equalsByElement @@ -386,89 +381,52 @@ internal fun List>.simplify(): List> { return root.topmostChildren { it.data != null }.map { it.data!! } } -/** - * Returns a new list of column paths, except the ones inside [columns]. - * NOTE: The structure is not kept the same; if a column is removed, its parent will be removed as well, and - * all its siblings will be lifted out of the group. This also happens if a column is "removed" that does - * not exist in [this]. - */ -internal fun List>.allColumnsExceptAndUnpack( - columns: Iterable>, -): List> { - if (isEmpty()) return emptyList() - val fullTree = collectTree() - columns.forEach { - var node = fullTree.getOrPut(it.path).asNullable() - node?.allChildren()?.forEach { it.data = null } - while (node != null) { - node.data = null - node = node.parent - } + +private fun ColumnWithPath<*>.renderName(): String = + if (isColumnGroup()) { + "${path.joinToString()}/{${ + columns().map { it.addPath() }.joinToString { it.renderName() } + }}" + } else { + path.joinToString() } - val subtrees = fullTree.topmostChildren { it.data != null } - return subtrees.map { it.data!!.addPath(it.pathFromRoot()) } -} /** - * Returns a new list of column paths, except the ones inside [columns]. - * NOTE: ColumnGroups are adapted to keep their structure. If a column inside a column group is excepted, it will - * be removed from the group. - * Empty groups will be removed if [removeEmptyGroups]` == true` + * Returns a new list of distinct column paths, except the ones inside [columns]. + * NOTE: There are no structural changes as removing nested columns is not allowed. + * + * @throws IllegalArgumentException if a nested column were to be removed */ -internal fun List>.allColumnsExceptKeepingStructure( - columns: Iterable>, - removeEmptyGroups: Boolean = true, -): List> { +internal fun List>.removeAll(columnsToRemove: Iterable>): List> { if (isEmpty()) return emptyList() - val fullTree = collectTree() - for (columnToExcept in columns) { - // grab the node representing the column from the tree - val nodeToExcept = fullTree.getOrPut(columnToExcept.path).asNullable() - if (nodeToExcept != null) { - // remove the children from the node (if it's a column group) and remove its data (the column itself) - nodeToExcept.allChildren().forEach { it.data = null } - nodeToExcept.data = null - - // we need to update the data of the parent node(s) to reflect the removal of the column - if (nodeToExcept.parent != null) { - // we grab the data of the parent node, which should be a column group - // treat it as a DF to remove the column to except from it and - // convert it back to a column group - val current = nodeToExcept.parent.data as ColumnGroup<*>? ?: continue - val adjustedCurrent = current - .remove(nodeToExcept.name) - .asColumnGroup(current.name) - .addPath(current.path()) - - // remove the group if it's empty and removeEmptyGroups is true - // else, simply update the parent's data with the adjusted column group - nodeToExcept.parent.data = - if (adjustedCurrent.cols().isEmpty() && removeEmptyGroups) { - null - } else { - adjustedCurrent - } - - // now we update the parent's parents recursively with new column group instances - var parent = nodeToExcept.parent.parent - - @Suppress("UNNECESSARY_NOT_NULL_ASSERTION") - var currentNode = nodeToExcept.parent!! - while (parent != null) { - val parentData = parent.data as ColumnGroup<*>? ?: break - parent.data = parentData - .replace(currentNode.name).with { currentNode.data!! } - .asColumnGroup(parentData.name) - .addPath(parentData.path()) - - currentNode = parent - parent = parent.parent - } - } + + // subtract columnsToRemove from this + val result = this.toMutableSet() + val columnPathsToRemove = columnsToRemove + .map { it.path } + .toSet() + .mapNotNull { toRemove -> + val removed = result.removeIf { it.path == toRemove } + if (removed) null else toRemove + } + + // provide a helpful exception when a user tries to remove a nested column + if (columnPathsToRemove.isNotEmpty()) { + val fullTree = this.collectTree() + val nestedColumns = columnPathsToRemove.filter { it in fullTree } + + if (nestedColumns.isNotEmpty()) { + throw IllegalArgumentException( + "Cannot exclude the nested columns '[${ + nestedColumns.joinToString { it.joinToString() } + }]' from the column set '[${ + this.joinToString { it.renderName() } + }]' with the except-functions. Use the `DataFrame<*>.remove { }` operation instead.", + ) } } - val subtrees = fullTree.topmostChildren { it.data != null } - return subtrees.map { it.data!!.addPath(it.pathFromRoot()) } + + return result.toList() } /** diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/columns/tree/Utils.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/columns/tree/Utils.kt index 0db48d0eda..7f40b9c127 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/columns/tree/Utils.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/columns/tree/Utils.kt @@ -30,6 +30,17 @@ internal fun TreeNode.getOrPut(path: ColumnPath, createData: (ColumnPath) return node } +/** + * Checks if a given column path exists from the current tree node. + * + * @param path The sequence of column path parts to check from the tree node. + * @return True if the specified path exists from the tree node, false otherwise. + */ +internal operator fun TreeNode.contains(path: ColumnPath): Boolean = + path.fold(this as TreeNode?) { node, pathPart -> + node?.get(pathPart) + } != null + /** * Traverses all children in the tree in depth-first order and returns the top-most nodes that satisfy * [yieldCondition]. This means that if a node satisfies [yieldCondition], its children are not traversed, regardless of diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/util/deprecationMessages.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/util/deprecationMessages.kt index e710b368c7..959f8189db 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/util/deprecationMessages.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/util/deprecationMessages.kt @@ -79,4 +79,12 @@ internal const val ALL_COLS_EXCEPT = internal const val ALL_COLS_REPLACE = "allColsExcept { other }" internal const val ALL_COLS_REPLACE_VARARG = "allColsExcept { others.toColumnSet() }" +internal const val ALL_COLS_EXCEPT_COLUMN_PATH = + "This overload is blocked because you cannot use `allColsExcept` for columns nested in this column group. " + + "Use a String to refer to a column instead, or use DataFrame.remove {} to remove nested columns." + +internal const val ALL_EXCEPT_COLUMN_PATH = + "This overload is blocked because you cannot use `allExcept` for nested columns. " + + "Use a String to refer to a column instead, or use DataFrame.remove {} to remove nested columns." + // endregion diff --git a/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/allExcept.kt b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/allExcept.kt index 8489a9db32..72c5363b89 100644 --- a/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/allExcept.kt +++ b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/allExcept.kt @@ -1,5 +1,6 @@ package org.jetbrains.kotlinx.dataframe.api +import io.kotest.assertions.throwables.shouldNotThrowAny import io.kotest.assertions.throwables.shouldThrow import io.kotest.matchers.shouldBe import org.jetbrains.kotlinx.dataframe.alsoDebug @@ -10,23 +11,56 @@ import org.jetbrains.kotlinx.dataframe.samples.api.isHappy import org.jetbrains.kotlinx.dataframe.samples.api.lastName import org.jetbrains.kotlinx.dataframe.samples.api.name import org.jetbrains.kotlinx.dataframe.samples.api.secondName -import org.jetbrains.kotlinx.dataframe.samples.api.thirdName import org.jetbrains.kotlinx.dataframe.samples.api.weight import org.junit.Test class AllExceptTests : ColumnsSelectionDslTests() { @Test - fun `exceptions`() { + fun `disallow excepting nested unselected cols`() { + shouldThrow { + df.select { allExcept { name.firstName } } + } + shouldThrow { + dfGroup.select { name.allColsExcept { firstName.firstName and firstName.secondName } } + } + shouldThrow { + dfGroup.select { allExcept(name.firstName.firstName) } + } + shouldThrow { + dfGroup.select { allExcept { name.firstName.firstName } } + } + shouldThrow { + dfGroup.select { all() except { name.firstName.firstName } } + } + shouldThrow { + df.select { all() except { name.firstName } } + } + shouldThrow { + df.select { all() except name.firstName } + } + shouldThrow { + df.select { all() except "name"["firstName"] } + } + shouldThrow { + df.select { allExcept { "name"["firstName"] } } + } + shouldNotThrowAny { + df.select { colsAtAnyDepth() except name.firstName } + } + } + + @Test + fun `cannot be resolved`() { shouldThrow { dfGroup.select { - name.firstName.allColsExcept("firstName"["secondName"]) + name.firstName.allColsExcept { "firstName"["secondName"] } } } shouldThrow { dfGroup.select { - name.firstName.allColsExcept(pathOf("name", "firstName", "secondName")) + name.firstName.allColsExcept { pathOf("name", "firstName", "secondName") } } } } @@ -36,10 +70,6 @@ class AllExceptTests : ColumnsSelectionDslTests() { df.select { name.allColsExcept { all() } } shouldBe df.select { none() } df.select { allExcept { all() } } shouldBe df.select { none() } - - df.select { allExcept { name.allCols() } }.alsoDebug() - - df.remove { name.allCols() }.alsoDebug() } @Test @@ -51,7 +81,7 @@ class AllExceptTests : ColumnsSelectionDslTests() { df.select { allExcept { cols { it.name in listOf("name", "city") } } }, df.select { allExcept("name", "city") }, df.select { allExcept(Person::name, Person::city) }, - df.select { allExcept(pathOf("name"), pathOf("city")) }, +// df.select { allExcept(pathOf("name"), pathOf("city")) }, // blocked ).shouldAllBeEqual() listOf( @@ -61,7 +91,7 @@ class AllExceptTests : ColumnsSelectionDslTests() { df.select { allExcept { cols { it.name == "name" } } }, df.select { allExcept("name") }, df.select { allExcept(Person::name) }, - df.select { allExcept(pathOf("name")) }, +// df.select { allExcept(pathOf("name")) }, // blocked ).shouldAllBeEqual() listOf( @@ -150,8 +180,8 @@ class AllExceptTests : ColumnsSelectionDslTests() { df.select { name.allColsExcept("lastName", "lastName") }, df.select { name.allColsExcept(Name::lastName) }, df.select { name.allColsExcept(Name::lastName, Name::lastName) }, - df.select { name.allColsExcept(pathOf("lastName")) }, - df.select { name.allColsExcept(pathOf("lastName"), pathOf("lastName")) }, +// df.select { name.allColsExcept(pathOf("lastName")) }, // blocked +// df.select { name.allColsExcept(pathOf("lastName"), pathOf("lastName")) }, // blocked // df.select { name.allColsExcept(pathOf("name", "lastName")) }, // breaks df.select { name.allColsExcept { cols { "last" in it.name } } }, df.select { "name".allColsExcept { lastNameAccessor } }, @@ -161,8 +191,8 @@ class AllExceptTests : ColumnsSelectionDslTests() { df.select { "name".allColsExcept("lastName", "lastName") }, df.select { "name".allColsExcept(Name::lastName) }, df.select { "name".allColsExcept(Name::lastName, Name::lastName) }, - df.select { "name".allColsExcept(pathOf("lastName")) }, - df.select { "name".allColsExcept(pathOf("lastName"), pathOf("lastName")) }, +// df.select { "name".allColsExcept(pathOf("lastName")) }, // blocked +// df.select { "name".allColsExcept(pathOf("lastName"), pathOf("lastName")) }, // blocked // df.select { "name".allColsExcept(pathOf("name", "lastName")) }, // breaks df.select { "name".allColsExcept { cols { "last" in it.name } } }, // df.select { Person::name.allColsExcept(name.lastName) }, // blocked @@ -171,8 +201,8 @@ class AllExceptTests : ColumnsSelectionDslTests() { df.select { Person::name.allColsExcept("lastName", "lastName") }, df.select { Person::name.allColsExcept(Name::lastName) }, df.select { Person::name.allColsExcept(Name::lastName, Name::lastName) }, - df.select { Person::name.allColsExcept(pathOf("lastName")) }, - df.select { Person::name.allColsExcept(pathOf("lastName"), pathOf("lastName")) }, +// df.select { Person::name.allColsExcept(pathOf("lastName")) }, // blocked +// df.select { Person::name.allColsExcept(pathOf("lastName"), pathOf("lastName")) }, // blocked // df.select { Person::name.allColsExcept(pathOf("name", "lastName")) }, // breaks df.select { Person::name.allColsExcept { cols { "last" in it.name } } }, df.select { NonDataSchemaPerson::name.allColsExcept { lastName } }, @@ -183,8 +213,8 @@ class AllExceptTests : ColumnsSelectionDslTests() { df.select { NonDataSchemaPerson::name.allColsExcept("lastName", "lastName") }, df.select { NonDataSchemaPerson::name.allColsExcept(Name::lastName) }, df.select { NonDataSchemaPerson::name.allColsExcept(Name::lastName, Name::lastName) }, - df.select { NonDataSchemaPerson::name.allColsExcept(pathOf("lastName")) }, - df.select { NonDataSchemaPerson::name.allColsExcept(pathOf("lastName"), pathOf("lastName")) }, +// df.select { NonDataSchemaPerson::name.allColsExcept(pathOf("lastName")) }, // blocked +// df.select { NonDataSchemaPerson::name.allColsExcept(pathOf("lastName"), pathOf("lastName")) }, // blocked // df.select { NonDataSchemaPerson::name.allColsExcept(pathOf("name", "lastName")) }, // breaks df.select { NonDataSchemaPerson::name.allColsExcept { cols { "last" in it.name } } }, df.select { pathOf("name").allColsExcept { lastNameAccessor } }, @@ -194,92 +224,10 @@ class AllExceptTests : ColumnsSelectionDslTests() { df.select { pathOf("name").allColsExcept("lastName", "lastName") }, df.select { pathOf("name").allColsExcept(Name::lastName) }, df.select { pathOf("name").allColsExcept(Name::lastName, Name::lastName) }, - df.select { pathOf("name").allColsExcept(pathOf("lastName")) }, - df.select { pathOf("name").allColsExcept(pathOf("lastName"), pathOf("lastName")) }, +// df.select { pathOf("name").allColsExcept(pathOf("lastName")) }, // blocked +// df.select { pathOf("name").allColsExcept(pathOf("lastName"), pathOf("lastName")) }, // blocked // df.select { pathOf("name").allColsExcept(pathOf("name", "lastName")) }, // breaks df.select { pathOf("name").allColsExcept { cols { "last" in it.name } } }, ).shouldAllBeEqual() } - - @Test - fun `2 levels deep`() { - listOf( - dfGroup.remove { name.firstName.secondName }.select { name.allCols() }.alsoDebug(), - dfGroup.select { - name.allColsExcept("firstName"["secondName"]) - }, - dfGroup.select { - name.allColsExcept { firstName.secondName and firstName.secondName } - }, - dfGroup.select { - name.allColsExcept { colGroup("firstName").col("secondName") } - }, - ).shouldAllBeEqual() - - listOf( - dfGroup.remove { name.firstName.secondName }.select { name.firstName.allCols() }.alsoDebug(), - dfGroup.select { - name.firstName.allColsExcept("secondName") - }, - dfGroup.select { - name.firstName.allColsExcept { secondName } - }, - dfGroup.select { - name.firstName.allColsExcept(pathOf("secondName")) - }, - ).shouldAllBeEqual() - - listOf( - dfGroup.select { name.firstName { secondName and thirdName } }, - dfGroup.select { name { firstName.allColsExcept("firstName") } }.alsoDebug(), - dfGroup.select { name { firstName.allColsExcept(pathOf("firstName")) } }.alsoDebug(), - dfGroup.select { (name.allColsExcept("firstName"["firstName"])).first().asColumnGroup().allCols() }, - dfGroup.remove { name.firstName.firstName }.select { name.firstName.allCols() }, - ).shouldAllBeEqual() - - val secondNameAccessor = column("secondName") - val thirdNameAccessor = column("thirdName") - - listOf( - dfGroup.select { name.firstName.firstName }, - dfGroup.select { name.firstName.allColsExcept { secondName and thirdName } }, - dfGroup.select { name.firstName.allColsExcept { secondNameAccessor and thirdNameAccessor } }, - dfGroup.select { name.firstName.allColsExcept("secondName", "thirdName") }, - dfGroup.select { name.firstName.allColsExcept(FirstNames::secondName, FirstNames::thirdName) }, - dfGroup.select { name.firstName.allColsExcept(pathOf("secondName"), pathOf("thirdName")) }, - dfGroup.select { name.firstName.allColsExcept { cols { it.name in listOf("secondName", "thirdName") } } }, - dfGroup.select { name.firstName { allExcept { secondName and thirdName } } }, - dfGroup.select { name.firstName { allExcept { secondNameAccessor and thirdNameAccessor } } }, - dfGroup.select { name.firstName { allExcept("secondName", "thirdName") } }, - dfGroup.select { name.firstName { allExcept(FirstNames::secondName, FirstNames::thirdName) } }, - dfGroup.select { name.firstName { allExcept(pathOf("secondName"), pathOf("thirdName")) } }, - dfGroup.select { name.firstName { allExcept { cols { it.name in listOf("secondName", "thirdName") } } } }, - dfGroup.select { name { firstName.allColsExcept { secondName and thirdName } } }, - dfGroup.select { name { firstName.allColsExcept { secondNameAccessor and thirdNameAccessor } } }, - dfGroup.select { name { firstName.allColsExcept("secondName", "thirdName") } }, - dfGroup.select { name { firstName.allColsExcept(FirstNames::secondName, FirstNames::thirdName) } }, - dfGroup.select { name { firstName.allColsExcept(pathOf("secondName"), pathOf("thirdName")) } }, - dfGroup.select { - name { - firstName.allColsExcept { - cols { it.name in listOf("secondName", "thirdName") } - } - } - }, - dfGroup.select { name { firstName { allExcept { secondName and thirdName } } } }, - dfGroup.select { name { firstName { allExcept { secondNameAccessor and thirdNameAccessor } } } }, - dfGroup.select { name { firstName { allExcept("secondName", "thirdName") } } }, - dfGroup.select { name { firstName { allExcept(FirstNames::secondName, FirstNames::thirdName) } } }, - dfGroup.select { name { firstName { allExcept(pathOf("secondName"), pathOf("thirdName")) } } }, - dfGroup.select { - name { - firstName { - allExcept { - cols { it.name in listOf("secondName", "thirdName") } - } - } - } - }, - ).shouldAllBeEqual() - } } diff --git a/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/testSets/person/DataFrameTests.kt b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/testSets/person/DataFrameTests.kt index 31f695f407..b1444e1c76 100644 --- a/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/testSets/person/DataFrameTests.kt +++ b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/testSets/person/DataFrameTests.kt @@ -2482,17 +2482,6 @@ class DataFrameTests : BaseTest() { @Test fun `except in columns selector`() { typed.select { allExcept { age and weight } } shouldBe typed.select { name and city } - - typed - .group { age and weight and city }.into("info") - .alsoDebug() - .select { allExcept { "info"["age"] } } - .alsoDebug() - .let { - it.name shouldBe typed.name - it["info"]["weight"] shouldBe typed.weight - it["info"]["city"] shouldBe typed.city - } } @Test