Skip to content

Commit 4bf8219

Browse files
committed
dfs -> allChildren
topDfs -> topmostChildren top -> roots
1 parent a47f67e commit 4bf8219

File tree

12 files changed

+306
-92
lines changed

12 files changed

+306
-92
lines changed

core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/ColumnsSelectionDsl.kt

Lines changed: 66 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4531,36 +4531,75 @@ public interface ColumnsSelectionDsl<out T> : ColumnSelectionDsl<T>, SingleColum
45314531
public fun <C> ColumnSet<C>.take(n: Int): ColumnSet<C> = transform { it.take(n) }
45324532
public fun <C> ColumnSet<C>.dropLast(n: Int = 1): ColumnSet<C> = transform { it.dropLast(n) }
45334533
public fun <C> ColumnSet<C>.takeLast(n: Int): ColumnSet<C> = transform { it.takeLast(n) }
4534-
public fun <C> ColumnSet<C>.top(): ColumnSet<C> = transform { it.top() }
4534+
4535+
@Deprecated("Use roots() instead", ReplaceWith("roots()"))
4536+
public fun <C> ColumnSet<C>.top(): ColumnSet<C> = roots()
4537+
4538+
/**
4539+
* ## Roots
4540+
*
4541+
* Returns a sub-set of columns that are roots of the trees of columns.
4542+
*
4543+
* In practice, this means that if a column in [this] is a child of another column in [this],
4544+
* it will not be included in the result.
4545+
*
4546+
* TODO example and SingleColumn case
4547+
*/
4548+
public fun <C> ColumnSet<C>.roots(): ColumnSet<C> = transform { it.roots() }
4549+
45354550
public fun <C> ColumnSet<C>.takeWhile(predicate: ColumnFilter<C>): ColumnSet<C> =
45364551
transform { it.takeWhile(predicate) }
45374552

45384553
public fun <C> ColumnSet<C>.takeLastWhile(predicate: ColumnFilter<C>): ColumnSet<C> =
45394554
transform { it.takeLastWhile(predicate) }
45404555

4541-
public fun <C> ColumnSet<C>.filter(predicate: ColumnFilter<C>): ColumnSet<C> =
4542-
colsInternal(predicate as ColumnFilter<*>) as ColumnSet<C>
4556+
public fun <C> ColumnSet<C>.filter(predicate: ColumnFilter<C>): TransformableColumnSet<C> =
4557+
colsInternal(predicate as ColumnFilter<*>) as TransformableColumnSet<C>
45434558

4544-
public fun SingleColumn<*>.nameContains(text: CharSequence): ColumnSet<*> = cols { it.name.contains(text) }
4545-
public fun <C> ColumnSet<C>.nameContains(text: CharSequence): ColumnSet<C> = cols { it.name.contains(text) }
4546-
public fun SingleColumn<*>.nameContains(regex: Regex): ColumnSet<*> = cols { it.name.contains(regex) }
4547-
public fun <C> ColumnSet<C>.nameContains(regex: Regex): ColumnSet<C> = cols { it.name.contains(regex) }
4548-
public fun SingleColumn<*>.startsWith(prefix: CharSequence): ColumnSet<*> = cols { it.name.startsWith(prefix) }
4549-
public fun <C> ColumnSet<C>.startsWith(prefix: CharSequence): ColumnSet<C> = cols { it.name.startsWith(prefix) }
4550-
public fun SingleColumn<*>.endsWith(suffix: CharSequence): ColumnSet<*> = cols { it.name.endsWith(suffix) }
4551-
public fun <C> ColumnSet<C>.endsWith(suffix: CharSequence): ColumnSet<C> = cols { it.name.endsWith(suffix) }
4559+
public fun SingleColumn<*>.nameContains(text: CharSequence): TransformableColumnSet<*> =
4560+
cols { it.name.contains(text) }
45524561

4553-
public fun <C> ColumnSet<C>.except(vararg other: ColumnSet<*>): ColumnSet<*> = except(other.toColumnSet())
4554-
public fun <C> ColumnSet<C>.except(vararg other: String): ColumnSet<*> = except(other.toColumnSet())
4562+
public fun <C> ColumnSet<C>.nameContains(text: CharSequence): TransformableColumnSet<C> =
4563+
cols { it.name.contains(text) }
45554564

