diff --git a/c/cli/mlrcli.c b/c/cli/mlrcli.c index 9b4d8d97f..dfe5daf4f 100644 --- a/c/cli/mlrcli.c +++ b/c/cli/mlrcli.c @@ -66,6 +66,7 @@ static mapper_setup_t* mapper_lookup_table[] = { &mapper_nothing_setup, &mapper_put_setup, &mapper_regularize_setup, + &mapper_remove_empty_columns_setup, &mapper_rename_setup, &mapper_reorder_setup, &mapper_repeat_setup, diff --git a/c/mapping/Makefile.am b/c/mapping/Makefile.am index bbc01a3f7..b655dc4c2 100644 --- a/c/mapping/Makefile.am +++ b/c/mapping/Makefile.am @@ -25,6 +25,7 @@ libmapping_la_SOURCES= \ mapper_fraction.c \ mapper_put_or_filter.c \ mapper_regularize.c \ + mapper_remove_empty_columns.c \ mapper_rename.c \ mapper_reorder.c \ mapper_repeat.c \ diff --git a/c/mapping/mapper_remove_empty_columns.c b/c/mapping/mapper_remove_empty_columns.c new file mode 100644 index 000000000..735fab40d --- /dev/null +++ b/c/mapping/mapper_remove_empty_columns.c @@ -0,0 +1,104 @@ +#include +#include "lib/mlrutil.h" +#include "containers/sllv.h" +#include "containers/lhmsi.h" +#include "mapping/mappers.h" + +typedef struct _mapper_remove_empty_columns_state_t { + sllv_t* precords; + lhmsi_t* pnames_with_nonempty_values; +} mapper_remove_empty_columns_state_t; + +static void mapper_remove_empty_columns_usage(FILE* o, char* argv0, char* verb); +static mapper_t* mapper_remove_empty_columns_parse_cli(int* pargi, int argc, char** argv, + cli_reader_opts_t* _, cli_writer_opts_t* __); +static mapper_t* mapper_remove_empty_columns_alloc(); +static void mapper_remove_empty_columns_free(mapper_t* pmapper, context_t* _); +static sllv_t* mapper_remove_empty_columns_process(lrec_t* pinrec, context_t* pctx, void* pvstate); + +// ---------------------------------------------------------------- +mapper_setup_t mapper_remove_empty_columns_setup = { + .verb = "remove-empty-columns", + .pusage_func = mapper_remove_empty_columns_usage, + .pparse_func = mapper_remove_empty_columns_parse_cli, + .ignores_input = FALSE, +}; + +// ---------------------------------------------------------------- +static void mapper_remove_empty_columns_usage(FILE* o, char* argv0, char* verb) { + fprintf(o, "Usage: %s %s\n", argv0, verb); + fprintf(o, "Omits fields which are empty on every input row. Non-streaming.\n"); +} + +static mapper_t* mapper_remove_empty_columns_parse_cli(int* pargi, int argc, char** argv, + cli_reader_opts_t* _, cli_writer_opts_t* __) +{ + if ((argc - *pargi) < 1) { + mapper_remove_empty_columns_usage(stderr, argv[0], argv[*pargi]); + return NULL; + } + mapper_t* pmapper = mapper_remove_empty_columns_alloc(); + *pargi += 1; + return pmapper; +} + +// ---------------------------------------------------------------- +static mapper_t* mapper_remove_empty_columns_alloc() { + mapper_t* pmapper = mlr_malloc_or_die(sizeof(mapper_t)); + + mapper_remove_empty_columns_state_t* pstate = mlr_malloc_or_die(sizeof(mapper_remove_empty_columns_state_t)); + pstate->precords = sllv_alloc(); + pstate->pnames_with_nonempty_values = lhmsi_alloc(); + + pmapper->pvstate = pstate; + pmapper->pprocess_func = mapper_remove_empty_columns_process; + pmapper->pfree_func = mapper_remove_empty_columns_free; + + return pmapper; +} + +static void mapper_remove_empty_columns_free(mapper_t* pmapper, context_t* _) { + mapper_remove_empty_columns_state_t* pstate = pmapper->pvstate; + // Free the container + sllv_free(pstate->precords); + lhmsi_free(pstate->pnames_with_nonempty_values); + free(pstate); + free(pmapper); +} + +// ---------------------------------------------------------------- +static sllv_t* mapper_remove_empty_columns_process(lrec_t* pinrec, context_t* pctx, void* pvstate) { + mapper_remove_empty_columns_state_t* pstate = pvstate; + if (pinrec != NULL) { + // The caller will free the outrecs + sllv_append(pstate->precords, pinrec); + for (lrece_t* pe = pinrec->phead; pe != NULL; pe = pe->pnext) { + if (pe->value[0] != 0) { + if (!lhmsi_has_key(pstate->pnames_with_nonempty_values, pe->key)) { + lhmsi_put(pstate->pnames_with_nonempty_values, mlr_strdup_or_die(pe->key), 1, FREE_ENTRY_KEY); + } + } + } + return NULL; + } + else { + for (sllve_t* pe = pstate->precords->phead; pe != NULL; pe = pe->pnext) { + lrec_t* prec = pe->pvvalue; + lrece_t* pf = prec->phead; + while (pf != NULL) { + if (lhmsi_has_key(pstate->pnames_with_nonempty_values, pf->key)) { + pf = pf->pnext; + } else { + lrece_t* pnext = pf->pnext; + lrec_unlink_and_free(prec, pf); + pf = pnext; + } + } + } + + sllv_append(pstate->precords, NULL); + sllv_t* retval = pstate->precords; + pstate->precords = sllv_alloc(); + return retval; + } +} diff --git a/c/mapping/mappers.h b/c/mapping/mappers.h index d060da10d..8d275c240 100644 --- a/c/mapping/mappers.h +++ b/c/mapping/mappers.h @@ -32,6 +32,7 @@ extern mapper_setup_t mapper_nest_setup; extern mapper_setup_t mapper_nothing_setup; extern mapper_setup_t mapper_put_setup; extern mapper_setup_t mapper_regularize_setup; +extern mapper_setup_t mapper_remove_empty_columns_setup; extern mapper_setup_t mapper_rename_setup; extern mapper_setup_t mapper_reorder_setup; extern mapper_setup_t mapper_repeat_setup;