///|
/// Drop rows where every declared column is empty or missing.
pub fn table_drop_empty_rows(table : CsvTable) -> CsvTable {
let rows : Array[Array[String]] = Array::new()
for row in table.rows {
if !cleaning_row_empty(row, table.headers.length()) {
rows.push(row)
}
}
{ headers: table.headers, rows }
}
///|
/// Drop columns where every row is empty or missing.
pub fn table_drop_empty_columns(table : CsvTable) -> CsvTable {
let keep : Array[String] = Array::new()
for index in 0.. CsvTable {
let rows : Array[Array[String]] = Array::new()
for row in table.rows {
let next : Array[String] = Array::new()
for field in row {
if field == old_value {
next.push(new_value)
} else {
next.push(field)
}
}
rows.push(next)
}
{ headers: table.headers, rows }
}
///|
/// Replace exact cell values in one column.
pub fn table_replace_in_column(
table : CsvTable,
column : String,
old_value : String,
new_value : String,
) -> CsvTable {
match cleaning_column_index(table.headers, column) {
Some(index) => {
let rows : Array[Array[String]] = Array::new()
for row in table.rows {
let next = row.copy()
if index < next.length() && next[index] == old_value {
next[index] = new_value
}
rows.push(next)
}
{ headers: table.headers, rows }
}
None => table
}
}
///|
/// Convert values in one column to lowercase.
pub fn table_lowercase_column(table : CsvTable, column : String) -> CsvTable {
match cleaning_column_index(table.headers, column) {
Some(index) =>
cleaning_map_column(table, index, fn(value) { value.to_lower() })
None => table
}
}
///|
/// Convert values in one column to uppercase.
pub fn table_uppercase_column(table : CsvTable, column : String) -> CsvTable {
match cleaning_column_index(table.headers, column) {
Some(index) =>
cleaning_map_column(table, index, fn(value) { value.to_upper() })
None => table
}
}
///|
/// Add a target column containing the first non-empty value among source columns.
pub fn table_coalesce_columns(
table : CsvTable,
columns : Array[String],
target : String,
) -> CsvTable {
let indices : Array[Int] = Array::new()
for column in columns {
match cleaning_column_index(table.headers, column) {
Some(index) => indices.push(index)
None => ()
}
}
table_add_column(table, target, fn(row) {
for index in indices {
let value = cleaning_cell_at(row, index)
if !value.is_empty() {
return value
}
}
""
})
}
///|
/// Normalize common boolean spellings to `true` or `false` in one column.
pub fn table_normalize_boolean(table : CsvTable, column : String) -> CsvTable {
match cleaning_column_index(table.headers, column) {
Some(index) =>
cleaning_map_column(table, index, fn(value) {
match value.to_lower() {
"true" | "1" | "yes" | "y" | "on" => "true"
"false" | "0" | "no" | "n" | "off" => "false"
_ => value
}
})
None => table
}
}
///|
/// Build a per-column missingness summary table.
pub fn table_missing_summary(table : CsvTable) -> CsvTable {
let rows : Array[Array[String]] = Array::new()
for index in 0.. CsvTable {
let headers : Array[String] = Array::new()
headers.push(column)
for header in table.headers {
headers.push(header)
}
let rows : Array[Array[String]] = Array::new()
for i in 0.. String,
) -> CsvTable {
let rows : Array[Array[String]] = Array::new()
for row in table.rows {
let next = row.copy()
if index < next.length() {
next[index] = transform(next[index])
}
rows.push(next)
}
{ headers: table.headers, rows }
}
///|
fn cleaning_column_index(headers : Array[String], column : String) -> Int? {
for i in 0.. String {
if index >= 0 && index < row.length() {
row[index]
} else {
""
}
}
///|
fn cleaning_row_empty(row : Array[String], width : Int) -> Bool {
for i in 0..