4556-
public fun <C> ColumnSet<C?>.withoutNulls(): ColumnSet<C> =
4557-
transform { it.filter { !it.hasNulls() } } as ColumnSet<C>
4565+
public fun SingleColumn<*>.nameContains(regex: Regex): TransformableColumnSet<*> = cols { it.name.contains(regex) }
4566+
public fun <C> ColumnSet<C>.nameContains(regex: Regex): TransformableColumnSet<C> = cols { it.name.contains(regex) }
4567+
public fun SingleColumn<*>.startsWith(prefix: CharSequence): TransformableColumnSet<*> =
4568+
cols { it.name.startsWith(prefix) }
4569+
4570+
public fun <C> ColumnSet<C>.startsWith(prefix: CharSequence): TransformableColumnSet<C> =
4571+
cols { it.name.startsWith(prefix) }
4572+
4573+
public fun SingleColumn<*>.endsWith(suffix: CharSequence): TransformableColumnSet<*> =
4574+
cols { it.name.endsWith(suffix) }
4575+
4576+
public fun <C> ColumnSet<C>.endsWith(suffix: CharSequence): TransformableColumnSet<C> =
4577+
cols { it.name.endsWith(suffix) }
4578+
4579+
public fun <C> ColumnSet<C>.except(vararg other: ColumnSet<*>): TransformableColumnSet<*> = except(other.toColumnSet())
4580+
public fun <C> ColumnSet<C>.except(vararg other: String): TransformableColumnSet<*> = except(other.toColumnSet())
4581+
4582+
public fun <C> ColumnSet<C?>.withoutNulls(): TransformableColumnSet<C> =
4583+
transform { it.filter { !it.hasNulls() } } as TransformableColumnSet<C>
4584+
4585+
public infix fun <C> ColumnSet<C>.except(other: ColumnSet<*>): TransformableColumnSet<*> =
4586+
// createColumnSet {
4587+
// [email protected](it).allColumnsExcept(other.resolve(it))
4588+
// }
4589+
createTransformableColumnSet(
4590+
resolver = { context ->
4591+
this@except.resolve(context).allColumnsExcept(other.resolve(context))
4592+
},
4593+
transformResolve = { context, transformer ->
4594+
transformer.transform(this@except)
4595+
.resolve(context)
4596+
.allColumnsExcept(other.resolve(context))
4597+
},
4598+
)
45584599

4559-
public infix fun <C> ColumnSet<C>.except(other: ColumnSet<*>): ColumnSet<*> =
4560-
createColumnSet { resolve(it).allColumnsExcept(other.resolve(it)) }
45614600

4562-
public infix fun <C> ColumnSet<C>.except(selector: ColumnsSelector<T, *>): ColumnSet<C> =
4563-
except(selector.toColumns()) as ColumnSet<C>
4601+
public infix fun <C> ColumnSet<C>.except(selector: ColumnsSelector<T, *>): TransformableColumnSet<C> =
4602+
except(selector.toColumns()) as TransformableColumnSet<C>
45644603

