@@ -8,6 +8,7 @@ import org.jetbrains.kotlinx.dataframe.api.asDataColumn
88import org.jetbrains.kotlinx.dataframe.api.cast
99import org.jetbrains.kotlinx.dataframe.api.isColumnGroup
1010import org.jetbrains.kotlinx.dataframe.api.pathOf
11+ import org.jetbrains.kotlinx.dataframe.api.toPath
1112import org.jetbrains.kotlinx.dataframe.columns.ColumnGroup
1213import org.jetbrains.kotlinx.dataframe.columns.ColumnPath
1314import org.jetbrains.kotlinx.dataframe.columns.ColumnReference
@@ -21,6 +22,7 @@ import org.jetbrains.kotlinx.dataframe.impl.columns.addPath
2122import org.jetbrains.kotlinx.dataframe.impl.columns.missing.MissingColumnGroup
2223import org.jetbrains.kotlinx.dataframe.impl.columns.missing.MissingDataColumn
2324import org.jetbrains.kotlinx.dataframe.nrow
25+ import kotlin.collections.map
2426
2527private fun <T > DataFrame<T>.unbox (): DataFrame <T > =
2628 when (this ) {
@@ -47,9 +49,7 @@ internal open class DataFrameReceiver<T>(
4749 host = this @DataFrameReceiver,
4850 ).asDataColumn().cast()
4951
50- UnresolvedColumnsPolicy .Fail -> error(
51- " Column '${path.joinToString()} ' not found among ${df.columnNames()} ." ,
52- )
52+ UnresolvedColumnsPolicy .Fail -> error(formatMissingColumnMessage(path))
5353 }
5454
5555 is MissingDataColumn -> this
@@ -59,6 +59,43 @@ internal open class DataFrameReceiver<T>(
5959 else -> this
6060 }
6161
62+ // Context:
63+ // it's strange that we have to reverse-search why the column is missing
64+ // would be nice to "fail fast" exactly where resolve failed, knowing the current path and parent.
65+ // but it's very unclear what to do with resolveSingle.
66+ // at first glance: a lot of changes.
67+ @Suppress(" FoldInitializerAndIfToElvis" )
68+ private fun formatMissingColumnMessage (path : ColumnPath ): String {
69+ val fullPath = path.joinToString()
70+
71+ for (depth in path.indices) {
72+ val currentPath = path.slice(0 .. depth).toPath()
73+ val currentPathString = currentPath.joinToString()
74+ val column = df.getColumnOrNull(currentPath)
75+ if (column == null ) {
76+ return if (depth == 0 ) {
77+ " Column '$currentPathString ' not found among ${df.columnNames()} ."
78+ } else {
79+ val parentPath = currentPath.dropLast()
80+ val parentPathString = parentPath.joinToString()
81+ val parentColumn = df.getColumnOrNull(parentPath)
82+ if (parentColumn != null && parentColumn.isColumnGroup()) {
83+ " Column '$currentPathString ' not found among columns of '$parentPathString ': ${parentColumn.columnNames()} ."
84+ } else {
85+ " Column '$currentPathString ' not found among ${df.columnNames()} ."
86+ }
87+ }
88+ }
89+
90+ if (depth != path.lastIndex) {
91+ if (! column.isColumnGroup()) {
92+ return " Column '$fullPath ' cannot be resolved: '$currentPathString ' is not a column group."
93+ }
94+ }
95+ }
96+ return " Column '$fullPath ' not found among ${df.columnNames()} ."
97+ }
98+
6299 override fun getColumnOrNull (name : String ) = df.getColumnOrNull(name).check(pathOf(name))
63100
64101 override fun getColumnOrNull (index : Int ) = df.getColumnOrNull(index).check(pathOf(" " ))
0 commit comments