Create New Variables - Flatten

From Q
Jump to: navigation, search

This QScript flattens one or more Pick One - Multi questions while maintaining any changes made to the categories of those questions (e.g. merging categories).



In this example, the categories Hate and Dislike have been merged for the original question and then the QScript has been used to create a new, flattened copy of the question.

Technical details

A new Pick Any question is created for each of the selected Pick One - Multi questions using JavaScript Variables.

How to apply this QScript

  • Start typing the name of the QScript into the Search features and data box in the top right of the Q window.
  • Click on the QScript when it appears in the QScripts and Rules section of the search results.


  • Select Automate > Browse Online Library.
  • Select this QScript from the list.

Customizing the QScript

This QScript is written in JavaScript and can be customized by copying and modifying the JavaScript.

Customizing QScripts in Q4.11 and more recent versions

  • Start typing the name of the QScript into the Search features and data box in the top right of the Q window.
  • Hover your mouse over the QScript when it appears in the QScripts and Rules section of the search results.
  • Press Edit a Copy (bottom-left corner of the preview).
  • Modify the JavaScript (see QScripts for more detail on this).
  • Either:
    • Run the QScript, by pressing the blue triangle button.
    • Save the QScript and run it at a later time, using Automate > Run QScript (Macro) from File.

Customizing QScripts in older versions

  • Copy the JavaScript shown on this page.
  • Create a new text file, giving it a file extension of .QScript. See here for more information about how to do this.
  • Modify the JavaScript (see QScripts for more detail on this).
  • Run the file using Automate > Run QScript (Macro) from File.


// Flatten a Pick One - Multi using data reduction changes
includeWeb('QScript Functions for Combining Categories');
includeWeb('QScript Selection Functions');
includeWeb('QScript Table Functions');
includeWeb('QScript Functions to Generate Outputs');
includeWeb('QScript Value Attributes Functions');
includeWeb('JavaScript Array Functions');
if (!main())
    log("QScript Canceled.");
    conditionallyEmptyLog("QScript Finished.");
function main() {
    // On the web just take from what is selected.
    var web_mode = (!!Q.isOnTheWeb && Q.isOnTheWeb());
    var questions_to_flatten;
    var not_applicable_questions = [];
    function questionIsApplicable (q) {
        return !q.isHidden
                && q.questionType == "Pick One - Multi" 
                && q.uniqueValues.filter(function (x) {return !isNaN(x); }).length > 1; 
    if (web_mode) {
        selected_questions =;
        var sorted_selection = splitArrayIntoApplicableAndNotApplicable(selected_questions, questionIsApplicable);
        questions_to_flatten = sorted_selection.applicable;
        not_applicable_questions = sorted_selection.notApplicable;
    } else {
        var data_file;
        if (project.dataFiles.length > 1)
            data_file = selectOneDataFile('There is more than one data file in your project. ' + 
                'Select the data file to use:', project.dataFiles)
            data_file = project.dataFiles[0];   
        // Show the user an appropriate list of Pick One - Multi questions 
        // Get Pick One - Multi questions
        var pick_one_multis = data_file.questions.filter(function (q) { return questionIsApplicable(q); });
        if (pick_one_multis.length == 0) {
            log("There are no appropriate questions in the data file.");
            return false;
        // letting the user select the questions to flatten
        while (!questions_to_flatten || !oneOrMoreQuestions(questions_to_flatten))
            questions_to_flatten = selectManyQuestions('Select questions to flatten:', pick_one_multis).questions;
    // Create new flattened questions
    var log_lines = [];
    var flattened_question_objects = flattenQuestionsKeepingDataReductionChanges(questions_to_flatten, log_lines);
    var flattened_questions = (obj) { return obj.newQuestion; });
    var log_joined = log_lines.join("\r\n");
    // For web mode, create a log
    if (web_mode) {
        if (flattened_questions.length > 0) {
            log( (q) { return; }).join("\r\n"));
            // Replace selected questions with flattened versions
            replaceQuestionsInSelectedTablesAndPlots(flattened_question_objects, true);    
        if (not_applicable_questions.length > 0 || log_lines.length > 0) {
            if (flattened_questions.length > 0)
            log("Could not be flattened:");
        if (not_applicable_questions.length > 0) 
            log( (q) { return; }).join("\r\n"));
        if (log_lines.length > 0)
        return true;
    // Otherwise create a report
    if (flattened_questions.length > 0) {
        var top_group_name = "Flattened questions";
        var new_group = generateGroupOfSummaryTables(top_group_name, flattened_questions);
        // More recent Q versions can point the user to the new items.
        if (fileFormatVersion() > 8.65)
            log("Flattened questions have been added to the folder: " + top_group_name + "\r\n\r\n" + log_joined);
    } else
        log("There are no questions to flatten." + "\r\n\r\n" + log_joined);
     return true;
// For each of the input questions, work out the data reduction changes and then
// create a flattened version of the question which includes these changes.
// Return an array of pairs (original, flattened) for each question which could be flattened,
// and add a log message to log__contents for are question that has been flattened already.  
function flattenQuestionsKeepingDataReductionChanges(questions_to_flatten, log_contents) {
    var flattened_questions = [];
    questions_to_flatten.forEach(function (q) {
        var flattened_name = + ' (flattened)';
        var q_flattened = q.dataFile.getQuestionByName(flattened_name);
        // Flatten the question according to the changes in the data reduction
        if (q_flattened != null) {
             log_contents.push( + " has already been flattened: " + flattened_name); 
        } else {
            var value_attributes = q.valueAttributes;
            var non_missing_values = q.uniqueValues.filter(function (x) {
                return !value_attributes.getIsMissingData(x);
            if (non_missing_values.length <= 1) {
                log_contents.push( + " has only one unique non-missing value, so there is nothing to flatten.");

            // Get the set of values for each code in the data reduction
            var merging_objects = getAllUnderlyingValues(q);
            // Filter out the set of values corresoponding to the NET as
            // we don't want a bunch of NETs in the flattened question
            merging_objects = merging_objects.filter(function (obj) {
                return obj.array.sort().toString() != non_missing_values.toString();
            merging_objects = (obj) {
                return { name: obj.label, values: obj.array };

            q_flattened = pickOneMultiToPickAnyFlattenAndMergeByRows(q, merging_objects, true);
            setCountThisValueForVariablesInQuestion(q_flattened, 1, true);
            setCountThisValueForVariablesInQuestion(q_flattened, 0, false);
            flattened_questions.push({ originalQuestion: q, newQuestion: q_flattened });
    return flattened_questions;

See also