Table JavaScript Functions for Subtracting Calculations

From Q
Jump to navigation Jump to search
This page is currently under construction, or it refers to features which are under development and not yet available for use.
This page is under construction. Its contents are only visible to developers!
// Add a new row (column) to showing the difference of two user specified rows (columns)
function computeDifferencesAlongTwoIndices(by_columns) {
    includeWeb('Table JavaScript Utility Functions');
    includeWeb('JavaScript Array Functions');
    includeWeb('QScript Utility Functions');
    table.requireNumericTable();
    let col_labels = table.columnLabels;
    let row_labels = table.rowLabels;
    if (by_columns && !col_labels) {
        form.ruleNotApplicable("the table only has a single column");
    }
    const dim = by_columns ? 'column' : 'row';
    const Dim = dim.charAt(0).toUpperCase() + dim.slice(1).toLowerCase();
    const dim_labels = by_columns ? col_labels : row_labels;
    // Create array of indices
    const dim_indices = ['First ' + Dim];
    let j = 1;
    let ord;
    for (i = dim_labels.length; i>=2; i--) {
        switch(i) {
            case 2: ord = 'nd'; break;
            case 3: ord = 'rd'; break;
            default: ord = 'th'
        }
        let text = i + ord + ' Last ' + Dim;
        dim_indices[j] = text;
        j++;
    }
    dim_indices.push('Last ' + Dim);
    if (dim_labels == null || dim_labels.length < 2 || dim_indices == null || dim_indices.length < 2)
        form.ruleNotApplicable('table has only one ' + dim);
    const spans = by_columns ? table.columnSpans : table.rowSpans;
    if (spans.length > 0)
        form.ruleNotApplicable('table has ' + dim + ' spans');

    // Set up controls for user input.
    const rule_name = 'Difference Between a Pair of ' + Dim + 's';
    form.setHeading(rule_name);
    let label_statistic = form.newLabel('Statistic to use:');
    let valid_statistics = table.statistics.filter(function (statistic) {
        return statistic !== 'Column Comparisons' && statistic !== 'Column Names' &&
               statistic !== 'Columns Compared';
    });
    if (valid_statistics.length === 0)
        form.ruleNotApplicable('there are no numeric statistics in this table');
    let valid_stats_translated = valid_statistics.map(function(s) {
        try {
           return table.getTranslation(s);
        } catch(e) {
           return s;
        }
    });

    let desc = form.newLabel('Calculates the difference between two ' + dim + 's');
    desc.lineBreakAfter = true;

    let selection_method_label = form.newLabel('Select ' + dim + 's by: ');
    let selection_method = form.newComboBox('selectionMethod', ['Label', Dim + ' Position']);
    selection_method.setDefault('Label');
    selection_method.lineBreakAfter = true;
    let by_label = selection_method.getValue() === 'Label';
    let combo_box_statistic = form.newComboBox('statistic', valid_stats_translated);
    combo_box_statistic.setDefault(valid_stats_translated[0]);
    combo_box_statistic.lineBreakAfter = true;
    let label_1 = form.newLabel(Dim + ' 1:');
    let options = by_label ? dim_labels : dim_indices;
    let combo_box_1 = form.newComboBox('combo1', options);
    let dim_label_1 = combo_box_1.getValue();
    let dim_labels_reduced = dim_labels.filter(item => item !== dim_label_1);
    let dim_indices_red = dim_indices.filter(item => item !== dim_label_1);
    let dim_indices_reduced = dim_label_1 === 'First ' + Dim ? dim_indices_red.slice(1) : dim_indices_red;
    let options_reduced = by_label ? dim_labels_reduced : dim_indices_reduced;
    let label_2 = form.newLabel(Dim + ' 2:');
    let combo_box_2 = form.newComboBox('combo2', options_reduced);
    combo_box_2.lineBreakAfter = true;
    let new_dim_label;
    let auto_label_check = form.newCheckBox('auto', 'Label new ' + dim + ' automatically');
    auto_label_check.setDefault(true);
    auto_label_check.lineBreakAfter = true;
    let controls = [desc, label_statistic, combo_box_statistic, selection_method_label, selection_method,
                    label_1, combo_box_1, label_2, combo_box_2, auto_label_check];
    if (!auto_label_check.getValue()) {
        let override_label = form.newLabel('New ' + dim + ' label');
        let override_text_box = form.newTextBox('newlabel');
        override_text_box.setDefault('Difference');
        controls.push(override_label);
        controls.push(override_text_box);
        override_text_box.lineBreakAfter = true;
        new_dim_label = override_text_box.requireValue();
    }
    let replace_box = form.newCheckBox('reptab', 'Replace table with difference');
    replace_box.setDefault(false);
    replace_box.lineBreakAfter = true;
    controls.push(replace_box);
    let missing_label = form.newLabel('Blank cells shown as: ');
    let missing_choice = form.newComboBox('missingChoice', ['Blank', 'NaN']);
    missing_choice.setDefault('Blank');
    controls.push(missing_label);
    controls.push(missing_choice);
    form.setInputControls(controls);
    let dim_label_2 = combo_box_2.requireValue();
    let target_stat_translated = combo_box_statistic.requireValue(); // The statistic to use in the calculation
    let target_statistic = valid_statistics[valid_stats_translated.indexOf(target_stat_translated)];
    //Specify the position of the first and second index along the dim (indices begin at 0)
    let first_index, second_index;
    if(by_label) {
        first_index = by_columns ? table.columnIndex(dim_label_1) : table.rowIndex(dim_label_1);
        second_index = by_columns ? table.columnIndex(dim_label_2) : table.rowIndex(dim_label_2);
    } else {
        first_index = dim_indices.indexOf(dim_label_1);
        second_index = dim_indices.indexOf(dim_label_2);
        //Adjust for non- 'First row/column' index
        if (dim_label_1 !== 'First ' + Dim) first_index = first_index - 1;
        if (dim_label_2 !== 'First ' + Dim) second_index = second_index - 1;
    }
    if (auto_label_check.getValue()) {
        let label_1 = by_columns ? col_labels[first_index] : row_labels[first_index];
        let label_2 = by_columns ? col_labels[second_index] : row_labels[second_index];
        new_dim_label = label_1 + ' - ' + label_2; // Specify the label of the new row/column (containing the differences)
    }

    form.setSummary('Creates a new ' + dim + ' that is "' + dim_label_1 + '" minus "' + dim_label_2 + '"');
    let new_index;
    if (replace_box.getValue()) {
        new_index = first_index; // Filling in new values in existing row/column. Later delete all other rows/columns
        let labs = by_columns ? table.columnLabels : table.rowLabels;
        labs[new_index] = new_dim_label;
        table[by_columns ? 'columnLabels' : 'rowLabels'] = labs;
    } else {
        let last = second_index < first_index ? first_index : second_index;
        new_index = last + 1; // Index of the new row/column
        by_columns ? insertColumnAfterComplete(last, new_dim_label) : insertRowAfterComplete(last, new_dim_label); // Add a new row/column to store the differences
    }
    let stats = table.get(target_statistic); // Obtain the array of statistics
    if (by_columns) {
        // Calculate the differences in the new column
        for (let j = 0; j < table.numberRows; j++)
            stats[j][new_index] = stats[j][first_index] - stats[j][second_index];
    } else {
        // Calculate the differences in the new row
        for (let j = 0; j < table.numberColumns; j++)
            stats[new_index][j] = stats[first_index][j] - stats[second_index][j];
    }
    // Assign the new statistics back to the table
    table.set(target_statistic, stats);

    // Remove other columns if needed
    if (replace_box.getValue()) {
        if (by_columns) {
            moveColumnAfterComplete(new_index, table.numberColumns - 1);
            while (table.numberColumns > 1) {
                deleteColumnComplete(0);
            }
        } else {
            moveRowAfterComplete(new_index, table.numberRows - 1)
            while (table.numberRows > 1)
                deleteRowComplete(0);
        }
    }
    table.showMissingAs(missing_choice.getValue() === 'NaN' ? NaN : '');
}