- if (var_is_alpha (arc->src_vars[i]))
- spec->items = hsh_create (2 * count, compare_alpha_value,
- hash_alpha_value, NULL, arc->src_vars[i]);
- else
- spec->items = hsh_create (2 * count, compare_numeric_value,
- hash_numeric_value, NULL, NULL);
-
- for (j = 0; *p; p++, j++)
- {
- struct arc_item *item = pool_alloc (trns->pool, sizeof *item);
- union arc_value *vp = *p;
-
- if (var_is_numeric (arc->src_vars[i]))
- item->from.f = vp->f;
- else
- item->from.c = pool_clone (trns->pool, vp->c,
- var_get_width (arc->src_vars[i]));
- item->to = arc->direction == ASCENDING ? j + 1 : count - j;
- hsh_force_insert (spec->items, item);
- }
+ /* Set print format. */
+ size_t n_items = hmap_count (&spec->items->ht);
+ char *longest_value = xasprintf ("%zu", n_items);
+ struct fmt_spec format = { .type = FMT_F, .w = strlen (longest_value) };
+ var_set_both_formats (spec->dst, &format);
+ free (longest_value);
+
+ /* Create array of pointers to items. */
+ struct arc_item **items = xmalloc (n_items * sizeof *items);
+ struct arc_item *item;
+ size_t j = 0;
+ HMAP_FOR_EACH (item, struct arc_item, hmap_node, &spec->items->ht)
+ items[j++] = item;
+ assert (j == n_items);
+
+ /* Sort array by value. */
+ sort (items, n_items, sizeof *items, compare_arc_items, &direction);
+
+ /* Assign recoded values in sorted order. */
+ for (j = 0; j < n_items; j++)
+ items[j]->to = j + 1;
+
+ if (print && (!group || i == 0))
+ {
+ struct pivot_value *title
+ = (group
+ ? pivot_value_new_text (N_("Recoding grouped variables."))
+ : spec->label && spec->label[0]
+ ? pivot_value_new_text_format (N_("Recoding %s into %s (%s)."),
+ spec->src_name,
+ var_get_name (spec->dst),
+ spec->label)
+ : pivot_value_new_text_format (N_("Recoding %s into %s."),
+ spec->src_name,
+ var_get_name (spec->dst)));
+ struct pivot_table *table = pivot_table_create__ (title, "Recoding");
+
+ pivot_dimension_create (
+ table, PIVOT_AXIS_COLUMN, N_("Attributes"),
+ N_("New Value"), N_("Value Label"));
+
+ struct pivot_dimension *old_values = pivot_dimension_create (
+ table, PIVOT_AXIS_ROW, N_("Old Value"));
+ old_values->root->show_label = true;
+
+ for (size_t k = 0; k < n_items; k++)
+ {
+ const struct arc_item *item = items[k];
+ int old_value_idx = pivot_category_create_leaf (
+ old_values->root, pivot_value_new_value (
+ &item->from, item->width,
+ (item->width
+ ? &(struct fmt_spec) { FMT_F, item->width, 0 }
+ : &spec->format),
+ dict_get_encoding (dict)));
+ pivot_table_put2 (table, 0, old_value_idx,
+ pivot_value_new_integer (item->to));
+
+ const char *value_label = item->value_label;
+ if (value_label && value_label[0])
+ pivot_table_put2 (table, 1, old_value_idx,
+ pivot_value_new_user_text (value_label, -1));
+ }
+
+ pivot_table_submit (table);
+ }
+
+ /* Assign user-missing values.
+
+ User-missing values in the source variable(s) must be marked
+ as user-missing values in the destination variable. There
+ might be an arbitrary number of missing values, since the
+ source variable might have a range. Our sort function always
+ puts missing values together at the top of the range, so that
+ means that we can use a missing value range to cover all of
+ the user-missing values in any case (but we avoid it unless
+ necessary because user-missing value ranges are an obscure
+ feature). */
+ size_t n_missing = n_items;
+ for (size_t k = 0; k < n_items; k++)
+ if (!items[n_items - k - 1]->missing)
+ {
+ n_missing = k;
+ break;
+ }
+ if (n_missing > 0)
+ {
+ size_t lo = n_items - (n_missing - 1);
+ size_t hi = n_items;
+
+ struct missing_values mv;
+ mv_init (&mv, 0);
+ if (n_missing > 3)
+ mv_add_range (&mv, lo, hi);
+ else if (n_missing > 0)
+ for (size_t k = 0; k < n_missing; k++)
+ mv_add_num (&mv, lo + k);
+ var_set_missing_values (spec->dst, &mv);
+ mv_destroy (&mv);
+ }
+
+ /* Add value labels to the destination variable. */
+ for (j = 0; j < n_items; j++)
+ {
+ const char *value_label = items[j]->value_label;
+ if (value_label && value_label[0])
+ {
+ union value to_val = { .f = items[j]->to };
+ var_add_value_label (spec->dst, &to_val, value_label);
+ }
+ }
+
+ /* Free array. */
+ free (items);