45654604
public operator fun <C> ColumnsSelector<T, C>.invoke(): ColumnSet<C> =
45664605
this(this@ColumnsSelectionDsl, this@ColumnsSelectionDsl)
@@ -4971,7 +5010,10 @@ private interface CommonColsOfDocs {
49715010
* @param [filter] an optional filter function that takes a column of type [C] and returns `true` if the column should be included.
49725011
* @return A [ColumnSet][org.jetbrains.kotlinx.dataframe.columns.ColumnSet] containing the columns of given type that were included by [filter].
49735012
*/
4974-
public fun <C> ColumnSet<*>.colsOf(type: KType, filter: (DataColumn<C>) -> Boolean = { true }): TransformableColumnSet<C> =
5013+
public fun <C> ColumnSet<*>.colsOf(
5014+
type: KType,
5015+
filter: (DataColumn<C>) -> Boolean = { true },
5016+
): TransformableColumnSet<C> =
49755017
colsInternal { it.isSubtypeOf(type) && filter(it.cast()) } as TransformableColumnSet<C>
49765018

49775019
/**
@@ -5053,7 +5095,10 @@ public inline fun <reified C> ColumnSet<*>.colsOf(noinline filter: (DataColumn<C
50535095
* @param [filter] an optional filter function that takes a column of type [C] and returns `true` if the column should be included.
50545096
* @return A [ColumnSet][org.jetbrains.kotlinx.dataframe.columns.ColumnSet] containing the columns of given type that were included by [filter].
50555097
*/
5056-
public fun <C> SingleColumn<*>.colsOf(type: KType, filter: (DataColumn<C>) -> Boolean = { true }): TransformableColumnSet<C> =
5098+
public fun <C> SingleColumn<*>.colsOf(
5099+
type: KType,
5100+
filter: (DataColumn<C>) -> Boolean = { true },
5101+
): TransformableColumnSet<C> =
50575102
colsInternal { it.isSubtypeOf(type) && filter(it.cast()) } as TransformableColumnSet<C>
50585103

50595104
/**

core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/flatten.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ import org.jetbrains.kotlinx.dataframe.impl.columns.toColumnSet
1515
internal fun <T, C> DataFrame<T>.flattenImpl(
1616
columns: ColumnsSelector<T, C>,
1717
): DataFrame<T> {
18-
val rootColumns = getColumnsWithPaths { columns.toColumnSet().filter { it.isColumnGroup() }.top() }
18+
val rootColumns = getColumnsWithPaths {
19+
columns.toColumnSet().filter { it.isColumnGroup() }.roots()
20+
}
1921
val rootPrefixes = rootColumns.map { it.path }.toSet()
2022
val nameGenerators = rootPrefixes.map { it.dropLast() }.distinct().associateWith { path ->
2123
val usedNames = get(path).asColumnGroup().columns().filter { path + it.name() !in rootPrefixes }.map { it.name() }

core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/columns/Utils.kt

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -167,25 +167,31 @@ internal fun <C> ColumnsContainer<*>.getColumn(path: ColumnPath, policy: Unresol
167167
UnresolvedColumnsPolicy.Create -> DataColumn.empty().cast<C>()
168168
}
169169

170-
internal fun <T> List<ColumnWithPath<T>>.top(): List<ColumnWithPath<T>> {
171-
val root = TreeNode.createRoot<ColumnWithPath<T>?>(null)
172-
forEach { root.put(it.path, it) }
173-
return root.topDfs { it.data != null }.map { it.data!! }
170+
/**
171+
* Returns a sub-list of columns that are roots of the trees of columns.
172+
*
173+
* In practice, this means that if a column in [this] is a child of another column in [this],
174+
* it will not be included in the result.
175+
*/
176+
internal fun <T> List<ColumnWithPath<T>>.roots(): List<ColumnWithPath<T>> {
177+
val emptyRoot = TreeNode.createRoot<ColumnWithPath<T>?>(data = null)
178+
this.forEach { emptyRoot.put(it.path, it) }
179+
return emptyRoot.topmostChildren { it.data != null }.map { it.data!! }
174180
}
175181

176182
internal fun List<ColumnWithPath<*>>.allColumnsExcept(columns: Iterable<ColumnWithPath<*>>): List<ColumnWithPath<*>> {
177183
if (isEmpty()) return emptyList()
178184
val fullTree = collectTree()
179185
columns.forEach {
180186
var node = fullTree.getOrPut(it.path).asNullable()
181-
node?.dfs()?.forEach { it.data = null }
187+
node?.allChildren()?.forEach { it.data = null }
182188
while (node != null) {
183189
node.data = null
184190
node = node.parent
185191
}
186192
}
187-
val dfs = fullTree.topDfs { it.data != null }
188-
return dfs.map { it.data!!.addPath(it.pathFromRoot()) }
193+
val subtrees = fullTree.topmostChildren { it.data != null }
194+
return subtrees.map { it.data!!.addPath(it.pathFromRoot()) }
189195
}
190196

191197
internal fun KType.toColumnKind(): ColumnKind = jvmErasure.let {

core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/columns/constructors.kt

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -144,10 +144,24 @@ internal fun <T> createColumn(values: Iterable<T>, suggestedType: KType, guessTy
144144

145145
// region create Columns
146146

147-
internal fun <C> createColumnSet(resolver: (ColumnResolutionContext) -> List<ColumnWithPath<C>>): ColumnSet<C> =
148-
object : ColumnSet<C> {
149-
override fun resolve(context: ColumnResolutionContext) = resolver(context)
150-
}
147+
internal fun <C> createColumnSet(
148+
resolver: (context: ColumnResolutionContext) -> List<ColumnWithPath<C>>,
149+
): ColumnSet<C> = object : ColumnSet<C> {
150+
override fun resolve(context: ColumnResolutionContext) = resolver(context)
151+
}
152+
153+
internal fun <C> createTransformableColumnSet(
154+
resolver: (context: ColumnResolutionContext) -> List<ColumnWithPath<C>>,
155+
transformResolve: (context: ColumnResolutionContext, transformer: ColumnSetTransformer) -> List<ColumnWithPath<C>>,
156+
): TransformableColumnSet<C> = object : TransformableColumnSet<C> {
157+
override fun resolve(context: ColumnResolutionContext) = resolver(context)
158+
159+
override fun transformResolve(
160+
context: ColumnResolutionContext,
161+
transformer: ColumnSetTransformer,
162+
): List<ColumnWithPath<C>> = transformResolve(context, transformer)
163+
}
164+
151165

152166
// region toColumnSet
153167

core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/columns/tree/TreeNode.kt

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ internal class TreeNode<T>(
66
override val name: String,
77
override val depth: Int,
88
override var data: T,
9-
override val parent: TreeNode<T>? = null
9+
override val parent: TreeNode<T>? = null,
1010
) : ReadonlyTreeNode<T> {
1111

1212
companion object {
@@ -28,7 +28,7 @@ internal class TreeNode<T>(
2828
fun pathFromRoot(): ColumnPath {
2929
val path = mutableListOf<String>()
3030
var node: TreeNode<T>? = this
31-
while (node != null && node.parent != null) {
31+
while (node?.parent != null) {
3232
path.add(node.name)
3333
node = node.parent
3434
}
@@ -48,19 +48,34 @@ internal class TreeNode<T>(
4848
return addChild(childName, createData())
4949
}
5050

51-
fun dfs(enterCondition: (TreeNode<T>) -> Boolean = { true }, yieldCondition: (TreeNode<T>) -> Boolean = { true }): List<TreeNode<T>> {
51+
@Deprecated("Use allChildren instead", ReplaceWith("allChildren(enterCondition, yieldCondition)"))
52+
fun dfs(
53+
enterCondition: (TreeNode<T>) -> Boolean = { true },
54+
yieldCondition: (TreeNode<T>) -> Boolean = { true },
55+
): List<TreeNode<T>> = allChildren(enterCondition, yieldCondition)
56+
57+
/**
58+
* Traverses the tree in depth-first order and returns all nodes that satisfy [yieldCondition].
59+
* If [enterCondition] returns false for a node, its children are not traversed.
60+
* By default, all nodes are traversed and all nodes are returned.
61+
*/
62+
fun allChildren(
63+
enterCondition: (TreeNode<T>) -> Boolean = { true },
64+
yieldCondition: (TreeNode<T>) -> Boolean = { true },
65+
): List<TreeNode<T>> {
5266
val result = mutableListOf<TreeNode<T>>()
53-
fun doDfs(node: TreeNode<T>) {
67+
68+
fun traverse(node: TreeNode<T>) {
5469
if (yieldCondition(node)) {
5570
result.add(node)
5671
}
5772
if (enterCondition(node)) {
5873
node.children.forEach {
59-
doDfs(it)
74+
traverse(it)
6075
}
6176
}
6277
}
63-
doDfs(this)
78+
traverse(this)
6479
return result
6580
}
6681
}

core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/columns/tree/Utils.kt

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,22 @@ internal fun <T> TreeNode<T>.getOrPut(path: ColumnPath, createData: (ColumnPath)
2929
return node
3030
}
3131

32-
internal fun <T> TreeNode<T>.topDfs(yieldCondition: (TreeNode<T>) -> Boolean): List<TreeNode<T>> = dfs(enterCondition = { !yieldCondition(it) }, yieldCondition = yieldCondition)
32+
/**
33+
* Traverses all children in the tree in depth-first order and returns the top-most nodes that satisfy
34+
* [yieldCondition]. This means that if a node satisfies [yieldCondition], its children are not traversed, regardless of
35+
* whether they satisfy [yieldCondition] or not.
36+
*/
37+
internal fun <T> TreeNode<T>.topmostChildren(yieldCondition: (TreeNode<T>) -> Boolean): List<TreeNode<T>> =
38+
allChildren(
39+
enterCondition = { !yieldCondition(it) },
40+
yieldCondition = yieldCondition,
41+
)
3342

34-
internal fun <T> TreeNode<T>.topDfsExcluding(excludeRoot: TreeNode<*>): List<TreeNode<T>> {
43+
@Deprecated("Use topmostChildren instead", ReplaceWith("topmostChildren(yieldCondition)"))
44+
internal fun <T> TreeNode<T>.topDfs(yieldCondition: (TreeNode<T>) -> Boolean): List<TreeNode<T>> =
45+
topmostChildren(yieldCondition)
46+
47+
internal fun <T> TreeNode<T>.topmostChildrenExcluding(excludeRoot: TreeNode<*>): List<TreeNode<T>> {
3548
val result = mutableListOf<TreeNode<T>>()
3649
fun doDfs(node: TreeNode<T>, exclude: TreeNode<*>) {
3750
if (exclude.children.isNotEmpty()) {
@@ -48,19 +61,31 @@ internal fun <T> TreeNode<T>.topDfsExcluding(excludeRoot: TreeNode<*>): List<Tre
4861
return result
4962
}
5063

51-
internal fun <T> TreeNode<T?>.dfsNotNull() = dfs { it.data != null }.map { it as TreeNode<T> }
52-
internal fun <T> TreeNode<T?>.dfsTopNotNull() = dfs(enterCondition = { it.data == null }, yieldCondition = { it.data != null }).map { it as TreeNode<T> }
64+
internal fun <T> TreeNode<T?>.allChildrenNotNull(): List<TreeNode<T>> =
65+
allChildren { it.data != null } as List<TreeNode<T>>
66+
67+
internal fun <T> TreeNode<T?>.topmostChildrenNotNull() =
68+
topmostChildren { it.data != null } as List<TreeNode<T>>
69+
70+
internal fun TreeNode<ColumnPosition>.allRemovedColumns() =
71+
allChildren { it.data.wasRemoved && it.data.column != null }
72+
73+
internal fun TreeNode<ColumnPosition>.allWithColumns() =
74+
allChildren { it.data.column != null }
5375

54-
internal fun TreeNode<ColumnPosition>.allRemovedColumns() = dfs { it.data.wasRemoved && it.data.column != null }
55-
internal fun TreeNode<ColumnPosition>.allWithColumns() = dfs { it.data.column != null }
5676
internal fun Iterable<ColumnWithPath<*>>.flattenRecursively(): List<ColumnWithPath<*>> {
5777
val result = mutableListOf<ColumnWithPath<*>>()
78+
5879
fun flattenRecursively(cols: Iterable<ColumnWithPath<*>>) {
5980
cols.forEach {
6081
result.add(it)
6182
val path = it.path
6283
if (it.data.isColumnGroup()) {
63-
flattenRecursively(it.data.asColumnGroup().columns().map { it.addPath(path + it.name()) })
84+
flattenRecursively(
85+
it.data.asColumnGroup()
86+
.columns()
87+
.map { it.addPath(path + it.name()) }
88+
)
6489
}
6590
}
6691
}

0 commit comments

Comments
 (0)