size_t n_refs;
struct ctables_category *cats;
size_t n_cats;
- bool show_empty;
};
struct ctables_category
ctables_categories_equal (const struct ctables_categories *a,
const struct ctables_categories *b)
{
- if (a->n_cats != b->n_cats || a->show_empty != b->show_empty)
+ if (a->n_cats != b->n_cats)
return false;
for (size_t i = 0; i < a->n_cats; i++)
/* Indexed by variable dictionary index. */
struct ctables_categories **categories;
size_t n_categories;
+ bool *show_empty;
double cilevel;
const struct variable *v0 = t->clabels_example;
int width = var_get_width (v0);
- struct ctables_categories *c0 = t->categories[var_get_dict_index (v0)];
- if (c0->show_empty)
+ size_t i0 = var_get_dict_index (v0);
+ struct ctables_categories *c0 = t->categories[i0];
+ if (t->show_empty[i0])
{
const struct val_labs *val_labs = var_get_value_labels (v0);
for (const struct val_lab *vl = val_labs_first (val_labs); vl;
for (size_t i = 0; i < t->n_categories; i++)
ctables_categories_unref (t->categories[i]);
free (t->categories);
+ free (t->show_empty);
for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
{
& (FMT_CAT_DATE | FMT_CAT_TIME | FMT_CAT_DATE_COMPONENT)));
struct ctables_categories *c = xmalloc (sizeof *c);
- *c = (struct ctables_categories) { .n_refs = n_vars, .show_empty = true };
- for (size_t i = 0; i < n_vars; i++)
- {
- struct ctables_categories **cp
- = &t->categories[var_get_dict_index (vars[i])];
- ctables_categories_unref (*cp);
- *cp = c;
- }
+ *c = (struct ctables_categories) { .n_refs = 1 };
+
+ bool set_categories = false;
size_t allocated_cats = 0;
int cats_start_ofs = -1;
int cats_end_ofs = -1;
if (lex_match (lexer, T_LBRACK))
{
+ set_categories = true;
cats_start_ofs = lex_ofs (lexer);
do
{
{
if (!c->n_cats && lex_match_id (lexer, "ORDER"))
{
+ set_categories = true;
lex_match (lexer, T_EQUALS);
if (lex_match_id (lexer, "A"))
cat.sort_ascending = true;
}
else if (!c->n_cats && lex_match_id (lexer, "KEY"))
{
+ set_categories = true;
key_start_ofs = lex_ofs (lexer) - 1;
lex_match (lexer, T_EQUALS);
if (lex_match_id (lexer, "VALUE"))
}
else if (!c->n_cats && lex_match_id (lexer, "MISSING"))
{
+ set_categories = true;
lex_match (lexer, T_EQUALS);
if (lex_match_id (lexer, "INCLUDE"))
cat.include_missing = true;
}
else if (lex_match_id (lexer, "TOTAL"))
{
+ set_categories = true;
lex_match (lexer, T_EQUALS);
if (!parse_bool (lexer, &show_totals))
goto error;
else if (lex_match_id (lexer, "EMPTY"))
{
lex_match (lexer, T_EQUALS);
+
+ bool show_empty;
if (lex_match_id (lexer, "INCLUDE"))
- c->show_empty = true;
+ show_empty = true;
else if (lex_match_id (lexer, "EXCLUDE"))
- c->show_empty = false;
+ show_empty = false;
else
{
lex_error_expecting (lexer, "INCLUDE", "EXCLUDE");
goto error;
}
+
+ for (size_t i = 0; i < n_vars; i++)
+ t->show_empty[var_get_dict_index (vars[i])] = show_empty;
}
else
{
}
}
+ if (set_categories)
+ for (size_t i = 0; i < n_vars; i++)
+ {
+ struct ctables_categories **cp
+ = &t->categories[var_get_dict_index (vars[i])];
+ ctables_categories_unref (*cp);
+ *cp = c;
+ c->n_refs++;
+ }
+
+ ctables_categories_unref (c);
free (vars);
return true;
error:
+ ctables_categories_unref (c);
free (vars);
return false;
}
if (k != s->nests[a]->scale_idx)
{
const struct variable *var = s->nests[a]->vars[k];
- const struct ctables_categories *cats = s->table->categories[
- var_get_dict_index (var)];
- if (cats->show_empty)
+ size_t idx = var_get_dict_index (var);
+ const struct ctables_categories *cats = s->table->categories[idx];
+ if (s->table->show_empty[idx])
{
show_empty = true;
ctables_add_category_occurrences (var, &s->occurrences[a][k], cats);
.n_refs = n_vars,
.cats = cat,
.n_cats = 1,
- .show_empty = true,
};
struct ctables_categories **categories = xnmalloc (n_vars,
for (size_t i = 0; i < n_vars; i++)
categories[i] = c;
+ bool *show_empty = xmalloc (n_vars);
+ memset (show_empty, true, n_vars);
+
struct ctables_table *t = xmalloc (sizeof *t);
*t = (struct ctables_table) {
.ctables = ct,
.clabels_to_axis = PIVOT_AXIS_LAYER,
.categories = categories,
.n_categories = n_vars,
+ .show_empty = show_empty,
.cilevel = 95,
};
ct->tables[ct->n_tables++] = t;
])
AT_CLEANUP
+AT_SETUP([CTABLES categories and EMPTY])
+AT_CHECK([ln $top_srcdir/tests/language/stats/nhtsa.sav . || cp $top_srcdir/tests/language/stats/nhtsa.sav .])
+AT_DATA([ctables.sps],
+DATA LIST LIST NOTABLE /class datum size.
+BEGIN DATA
+1 1 1
+2 2 1
+1 3 1
+2 4 2
+1 5 2
+2 6 2
+END DATA.
+VARIABLE LEVEL class datum size (NOMINAL).
+FORMATS class datum size (F1.0).
+
+* The following are the same except for the order of the CATEGORIES commands.
+* The test checks that they produce the same resuls.
+CTABLES /TABLE=class > datum BY size
+ /CATEGORIES VARIABLES=ALL EMPTY=EXCLUDE
+ /CATEGORIES VARIABLES=size TOTAL=YES.
+CTABLES /TABLE=class > datum BY size
+ /CATEGORIES VARIABLES=size TOTAL=YES
+ /CATEGORIES VARIABLES=ALL EMPTY=EXCLUDE.
+])
+AT_CHECK([pspp ctables.sps -O box=unicode -O width=80], [0], [dnl
+ Custom Tables
+╭───────────────┬─────────────────╮
+│ │ size │
+│ ├─────┬─────┬─────┤
+│ │ 1 │ 2 │Total│
+│ ├─────┼─────┼─────┤
+│ │Count│Count│Count│
+├───────────────┼─────┼─────┼─────┤
+│class 1 datum 1│ 1│ │ 1│
+│ 3│ 1│ │ 1│
+│ 5│ │ 1│ 1│
+│ ╶─────────┼─────┼─────┼─────┤
+│ 2 datum 2│ 1│ │ 1│
+│ 4│ │ 1│ 1│
+│ 6│ │ 1│ 1│
+╰───────────────┴─────┴─────┴─────╯
+
+ Custom Tables
+╭───────────────┬─────────────────╮
+│ │ size │
+│ ├─────┬─────┬─────┤
+│ │ 1 │ 2 │Total│
+│ ├─────┼─────┼─────┤
+│ │Count│Count│Count│
+├───────────────┼─────┼─────┼─────┤
+│class 1 datum 1│ 1│ │ 1│
+│ 3│ 1│ │ 1│
+│ 5│ │ 1│ 1│
+│ ╶─────────┼─────┼─────┼─────┤
+│ 2 datum 2│ 1│ │ 1│
+│ 4│ │ 1│ 1│
+│ 6│ │ 1│ 1│
+╰───────────────┴─────┴─────┴─────╯
+])
+AT_CLEANUP
+
AT_SETUP([CTABLES sorting categories])
AT_CHECK([ln $top_srcdir/tests/language/stats/nhtsa.sav . || cp $top_srcdir/tests/language/stats/nhtsa.sav .])
AT_DATA([ctables.sps],