Skip to content

Commit 1a6d6e4

Browse files
committed
Add documentation for the first and firstOrNull functions
1 parent 95c575a commit 1a6d6e4

File tree

1 file changed

+294
-0
lines changed
  • core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api

1 file changed

+294
-0
lines changed

core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/first.kt

Lines changed: 294 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import org.jetbrains.kotlinx.dataframe.columns.values
1818
import org.jetbrains.kotlinx.dataframe.documentation.DslGrammarTemplateColumnsSelectionDsl.DslGrammarTemplate
1919
import org.jetbrains.kotlinx.dataframe.documentation.Indent
2020
import org.jetbrains.kotlinx.dataframe.documentation.LineBreak
21+
import org.jetbrains.kotlinx.dataframe.documentation.RowFilterDescription
22+
import org.jetbrains.kotlinx.dataframe.documentation.SelectingColumns
2123
import org.jetbrains.kotlinx.dataframe.impl.columns.TransformableColumnSet
2224
import org.jetbrains.kotlinx.dataframe.impl.columns.singleOrNullWithTransformerImpl
2325
import org.jetbrains.kotlinx.dataframe.impl.columns.transform
@@ -27,32 +29,141 @@ import kotlin.reflect.KProperty
2729

2830
// region DataColumn
2931

32+
/**
33+
* Returns the first value in this [DataColumn].
34+
*
35+
* @param T The type of the values in the [DataColumn].
36+
*
37+
* @throws [IndexOutOfBoundsException] if the [DataColumn] is empty.
38+
*/
3039
public fun <T> DataColumn<T>.first(): T = get(0)
3140

41+
/**
42+
* Returns the first value in this [DataColumn]. If the [DataColumn] is empty, returns `null`.
43+
*
44+
* @param T The type of the values in the [DataColumn].
45+
*/
3246
public fun <T> DataColumn<T>.firstOrNull(): T? = if (size > 0) first() else null
3347

48+
/**
49+
* Returns the first value in this [DataColumn] that matches the given [predicate].
50+
*
51+
* ### Example
52+
* ```kotlin
53+
* // Select from the column "age" the first value where the age is greater than 17
54+
* df.age.first { it > 17 }
55+
* ```
56+
*
57+
* @param T The type of the values in the [DataColumn].
58+
* @param predicate A lambda expression used to select a value
59+
* that satisfies a condition specified in this expression.
60+
* This predicate takes a value from the [DataColumn] as an input
61+
* and returns `true` if the value satisfies the condition or `false` otherwise.
62+
*
63+
* @throws [NoSuchElementException] if the [DataColumn] contains no element matching the [predicate]
64+
* (including the case when the [DataColumn] is empty).
65+
*/
3466
public fun <T> DataColumn<T>.first(predicate: (T) -> Boolean): T = values.first(predicate)
3567

68+
/**
69+
* Returns the first value in this [DataColumn] that matches the given [predicate].
70+
* Returns `null` if the [DataColumn] contains no element matching the [predicate]
71+
* (including the case when the [DataColumn] is empty).
72+
*
73+
* ### Example
74+
* ```kotlin
75+
* // Select from the column "age" the first value where the age is greater than 17,
76+
* // or null if there is no such value
77+
* df.age.firstOrNull { it > 17 }
78+
* ```
79+
*
80+
* @param T The type of the values in the [DataColumn].
81+
* @param predicate A lambda expression used to select a value
82+
* that satisfies a condition specified in this expression.
83+
* This predicate takes a value from the [DataColumn] as an input
84+
* and returns `true` if the value satisfies the condition or `false` otherwise.
85+
*/
3686
public fun <T> DataColumn<T>.firstOrNull(predicate: (T) -> Boolean): T? = values.firstOrNull(predicate)
3787

3888
// endregion
3989

4090
// region DataFrame
4191

92+
/**
93+
* Returns the first row in this [DataFrame].
94+
*
95+
* @param T The type of the [DataFrame].
96+
*
97+
* @throws NoSuchElementException if the [DataFrame] contains no rows.
98+
*/
4299
public fun <T> DataFrame<T>.first(): DataRow<T> {
43100
if (nrow == 0) {
44101
throw NoSuchElementException("DataFrame has no rows. Use `firstOrNull`.")
45102
}
46103
return get(0)
47104
}
48105

