@@ -10,53 +10,230 @@ import org.jetbrains.kotlinx.dataframe.annotations.Interpretable
1010import org.jetbrains.kotlinx.dataframe.annotations.Refine
1111import org.jetbrains.kotlinx.dataframe.columns.ColumnAccessor
1212import org.jetbrains.kotlinx.dataframe.columns.ColumnPath
13+ import org.jetbrains.kotlinx.dataframe.documentation.DocumentationUrls
14+ import org.jetbrains.kotlinx.dataframe.documentation.DslGrammarLink
15+ import org.jetbrains.kotlinx.dataframe.documentation.ExcludeFromSources
16+ import org.jetbrains.kotlinx.dataframe.documentation.Indent
17+ import org.jetbrains.kotlinx.dataframe.documentation.LineBreak
18+ import org.jetbrains.kotlinx.dataframe.documentation.SelectingColumns
1319import org.jetbrains.kotlinx.dataframe.impl.api.insertImpl
1420import org.jetbrains.kotlinx.dataframe.impl.columnName
1521import org.jetbrains.kotlinx.dataframe.impl.removeAt
1622import org.jetbrains.kotlinx.dataframe.util.DEPRECATED_ACCESS_API
23+ import org.jetbrains.kotlinx.dataframe.util.INSERT_AFTER_COL_PATH
24+ import org.jetbrains.kotlinx.dataframe.util.INSERT_AFTER_COL_PATH_REPLACE
1725import kotlin.reflect.KProperty
1826
1927// region DataFrame
2028
2129// region insert
2230
31+ /* *
32+ * This function does not immediately insert the new column but instead specify a column to insert and
33+ * returns an [InsertClause],
34+ * which serves as an intermediate step.
35+ * The [InsertClause] object provides methods to insert a new column using:
36+ * - [under][InsertClause.under] - inserts a new column under the specified column group.
37+ * - [after][InsertClause.after] - inserts a new column after the specified column.
38+ * - [at][InsertClause.at]- inserts a new column at the specified position.
39+ *
40+ * Each method returns a new [DataFrame] with the inserted column.
41+ *
42+ * Check out [Grammar].
43+ *
44+ * @include [SelectingColumns.ColumnGroupsAndNestedColumnsMention]
45+ *
46+ * See [Selecting Columns][InsertSelectingOptions].
47+ *
48+ * For more information: {@include [DocumentationUrls.Insert]}
49+ *
50+ * See also:
51+ * - [move][DataFrame.move] - move columns to a new position within the [DataFrame].
52+ * - [add][DataFrame.add] - add new columns to the [DataFrame]
53+ * (without specifying a position, to the end of the [DataFrame]).
54+ */
55+ internal interface InsertDocs {
56+
57+ /* *
58+ * {@comment Version of [SelectingColumns] with correctly filled in examples}
59+ * @include [SelectingColumns] {@include [SetInsertOperationArg]}
60+ */
61+ interface InsertSelectingOptions
62+
63+ /* *
64+ * ## Insert Operation Grammar
65+ * {@include [LineBreak]}
66+ * {@include [DslGrammarLink]}
67+ * {@include [LineBreak]}
68+ *
69+ * [**`insert`**][insert]**`(`**`column: `[`DataColumn`][DataColumn]**`)`**` /`
70+ *
71+ * [**`insert`**][insert]**`(`**`name: `[`String`][String]**`, `**`infer: `[`Infer`][Infer]**`) { `**`rowExpression: `[`RowExpression`][RowExpression]**` }`**
72+ *
73+ * {@include [Indent]}
74+ * __`.`__[**`under`**][InsertClause.under]**` { `**`column: `[`ColumnSelector`][ColumnSelector]**` }`**
75+ *
76+ * {@include [Indent]}
77+ * `| `__`.`__[**`under`**][InsertClause.under]**`(`**` columnPath: `[`ColumnPath`][ColumnPath]**`)`**
78+ *
79+ * {@include [Indent]}
80+ * `| `__`.`__[**`after`**][InsertClause.after]**` { `**`column: `[`ColumnSelector`][ColumnSelector]**` }`**
81+ *
82+ * {@include [Indent]}
83+ * `| `__`.`__[**`at`**][InsertClause.at]**`(`**`position: `[`Int`][Int]**`)`**
84+ */
85+ interface Grammar
86+ }
87+
88+ /* * {@set [SelectingColumns.OPERATION] [insert][insert]} */
89+ @ExcludeFromSources
90+ private interface SetInsertOperationArg
91+
92+ /* *
93+ * Inserts the given [column] into this [DataFrame].
94+ *
95+ * {@include [InsertDocs]}
96+ *
97+ * ### Examples:
98+ * ```kotlin
99+ * // Insert a new column "age" under the column group with path ("info", "personal").
100+ * df.insert(age).under(pathOf("info", "personal"))
101+ *
102+ * // Insert a new column "count" after the column "url".
103+ * df.insert(count).after { url }
104+ * ```
105+ *
106+ * @param [column] A single [DataColumn] to insert into the [DataFrame].
107+ * @return An [InsertClause] for specifying the placement of the new column.
108+ */
23109public fun <T , C > DataFrame<T>.insert (column : DataColumn <C >): InsertClause <T > = InsertClause (this , column)
24110
111+ /* *
112+ * Creates a new column using the provided [expression][AddExpression] and inserts it into this [DataFrame].
113+ *
114+ * {@include [AddExpressionDocs]}
115+ *
116+ * {@include [InsertDocs]}
117+ *
118+ * ## Examples
119+ *
120+ * ```kotlin
121+ * // Insert a new column "sum" that contains the sum of values from the "firstValue"
122+ * // and "secondValue" columns for each row after the "firstValue" column.
123+ * val dfWithSum = df.insert("sum") { firstValue + secondValue }.after { firstValue }
124+ *
125+ * // Insert a new "fibonacci" column with the Fibonacci sequence under a "math" column group:
126+ * // for the first two rows, the value is 1;
127+ * // for subsequent rows, it's the sum of the two previous Fibonacci values.
128+ * val dfWithFibonacci = df.insert("fibonacci") {
129+ * if (index() < 2) 1
130+ * else prev()!!.newValue<Int>() + prev()!!.prev()!!.newValue<Int>()
131+ * }.under("math")
132+ * ```
133+ *
134+ * @param [name] The name of the new column to be created and inserted.
135+ * @param [infer] Controls how values are inferred when building the new column. Defaults to [Infer.Nulls].
136+ * @param [expression] An [AddExpression] that computes the value for each row of the new column.
137+ * @return An [InsertClause] for specifying the placement of the newly created column.
138+ */
25139@Interpretable(" Insert1" )
26140public inline fun <T , reified R > DataFrame<T>.insert (
27141 name : String ,
28142 infer : Infer = Infer .Nulls ,
29- noinline expression : RowExpression <T , R >,
143+ noinline expression : AddExpression <T , R >,
30144): InsertClause <T > = insert(mapToColumn(name, infer, expression))
31145
32146@Deprecated(DEPRECATED_ACCESS_API )
33147@AccessApiOverload
34148public inline fun <T , reified R > DataFrame<T>.insert (
35149 column : ColumnAccessor <R >,
36150 infer : Infer = Infer .Nulls ,
37- noinline expression : RowExpression <T , R >,
151+ noinline expression : AddExpression <T , R >,
38152): InsertClause <T > = insert(column.name(), infer, expression)
39153
40154@Deprecated(DEPRECATED_ACCESS_API )
41155@AccessApiOverload
42156public inline fun <T , reified R > DataFrame<T>.insert (
43157 column : KProperty <R >,
44158 infer : Infer = Infer .Nulls ,
45- noinline expression : RowExpression <T , R >,
159+ noinline expression : AddExpression <T , R >,
46160): InsertClause <T > = insert(column.columnName, infer, expression)
47161
48162// endregion
49163
164+ /* *
165+ * An intermediate class used in the [insert] operation.
166+ *
167+ * This class itself does not perform any insertions — it is a transitional step
168+ * before specifying how to insert the selected columns.
169+ * It must be followed by one of the inserting methods
170+ * to produce a new [DataFrame] with an inserted column.
171+ *
172+ * Use the following methods to perform the insertion:
173+ * - [under][InsertClause.under] - inserts a new column under the specified column group.
174+ * - [after][InsertClause.after] - inserts a new column after the specified column.
175+ * - [at][InsertClause.at]- inserts a new column at the specified position.
176+ *
177+ * See [Grammar][InsertDocs.Grammar] for more details.
178+ */
50179public class InsertClause <T >(internal val df : DataFrame <T >, internal val column : AnyCol ) {
51180 override fun toString (): String = " InsertClause(df=$df , column=$column )"
52181}
53182
54183// region under
55184
185+ /* *
186+ * Inserts the new column previously specified with [insert] under
187+ * the selected [column group][column].
188+ *
189+ * Works only with existing column groups.
190+ * To insert into a new column group, use the overloads:
191+ * `under(path: ColumnPath)` or `under(column: String)`.
192+ * [Should be fixed](https://github.com/Kotlin/dataframe/issues/1411).
193+ *
194+ * For more information: {@include [DocumentationUrls.Insert]}
195+ *
196+ * See [Grammar][InsertDocs.Grammar] for more details.
197+ *
198+ * See [SelectingColumns.Dsl].
199+ *
200+ * ### Examples
201+ * ```kotlin
202+ * // Insert a new column "age" under the column group with path ("info", "personal")
203+ * df.insert(age).under { info.personal }
204+ *
205+ * // Insert a new column "sum" under the only top-level column group
206+ * val dfWithSum = df.insert("sum") { a + b }.under { colGroups().single() }
207+ * ```
208+ *
209+ * @param column The [ColumnSelector] used to choose an existing column group in this [DataFrame]
210+ * under which the new column will be inserted.
211+ * @return A new [DataFrame] with the inserted column placed under the selected group.
212+ */
56213@Refine
57214@Interpretable(" Under0" )
58215public fun <T > InsertClause<T>.under (column : ColumnSelector <T , * >): DataFrame <T > = under(df.getColumnPath(column))
59216
217+ /* *
218+ * Inserts the new column previously specified with [insert] under
219+ * the column group defined by the given [columnPath].
220+ *
221+ * {@include [org.jetbrains.kotlinx.dataframe.documentation.ColumnPathCreation]}
222+ *
223+ * See [Grammar][InsertDocs.Grammar] for more details.
224+ *
225+ * For more information: {@include [DocumentationUrls.Insert]}
226+ *
227+ * ### Example
228+ * ```kotlin
229+ * // Insert a new column "age" under the column group with path ("info", "personal")
230+ * df.insert(age).under(pathOf("info", "personal"))
231+ * ```
232+ *
233+ * @param [columnPath] The [ColumnPath] specifying the path to a column group in this [DataFrame]
234+ * under which the new column will be inserted.
235+ * @return A new [DataFrame] with the inserted column placed under the specified column group.
236+ */
60237@Refine
61238@Interpretable(" Under1" )
62239public fun <T > InsertClause<T>.under (columnPath : ColumnPath ): DataFrame <T > =
@@ -70,6 +247,26 @@ public fun <T> InsertClause<T>.under(column: ColumnAccessor<*>): DataFrame<T> =
70247@AccessApiOverload
71248public fun <T > InsertClause<T>.under (column : KProperty <* >): DataFrame <T > = under(column.columnName)
72249
250+ /* *
251+ * Inserts the new column previously specified with [insert] under
252+ * the given column group by its [name][column].
253+ *
254+ * If the column group with the provided [name][column] does not exist, it will be created automatically.
255+ *
256+ * For more information: {@include [DocumentationUrls.Insert]}
257+ *
258+ * See [Grammar][InsertDocs.Grammar] for more details.
259+ *
260+ * ### Example
261+ * ```kotlin
262+ * // Insert a new column "age" under the "info" column group.
263+ * df.insert(age).under("info")
264+ * ```
265+ *
266+ * @param [column] The [name][String] of the column group in this [DataFrame].
267+ * If the group does not exist, it will be created.
268+ * @return A new [DataFrame] with the inserted column placed under the specified column group.
269+ */
73270@Refine
74271@Interpretable(" Under4" )
75272public fun <T > InsertClause<T>.under (column : String ): DataFrame <T > = under(pathOf(column))
@@ -78,29 +275,98 @@ public fun <T> InsertClause<T>.under(column: String): DataFrame<T> = under(pathO
78275
79276// region after
80277
278+ /* *
279+ * Inserts the new column previously specified with [insert]
280+ * at the position immediately after the selected [column] (on the same level).
281+ *
282+ * For more information: {@include [DocumentationUrls.Insert]}
283+ *
284+ * See [Grammar][InsertDocs.Grammar] for more details.
285+ *
286+ * See also: [SelectingColumns.Dsl].
287+ *
288+ * ### Examples:
289+ * ```kotlin
290+ * // Insert a new column "age" after the "name" column
291+ * df.insert(age).after { name }
292+ *
293+ * // Insert a new column "sum" after the nested "min" column (inside the "stats" column group)
294+ * val dfWithSum = df.insert("sum") { a + b }.after { stats.min }
295+ * ```
296+ *
297+ * @param [column] The [ColumnSelector] used to choose an existing column in this [DataFrame],
298+ * after which the new column will be inserted.
299+ * @return A new [DataFrame] with the inserted column placed after the selected column.
300+ */
81301@Refine
82302@Interpretable(" InsertAfter0" )
83- public fun <T > InsertClause<T>.after (column : ColumnSelector <T , * >): DataFrame <T > = after (df.getColumnPath(column))
303+ public fun <T > InsertClause<T>.after (column : ColumnSelector <T , * >): DataFrame <T > = afterImpl (df.getColumnPath(column))
84304
305+ /* *
306+ * Inserts the new column previously specified with [insert]
307+ * at the position immediately after the column with the given [name][column].
308+ *
309+ * For more information: {@include [DocumentationUrls.Insert]}
310+ *
311+ * See [Grammar][InsertDocs.Grammar] for more details.
312+ *
313+ * See also: [SelectingColumns.ColumnNames].
314+ *
315+ * ### Example
316+ * ```kotlin
317+ * // Insert a new column "age" after the "name" column
318+ * df.insert(age).after("name")
319+ * ```
320+ *
321+ * @param [column] The [String] name of the column in this [DataFrame]
322+ * after which the new column will be inserted.
323+ * @return A new [DataFrame] with the inserted column placed after the specified column.
324+ */
85325public fun <T > InsertClause<T>.after (column : String ): DataFrame <T > = df.add(this .column).move(this .column).after(column)
86326
87327@Deprecated(DEPRECATED_ACCESS_API )
88328@AccessApiOverload
89- public fun <T > InsertClause<T>.after (column : ColumnAccessor <* >): DataFrame <T > = after (column.path())
329+ public fun <T > InsertClause<T>.after (column : ColumnAccessor <* >): DataFrame <T > = afterImpl (column.path())
90330
91331@Deprecated(DEPRECATED_ACCESS_API )
92332@AccessApiOverload
93333public fun <T > InsertClause<T>.after (column : KProperty <* >): DataFrame <T > = after(column.columnName)
94334
335+ @Deprecated(INSERT_AFTER_COL_PATH , ReplaceWith (INSERT_AFTER_COL_PATH_REPLACE ), DeprecationLevel .ERROR )
95336public fun <T > InsertClause<T>.after (columnPath : ColumnPath ): DataFrame <T > {
96337 val dstPath = ColumnPath (columnPath.removeAt(columnPath.size - 1 ) + column.name())
97338 return df.insertImpl(dstPath, column).move { dstPath }.after { columnPath }
98339}
99340
341+ internal fun <T > InsertClause<T>.afterImpl (columnPath : ColumnPath ): DataFrame <T > {
342+ val dstPath = ColumnPath (columnPath.removeAt(columnPath.size - 1 ) + column.name())
343+ return df.insertImpl(dstPath, column).move { dstPath }.after { columnPath }
344+ }
345+
100346// endregion
101347
102348// region at
103349
350+ /* *
351+ * Inserts the new column previously specified with [insert]
352+ * at the given [position] in the [DataFrame].
353+ *
354+ * The new column will be placed at the specified index, shifting existing columns to the right.
355+ *
356+ * For more information: {@include [DocumentationUrls.Insert]}
357+ *
358+ * See [Grammar][InsertDocs.Grammar] for more details.
359+ *
360+ * ### Example
361+ * ```kotlin
362+ * // Insert a new column "age" at index 3
363+ * df.insert(age).at(3)
364+ * ```
365+ *
366+ * @param [position] The [Int] index where the new column should be inserted.
367+ * Columns currently at this index and after will be shifted right.
368+ * @return A new [DataFrame] with the inserted column placed at the specified position.
369+ */
104370@Refine
105371@Interpretable(" InsertAt" )
106372public fun <T > InsertClause<T>.at (position : Int ): DataFrame <T > = df.add(column).move(column).to(position)
0 commit comments