106+
/**
107+
* Returns the first row in this [DataFrame]. If the [DataFrame] does not contain any rows, returns `null`.
108+
*
109+
* @param T The type of the [DataFrame].
110+
*/
49111
public fun <T> DataFrame<T>.firstOrNull(): DataRow<T>? = if (nrow > 0) first() else null
50112

113+
/**
114+
* Returns the first row in this [DataFrame] that satisfies the given [predicate].
115+
*
116+
* {@include [RowFilterDescription]}
117+
*
118+
* @include [SelectingColumns.ColumnGroupsAndNestedColumnsMention]
119+
*
120+
* ### Example
121+
* ```kotlin
122+
* // Select the first row where the value in the "age" column is greater than 17
123+
* // and the "name/firstName" column starts with 'A'
124+
* df.first { age > 17 && name.firstName.startsWith("A") }
125+
* ```
126+
*
127+
* @param T The type of the [DataFrame].
128+
* @param predicate A lambda expression used to select a value
129+
* that satisfies a condition specified in this expression.
130+
* This predicate takes a value from the [DataFrame] as an input
131+
* and returns `true` if the value satisfies the condition or `false` otherwise.
132+
*
133+
* @return A [DataRow] containing the first row that matches the given [predicate].
134+
*
135+
* @throws [NoSuchElementException] if the [DataFrame] contains no rows matching the [predicate].
136+
*/
51137
public inline fun <T> DataFrame<T>.first(predicate: RowFilter<T>): DataRow<T> =
52138
rows().first {
53139
predicate(it, it)
54140
}
55141

142+
/**
143+
* Returns the first row in this [DataFrame] that satisfies the given [predicate].
144+
* Returns `null` if the [DataFrame] contains no rows matching the [predicate]
145+
* (including the case when the [DataFrame] is empty).
146+
*
147+
* {@include [RowFilterDescription]}
148+
*
149+
* @include [SelectingColumns.ColumnGroupsAndNestedColumnsMention]
150+
*
151+
* ### Example
152+
* ```kotlin
153+
* // Select the first row where the value in the "age" column is greater than 17
154+
* // and the "name/firstName" column starts with 'A'
155+
* df.firstOrNull { age > 17 && name.firstName.startsWith("A") }
156+
* ```
157+
*
158+
* @param T The type of the [DataFrame].
159+
* @param predicate A lambda expression used to select a value
160+
* that satisfies a condition specified in this expression.
161+
* This predicate takes a value from the [DataFrame] as an input
162+
* and returns `true` if the value satisfies the condition or `false` otherwise.
163+
*
164+
* @return A [DataRow] containing the first row that matches the given [predicate],
165+
* or `null` if the [DataFrame] contains no rows matching the [predicate]
166+
*/
56167
public inline fun <T> DataFrame<T>.firstOrNull(predicate: RowFilter<T>): DataRow<T>? =
57168
rows().firstOrNull {
58169
predicate(it, it)
@@ -62,26 +173,209 @@ public inline fun <T> DataFrame<T>.firstOrNull(predicate: RowFilter<T>): DataRow
62173

63174
// region GroupBy
64175

176+
/**
177+
* Selects the first row from each group of the given [GroupBy]
178+
* and returns a [ReducedGroupBy] containing these rows
179+
* (one row per group, each row is the first row in its group).
180+
*
181+
* ### Example
182+
* ```kotlin
183+
* // Select the first employee from each group formed by the job title
184+
* employees.groupBy { jobTitle }.first()
185+
* ```
186+
*
187+
* @param T The type of the values in the [GroupBy].
188+
* @param G The type of the groups in the [GroupBy].
189+
*
190+
* @return A [ReducedGroupBy] containing the first row from each group.
191+
*/
65192
@Interpretable("GroupByReducePredicate")
66193
public fun <T, G> GroupBy<T, G>.first(): ReducedGroupBy<T, G> = reduce { firstOrNull() }
67194

195+
/**
196+
* Selects from each group of the given [GroupBy] the first row satisfying the given [predicate],
197+
* and returns a [ReducedGroupBy] containing these rows (one row per group,
198+
* each row is the first row in its group that satisfies the [predicate]).
199+
*
200+
* If the group in [GroupBy] contains no matching rows,
201+
* the corresponding row in [ReducedGroupBy] will contain `null` values for all columns in the group.
202+
*
203+
* {@include [RowFilterDescription]}
204+
*
205+
* @include [SelectingColumns.ColumnGroupsAndNestedColumnsMention]
206+
*
207+
* ### Example
208+
* ```kotlin
209+
* // Select the first employee older than 25 from each group formed by the job title
210+
* employees.groupBy { jobTitle }.first { age > 25 }
211+
* ```
212+
*
213+
* @param T The type of the values in the [GroupBy].
214+
* @param G The type of the groups in the [GroupBy].
215+
* @param predicate A lambda expression used to select a value
216+
* that satisfies a condition specified in this expression.
217+
* This predicate takes a value from the [GroupBy] as an input
218+
* and returns `true` if the value satisfies the condition or `false` otherwise.
219+
*
220+
* @return A [ReducedGroupBy] containing the first row matching the [predicate]
221+
* (or a row with `null` values, except values in the column with the grouping key), from each group.
222+
*/
68223
@Interpretable("GroupByReducePredicate")
69224
public fun <T, G> GroupBy<T, G>.first(predicate: RowFilter<G>): ReducedGroupBy<T, G> = reduce { firstOrNull(predicate) }
70225

71226
// endregion
72227

73228
// region Pivot
74229

230+
/**
231+
* Reduces this [Pivot] by selecting the first row from each group.
232+
*
233+
* Returns a [ReducedPivot] where:
234+
* - each column corresponds to a [pivot] group — if multiple pivot keys were used,
235+
* the result will contain column groups for each pivot key, with columns inside
236+
* corresponding to the values of that key;
237+
* - each value contains the first row from that group.
238+
*
239+
* The original [Pivot] column structure is preserved.
240+
* If the [Pivot] was created using multiple or nested keys
241+
* (e.g., via [and][PivotDsl.and] or [then][PivotDsl.then]),
242+
* the structure remains unchanged — only the contents of each group
243+
* are replaced with the first row from that group.
244+
*
245+
* Equivalent to `reduce { firstOrNull() }`.
246+
*
247+
* See also:
248+
* - [pivot];
249+
* - common [reduce][Pivot.reduce].
250+
*
251+
* ### Example
252+
* ```kotlin
253+
* // Select the first row for each city.
254+
* // Returns a ReducedPivot with one column per city and the first row from the group in each column.
255+
* df.pivot { city }.first()
256+
* ```
257+
*
258+
* @return A [ReducedPivot] containing in each column the first row from the corresponding group.
259+
*/
75260
public fun <T> Pivot<T>.first(): ReducedPivot<T> = reduce { firstOrNull() }
76261

262+
/**
263+
* Reduces this [Pivot] by selecting from each group the first row satisfying the given [predicate].
264+
*
265+
* Returns a [ReducedPivot] where:
266+
* - each column corresponds to a [pivot] group — if multiple pivot keys were used,
267+
* the result will contain column groups for each pivot key, with columns inside
268+
* corresponding to the values of that key;
269+
* - each value contains the first row from that group that satisfies the [predicate],
270+
* or a row with `null` values if no rows in this group match the [predicate].
271+
*
272+
* The original [Pivot] column structure is preserved.
273+
* If the [Pivot] was created using multiple or nested keys
274+
* (e.g., via [and][PivotDsl.and] or [then][PivotDsl.then]),
275+
* the structure remains unchanged — only the contents of each group
276+
* are replaced with the first row from that group that satisfies the [predicate].
277+
*
278+
* Equivalent to `reduce { firstOrNull(predicate) }`.
279+
*
280+
* See also:
281+
* - [pivot];
282+
* - common [reduce][Pivot.reduce].
283+
*
284+
* {@include [RowFilterDescription]}
285+
*
286+
* @include [SelectingColumns.ColumnGroupsAndNestedColumnsMention]
287+
*
288+
* ### Example
289+
* ```kotlin
290+
* // Select the first row for each city where the population is greater than 100 000.
291+
* df.pivot { city }.first { population > 100000 }
292+
* ```
293+
*
294+
* @param predicate A lambda expression used to select a value
295+
* that satisfies a condition specified in this expression.
296+
*
297+
* @return A [ReducedPivot] containing in each column the first row
298+
* that satisfies the [predicate], from the corresponding group (or a row with `null` values)
299+
*/
77300
public fun <T> Pivot<T>.first(predicate: RowFilter<T>): ReducedPivot<T> = reduce { firstOrNull(predicate) }
78301

79302
// endregion
80303

81304
// region PivotGroupBy
82305

306+
/**
307+
* Reduces this [PivotGroupBy] by selecting the first row from each combined [pivot] + [groupBy] group.
308+
*
309+
* Returns a [ReducedPivotGroupBy] containing the following matrix:
310+
* - one row per [groupBy] key (or keys set);
311+
* - one column group per [pivot] key, where each inner column corresponds to a value of that key;
312+
* - each combination of a [groupBy] key and a [pivot] key contains either the first row of the corresponding
313+
* dataframe formed by this pivot–group pair, or a row with `null` values if this dataframe is empty.
314+
*
315+
* The original [PivotGroupBy] column structure is preserved.
316+
* If the [PivotGroupBy] was created using multiple or nested keys
317+
* (e.g., via [and][PivotDsl.and] or [then][PivotDsl.then]),
318+
* the result will contain nested column groups reflecting that key structure,
319+
* with each group containing columns for the values of the corresponding key.
320+
*
321+
* Equivalent to `reduce { firstOrNull() }`.
322+
*
323+
* See also:
324+
* - [pivot], [Pivot.groupBy] and [GroupBy.pivot];
325+
* - common [reduce][PivotGroupBy.reduce].
326+
*
327+
* ### Example
328+
* ```kotlin
329+
* // Select the first student from each combination of faculty and enrollment year.
330+
* students.pivot { faculty }.groupBy { enrollmentYear }.first()
331+
* ```
332+
*
333+
* @return A [ReducedPivotGroupBy] containing in each combination of a [groupBy] key and a [pivot] key either
334+
* the first row of the corresponding dataframe formed by this pivot–group pair,
335+
* or a row with `null` values if this dataframe is empty.
336+
*/
83337
public fun <T> PivotGroupBy<T>.first(): ReducedPivotGroupBy<T> = reduce { firstOrNull() }
84338

339+
/**
340+
* Reduces this [PivotGroupBy] by selecting from each combined [pivot] + [groupBy] group
341+
* the first row satisfying the given [predicate].
342+
*
343+
* Returns a [ReducedPivotGroupBy] containing the following matrix:
344+
* - one row per [groupBy] key (or keys set);
345+
* - one column group per [pivot] key, where each inner column corresponds to a value of that key;
346+
* - each combination of a [groupBy] key and a [pivot] key contains either the first matching the [predicate] row
347+
* of the corresponding dataframe formed by this pivot–group pair,
348+
* or a row with `null` values if this dataframe does not contain any rows matching the [predicate].
349+
*
350+
* The original [PivotGroupBy] column structure is preserved.
351+
* If the [PivotGroupBy] was created using multiple or nested keys
352+
* (e.g., via [and][PivotDsl.and] or [then][PivotDsl.then]),
353+
* the result will contain nested column groups reflecting that key structure,
354+
* with each group containing columns for the values of the corresponding key.
355+
*
356+
* Equivalent to `reduce { firstOrNull(predicate) }`.
357+
*
358+
* See also:
359+
* - [pivot], [Pivot.groupBy] and [GroupBy.pivot];
360+
* - common [reduce][PivotGroupBy.reduce].
361+
*
362+
* {@include [RowFilterDescription]}
363+
*
364+
* @include [SelectingColumns.ColumnGroupsAndNestedColumnsMention]
365+
*
366+
* ### Example
367+
* ```kotlin
368+
* // From each combination of faculty and enrollment year select the first student older than 21.
369+
* students.pivot { faculty }.groupBy { enrollmentYear }.first { age > 21 }
370+
* ```
371+
*
372+
* @param predicate A lambda expression used to select a value
373+
* that satisfies a condition specified in this expression.
374+
*
375+
* @return A [ReducedPivotGroupBy] containing in each combination of a [groupBy] key and a [pivot] key either
376+
* the first matching the [predicate] row of the corresponding dataframe formed by this pivot–group pair,
377+
* or a row with `null` values if this dataframe does not contain any rows matching the [predicate].
378+
*/
85379
public fun <T> PivotGroupBy<T>.first(predicate: RowFilter<T>): ReducedPivotGroupBy<T> =
86380
reduce { firstOrNull(predicate) }
87381

0 commit comments

Comments
 (0)