QScript Selection Functions

From Q
Jump to navigation Jump to search

The QScript functions selectOne and selectMany allow the user of the QScript to select from a list of strings. The functions on this page are designed to make it easier for the QScript writer to present menus of Q objects, like questions, variables, or data files, for the user to select from without the need to write extra code to extract the labels or names of these objects.

To make these functions available when writing a QScript or Rule see JavaScript Reference.

selectManyQuestions(message, question_array, use_preselections)

selectManyQuestions() allows you to provide an array of Q Question objects (obtained, for example, by getQuestionsByName()) and a message string. The function obtains the names of the questions and presents them to the user in a box with your message. The user can make multiple selections. The function returns an object with two properties:

questions - is an array of the selected questions
names - is an array of the names of the selected questions

The boolean flag use_preselections is used to specify whether or not the menu shown to the user should have items already selected, based on the user's selections in the Variables and Questions tab.

selectManyVariablesByName(message, variable_array, use_preselections)

selectManyVariablesByName() allows you to provide an array of Q Variable objects (obtained, for example, by getVariablesByName()) and a message string. The function obtains the names of the variables and presents them to the user in a box with your message. The user can make multiple selections. The function returns an object with two properties:

variables - is an array of the selected variables
names - is an array of the names of the selected variables

The boolean flag use_preselections is used to specify whether or not the menu shown to the user should have items already selected, based on the user's selections in the Variables and Questions tab.

selectManyVariablesByLabel(message, variable_array, use_preselections)

selectManyVariablesByLabel() allows you to provide an array of Q Variable objects (obtained, for example, by getVariablesByLabel()) and a message string. The function obtains the labels of the variables and presents them to the user in a box with your message. The user can make multiple selections. The function returns an object with two properties:

variables - is an array of the selected variables
labels - is an array of the labels of the selected variables

The boolean flag use_preselections is used to specify whether or not the menu shown to the user should have items already selected, based on the user's selections in the Variables and Questions tab.

selectManyVariablesByNameAndLabel(message, variable_array, use_preselections)

selectManyVariablesByNameAndLabel() allows you to provide an array of Q Variable objects (obtained, for example, by getVariablesByLabel()) and a message string. The function obtains the names and labels of the variables and presents them to the user in a box with your message. The user can make multiple selections. The function returns an object with two properties:

variables - is an array of the selected variables
names_labels - is an array of the names and labels of the selected variables

The boolean flag use_preselections is used to specify whether or not the menu shown to the user should have items already selected, based on the user's selections in the Variables and Questions tab.

selectManyVariablesByQuestionNameAndLabel(message, variable_array, use_preselections)

selectManyVariablesByQuestionNameAndLabel() allows you to provide an array of Q Variable objects (obtained, for example, by getVariablesByLabel()) and a message string. The function obtains the question names and variable labels of the variables and presents them to the user in a box with your message. The user can make multiple selections. The function returns an object with two properties:

variables - is an array of the selected variables
names_labels - is an array of the names and labels of the selected variables

The boolean flag use_preselections is used to specify whether or not the menu shown to the user should have items already selected, based on the user's selections in the Variables and Questions tab.

selectManyVariablesByQuestionNameAndLabelAndDataFile(message, variable_array, use_preselections)

selectManyVariablesByQuestionNameAndLabelAndDataFile() allows you to provide an array of Q Variable objects (obtained, for example, by getVariablesByLabel()) and a message string. The function obtains the question names and variable labels and data file names of the variables and presents them to the user in a box with your message. The user can make multiple selections. The function returns an object with two properties:

variables - is an array of the selected variables
names_labels - is an array of the names and labels of the selected variables

The boolean flag use_preselections is used to specify whether or not the menu shown to the user should have items already selected, based on the user's selections in the Variables and Questions tab.

selectManyDataFiles(message, datafile_array)

selectManyDataFiles() allows you to provide an array of Q Data File objects (obtained, for example, by getVariablesByName()) and a message string. The function obtains the names of the data files and presents them to the user in a box with your message. The user can make multiple selections. The function returns an object with two properties:

files - is an array of the selected files
names - is an array of the names of the selected files

selectOneDataFileFromSelectedQuestions()

Intended for use in Displayr only, selectOneDataFileFromSelectedQuestions() either returns the only dataFile in the report, or if there are multiple dataFiles, uses project.report.selectedQuestions() to determine which dataFile to report based on which one the variable set the user has selected from Data Sets. An error is thrown if the user has selected multiple questions from different data sets or if there are multiple data sets and nothing is selected.

selectOneQuestion(message, question_array, use_preselections)

selectOneQuestion() allows you to provide an array of Q Question objects (obtained, for example, by getQuestionsByName()) and a message string. The function obtains the names of the questions and presents them to the user in a box with your message. The user can only make a single selection. The function returns the question object that the user has selected.

The boolean flag use_preselections is used to specify whether or not the menu shown to the user should have items already selected, based on the user's selections in the Variables and Questions tab.

selectOneQuestionByNameAndDataFile(message, question_array, use_preselections)

selectOneQuestionByNameAndDataFile() allows you to provide an array of Q Question objects (obtained, for example, by getQuestionsByName()) and a message string. The function obtains the names of the questions with the data file name and presents them to the user in a box with your message. The user can only make a single selection. The function returns the question object that the user has selected.

The boolean flag use_preselections is used to specify whether or not the menu shown to the user should have items already selected, based on the user's selections in the Variables and Questions tab.

selectOneVariableByLabel(message, variable_array, use_preselections)

selectOneVariableByLabel() allows you to provide an array of Q Variable objects (obtained, for example, by getVariablesByLabel()) and a message string. The function obtains the labels of the variables and presents them to the user in a box with your message. The user can only make a single selection. The function returns the variable object that the user has selected.

The boolean flag use_preselections is used to specify whether or not the menu shown to the user should have items already selected, based on the user's selections in the Variables and Questions tab.

selectOneVariableByName(message, variable_array, use_preselections)

selectOneVariableByName() allows you to provide an array of Q Variable objects (obtained, for example, by getVariablesByName()) and a message string. The function obtains the names of the variables and presents them to the user in a box with your message. The user can only make a single selection. The function returns the variable object that the user has selected.

The boolean flag use_preselections is used to specify whether or not the menu shown to the user should have items already selected, based on the user's selections in the Variables and Questions tab.

selectOneVariableByNameAndLabel(message, variable_array, use_preselections)

selectOneVariableByNameAndLabel() allows you to provide an array of Q Variable objects (obtained, for example, by getVariablesByName()) and a message string. The function obtains the names and labels of the variables and presents them to the user in a box with your message. The user can only make a single selection. The function returns the variable object that the user has selected.

The boolean flag use_preselections is used to specify whether or not the menu shown to the user should have items already selected, based on the user's selections in the Variables and Questions tab.

selectOneVariableByQuestionNameAndLabel(message, variable_array, use_preselections)

selectOneVariableByQuestionNameAndLabel() allows you to provide an array of Q Variable objects (obtained, for example, by getVariablesByName()) and a message string. The function obtains the question names and variable labels of the variables and presents them to the user in a box with your message. The user can only make a single selection. The function returns the variable object that the user has selected.

The boolean flag use_preselections is used to specify whether or not the menu shown to the user should have items already selected, based on the user's selections in the Variables and Questions tab.

selectOneVariableByQuestionNameAndLabelAndDataFile(message, variable_array, use_preselections)

selectOneVariableByQuestionNameAndLabel() allows you to provide an array of Q Variable objects (obtained, for example, by getVariablesByName()) and a message string. The function obtains the question names and variable labels and data file names of the variables and presents them to the user in a box with your message. The user can only make a single selection. The function returns the variable object that the user has selected.

The boolean flag use_preselections is used to specify whether or not the menu shown to the user should have items already selected, based on the user's selections in the Variables and Questions tab.

selectOneDataFile(message, datafile_array, default_index)

selectOneDataFile() allows you to provide an array of Q Data File objects (obtained, for example, by project.dataFiles) and a message string. The function obtains the names of the variables and presents them to the user in a box with your message. The user can only make a single selection. The function returns the data file object that the user has selected. The parameter default_index specifies the default selection to show.

truncateStringForSelectionWindow(string)

truncateStringForSelectionWindow() truncates the input string to 120 characters. This function is used by the selection functions above to ensure that the message box does not become very wide when question names and variable labels are very long.

dataFileSelection()

This function checks the project for multiple data files, and if more than one file exists in the project it presents the user with a list of files and asks them which files they would like to use. It then returns an array containing the selected data file objects.

selectManyTablesWithGroupNames(message, group_item)

This function presents the user with a list of all of the tables in the input group_item and allows them to select multiple items.

Each item in the list contains the name of the table, along with the name of the groups that contain the table, up to but not including the name of group_item.

The function returns an object containing two properties:

  • tables is an array of the selected table objects.
  • names is an array containing the names of the selected tables.

selectManyTablesAndPlotsWithGroupNamesAndTypes(message, group_item)

This function presents a list of all of the tables and charts in the input group_item and allows the user to make multiple selections.

Each item in the list contains the name of the table, along with the name of the groups that contain the table, up to but not including the name of group_item, and also whether the item it a table or a chart.

The function returns an object containing two properties:

  • items is an array of the selected items.
  • names is an array containing the names of the selected items.

selectOneTableWithGroupNames(message, group_item)

This function presents the user with a list of all of the tables in the input group_item and allows them to select one table. Each item in the list contains the name of the table, along with the name of the groups that contain the table, up to but not including the name of group_item. The table object selected by the user is returned.

requestOneDataFileFromProject(null_if_cancelled, last_as_default)

This function requests the user to select a data file if there is more than one data file in the project, and returns the selected data file. Otherwise returns the single data file in the project. The flag null_if_cancelled controls whether to return null if the dialog is cancelled. The flag last_as_default controls whether to show the last data file as the default selection.

selectedTablesAndPlots(report)

This function returns all tables and charts selected in the report tree in Q 4.8 or later. For older versions of Q, a listbox is shown which allows the user to select from tables and charts obtained from the report tree.

filePromptWithVerification(message, override_file_name, options)

This function is used to add a data file to the Q Project by prompting the user to enter the path and name of the file. If the user mis-types the file name then they are given the opportunity to re-enter the file name. When using Q 4.9 and above it is better to use the function addDataFileDialog() instead.

oneOrMoreQuestions(questions)

Returns true if the array questions is not empty. If it is empty it returns false and returns a message for the user. This is intended to be used in a loop to give the user additional opportunity to select questions of they failed to select a question when prompted.

indicesOfQObjectsInArray(array_1, array_2)

Returns the indices of objects in array_1 which are also elements of array_2. The two input arrays should only contain Q objects, e.g. questions, variables, etc.

selectOneVariableOrQuestionUsingPreselections(message, object_array, label_array, use_preselections, help_page)

Generalizes the function selectOne() for questions and variables, allowing any selections made by the user to be pre-selected in the list that is shown to the user. Returns the index of the item selected by the user just like selectOne().

  • message: The message to show to user.
  • object_array: The array of questions or variables to be selected from.
  • label_array: The labels corresponding to the items in object_array, in the same order as the objects. These are shown to the user.
  • use_preselections: Boolean flag to toggle whether the user is shown preselected items in the menu.
  • help_page: help page to direct the user to.

selectManyVariablesOrQuestionsUsingPreselections(message, object_array, label_array, use_preselections, help_page)

Generalizes the function selectMany() for questions and variables, allowing any selections made by the user to be pre-selected in the list that is shown to the user. Returns the indices of the items selected by the user just like selectMany().

  • message: The message to show to user.
  • object_array: The array of questions or variables to be selected from.
  • label_array: The labels corresponding to the items in object_array, in the same order as the objects. These are shown to the user.
  • use_preselections: Boolean flag to toggle whether the user is shown preselected items in the menu.
  • help_page: help page to direct the user to.

promptUntilBlank(message)

Repeatedly prompts the user until they enter a blank selection. Returns an array of the entered strings.

promptForDKLabels()

Prompt the user to enter extra labels corresponding to Don't Know options in the questionnaire. Returns an array containing the entered labels.

getQuestionsSelectedInTables()

Search through all of the tables and charts that have been selected by the user at the time the script is run and return an array of objects, each of which has two properties:

  • question - The question object
  • item - the table or chart which contains the question
  • position - a string telling us if the questions is in Primary (blue), Secondary (brown), or Tertiary (the second blue menu on some charts).

replaceQuestionsInSelectedTablesAndPlots(question_pairs, replace_in_plots)

Go through each of the tables and charts that are selected at the time the QScript runs, and use the array question_pairs to replace the questions that are selected in those items.

question_pairs should be an array, where each element has two properties:

  • originalQuestion - the original question that we want to replace.
  • newQuestion - the new question that we want to replace originalQuestion with.

If it is not appropriate to make replacements in charts, then set replace_in_plots to false.

getSelectedROutputFromPage(required_class)

This function is designed to resolve ambiguity about which R Output the user has selected when running various Save Variables QScripts. It relaxes the restriction that the user has to specifically have the intended R Output selected, instead looking for an unambiguously appropriate output in the current page (folder). If there is a single appropriate R Output on the current page then this will be returned. When there are multiple appropriate R Outputs on the page, then a message will be returned prompting the user to select one of them.

The argument required_class is used to restrict the search to only R Outputs that have a specific R class. If you don't want to restrict to a particular class, then supply a value of null

questionsInSelectedItems()

This function returns an array of the questions that are used by all the selected tables and plots.


Source Code

includeWeb('QScript Functions for Processing Arrays');
includeWeb('QScript Utility Functions');
includeWeb('QScript Table Functions');


// SELECTION

function selectManyQuestions(message, question_array, use_preselections) {
    if (use_preselections == undefined)
        var use_preselections = true;
    var question_names = getNamesOfQuestionsInArray(question_array);
    var selected_indices = selectManyVariablesOrQuestionsUsingPreselections(message, question_array, question_names.map(truncateStringForSelectionWindow), use_preselections, null);
    var selected_questions = getElementsOfArrayBySelectedIndices(question_array, selected_indices);
    var selected_names = getElementsOfArrayBySelectedIndices(question_names, selected_indices);
    return {questions: selected_questions, names: selected_names};
}

// Generalization of selectMany() to array of variables, allowing the user
// to make selection by variable names.
function selectManyVariablesByName(message, variable_array, use_preselections) {
    if (use_preselections == undefined)
        var use_preselections = true;
    var variable_names = getNamesOfVariablesInArray(variable_array);
    var selected_indices = selectManyVariablesOrQuestionsUsingPreselections(message, variable_array, variable_names.map(truncateStringForSelectionWindow), use_preselections, null);
    var selected_variables = getElementsOfArrayBySelectedIndices(variable_array, selected_indices);
    var selected_names = getElementsOfArrayBySelectedIndices(variable_names, selected_indices);
    return {variables: selected_variables, names: selected_names};
}

 
// Generalization of selectMany() to array of variables, allowing the user
// to make selection by variable labels.
function selectManyVariablesByLabel(message, variable_array, use_preselections) {
    if (use_preselections == undefined)
        var use_preselections = true;
    var variable_labels = getLabelsOfVariablesInArray(variable_array);
    var selected_indices = selectManyVariablesOrQuestionsUsingPreselections(message, variable_array, variable_labels.map(truncateStringForSelectionWindow), use_preselections, null);
    var selected_variables = getElementsOfArrayBySelectedIndices(variable_array, selected_indices);
    var selected_labels = getElementsOfArrayBySelectedIndices(variable_labels, selected_indices);
    return {variables: selected_variables, labels: selected_labels};
}

function selectManyVariablesByNameAndLabel(message, variable_array, use_preselections) {
    if (use_preselections == undefined)
        var use_preselections = true;
    var variable_names_labels = getNamesAndLabelsOfVariablesInArray(variable_array);
    var selected_indices = selectManyVariablesOrQuestionsUsingPreselections(message, variable_array, variable_names_labels.map(truncateStringForSelectionWindow), use_preselections, null);
    var selected_variables = getElementsOfArrayBySelectedIndices(variable_array, selected_indices);
    var selected_names_labels = getElementsOfArrayBySelectedIndices(variable_names_labels, selected_indices);
    return {variables: selected_variables, names_labels: selected_names_labels};
}

function selectManyVariablesByQuestionNameAndLabel(message, variable_array, use_preselections) {
    if (use_preselections == undefined)
        var use_preselections = true;
    var variable_names_labels = getQuestionNamesAndLabelsOfVariablesInArray(variable_array);
    var selected_indices = selectManyVariablesOrQuestionsUsingPreselections(message, variable_array, variable_names_labels.map(truncateStringForSelectionWindow), use_preselections, null);
    var selected_variables = getElementsOfArrayBySelectedIndices(variable_array, selected_indices);
    var selected_names_labels = getElementsOfArrayBySelectedIndices(variable_names_labels, selected_indices);
    return {variables: selected_variables, names_labels: selected_names_labels};
}

function selectManyVariablesByQuestionNameAndLabelAndDataFile(message, variable_array, use_preselections) {
    if (use_preselections == undefined)
        var use_preselections = true;
    var variable_names_labels = getQuestionNamesAndLabelsAndDataFilesOfVariablesInArray(variable_array);
    var selected_indices = selectManyVariablesOrQuestionsUsingPreselections(message, variable_array, variable_names_labels.map(truncateStringForSelectionWindow), use_preselections, null);
    var selected_variables = getElementsOfArrayBySelectedIndices(variable_array, selected_indices);
    var selected_names_labels = getElementsOfArrayBySelectedIndices(variable_names_labels, selected_indices);
    return {variables: selected_variables, names_labels: selected_names_labels};
}

// Since we can't prompt users to choose a data set from a drop down in Displayr
// this function returns the data set corresponding to a question they have
// selected from Data Sets
function selectOneDataFileFromSelectedQuestions() {
    if (project.dataFiles.length === 0) 
        throw new UserError("You first must add a Data Set to your project to use this QScript.")

    if (project.dataFiles.length === 1) {
        return project.dataFiles[0];
    } else {
        var selected_questions = project.report.selectedQuestions();
        if (selected_questions.length == 0) {
            throw new UserError("This QScript is meant to work with only one Data Set at a time. Please select any Variable Set from the single Data Set you wish to use.");
        }
        var selected_datafile = selected_questions[0].dataFile;
        // Make sure all questions are from the same data set
        if (!selected_questions.map(function (q) { return q.dataFile.name; }).every(function (type) { return type == selected_datafile.name; })) {
            throw new UserError("This QScript is meant to work with only one Data Set at a time. Please select a Variable Set from a single Data Set.");
        }
        return selected_datafile;
    }
}

// Generalization of selectOne() to accept an array of questions
function selectOneQuestion(message, question_array, use_preselections) {
    if (use_preselections == undefined)
        var use_preselections = true;
    var question_names = getNamesOfQuestionsInArray(question_array).map(truncateStringForSelectionWindow);
    var selected_index = selectOneVariableOrQuestionUsingPreselections(message, question_array, question_names, use_preselections, null);
    return question_array[selected_index];
}

function selectOneQuestionByNameAndDataFile(message, question_array, use_preselections) {
    if (use_preselections == undefined)
        var use_preselections = true;
    var question_names = getNamesOfQuestionsAndDataFilesInArray(question_array);
    var selected_index = selectOneVariableOrQuestionUsingPreselections(message, question_array, question_names, use_preselections, null);
    return question_array[selected_index];
}


// Generalization of selectOne() to accept an array of variables, allowing the user to
// make a selection by the variable labels
function selectOneVariableByLabel(message, variable_array, use_preselections) {
    if (use_preselections == undefined)
        var use_preselections = true;
    var variable_labels = getLabelsOfVariablesInArray(variable_array);
    var selected_index = selectOneVariableOrQuestionUsingPreselections(message, variable_array, variable_labels.map(truncateStringForSelectionWindow), use_preselections, null);
    return variable_array[selected_index];
}

 
// Generalization of selectOne() to accept an array of variables, allowing the user to
// make a selection by the variable names
function selectOneVariableByName(message, variable_array, use_preselections) {
    if (use_preselections == undefined)
        var use_preselections = true;
    var variable_names = getNamesOfVariablesInArray(variable_array);
    var selected_index = selectOneVariableOrQuestionUsingPreselections(message, variable_array, variable_names.map(truncateStringForSelectionWindow), use_preselections, null);
    return variable_array[selected_index];
}

function selectOneVariableByNameAndLabel(message, variable_array, use_preselections) {
    if (use_preselections == undefined)
        var use_preselections = true;
    var variable_names = getNamesAndLabelsOfVariablesInArray(variable_array);
    var selected_index = selectOneVariableOrQuestionUsingPreselections(message, variable_array, variable_names.map(truncateStringForSelectionWindow), use_preselections, null);
    return variable_array[selected_index];
}
 
function selectOneVariableByQuestionNameAndLabel(message, variable_array, use_preselections) {
    if (use_preselections == undefined)
        var use_preselections = true;
    var variable_names = getQuestionNamesAndLabelsOfVariablesInArray(variable_array);
    var selected_index = selectOneVariableOrQuestionUsingPreselections(message, variable_array, variable_names.map(truncateStringForSelectionWindow), use_preselections, null);
    return variable_array[selected_index];
}
 
function selectOneVariableByQuestionNameAndLabelAndDataFile(message, variable_array, use_preselections) {
    if (use_preselections == undefined)
        var use_preselections = true;
    var variable_names = getQuestionNamesAndLabelsAndDataFilesOfVariablesInArray(variable_array);
    var selected_index = selectOneVariableOrQuestionUsingPreselections(message, variable_array, variable_names.map(truncateStringForSelectionWindow), use_preselections, null);
    return variable_array[selected_index];
} 

// Generalization of selectOne() to accept an array of data files, allowing the user to
// make a selection by the data file names
function selectOneDataFile(message, datafile_array, default_index) {
    if (datafile_array.length === 0)
         throw new UserError("At least one Data Set is required to use this script. " + 
                             "Please add a Data Set before re-running this script.");
    if (datafile_array.length === 1)
         return datafile_array[0];
    var datafile_names = getNamesOfDataFilesInArray(datafile_array);
    var selected_index = selectOne(message, datafile_names.map(truncateStringForSelectionWindow), null, default_index);
    return datafile_array[selected_index];
}

// Generalization of selectMany() to array of data files, allowing the user
// to make selection by the current name of the file.
function selectManyDataFiles(message, datafile_array) {
    var datafile_names = getNamesOfDataFilesInArray(datafile_array);
    var selected_indices = selectMany(message, datafile_names.map(truncateStringForSelectionWindow));
    var selected_datafiles = getElementsOfArrayBySelectedIndices(datafile_array, selected_indices);
    var selected_names = getElementsOfArrayBySelectedIndices(datafile_names, selected_indices);
    return {files: selected_datafiles, names: selected_names};
}

// If string is longer than 120 characters, truncate it so that it fits nicely on the window.
function truncateStringForSelectionWindow(string) {
    var max_chars = 120;
    if (string.length < max_chars)
        return string;
    else
        return string.substring(0, max_chars - 3) + "...";
}

// If there is more than one data file in the current project, show the user a list
// and ask them which data files they want to use.
function dataFileSelection() {
    var datafiles = project.dataFiles;
    var selected_datafiles = datafiles;
    if (datafiles.length > 1) 
        selected_datafiles = selectManyDataFiles("Please choose which data files you would like to use:", datafiles).files;
    return selected_datafiles;
}

function selectManyTablesWithGroupNames(message, group_item) {
    var all_tables = [];
    recursiveGetAllTablesInGroup(group_item, all_tables);
    var table_names = getGroupNamesForTablesInArray(all_tables, group_item.name);
    var selected_indices = selectMany(message, table_names);
    var selected_tables = getElementsOfArrayBySelectedIndices(all_tables, selected_indices);
    var selected_names = getElementsOfArrayBySelectedIndices(table_names, selected_indices);
    return {tables: selected_tables, names: selected_names};
}

function selectManyTablesAndPlotsWithGroupNamesAndTypes(message, group_item) {
    var all_items = [];
    recursiveGetAllTablesAndPlotsInGroup(group_item, all_items);
    var item_names = getGroupNamesForTablesAndPlotsInArrayWithType(all_items, group_item.name);
    var selected_indices = selectMany(message, item_names);
    var selected_items = getElementsOfArrayBySelectedIndices(all_items, selected_indices);
    var selected_names = getElementsOfArrayBySelectedIndices(item_names, selected_indices);
    return {items: selected_items, names: selected_names};
}

function selectOneTableWithGroupNames(message, group_item) {
    var all_tables = [];
    recursiveGetAllTablesInGroup(group_item, all_tables);
    var table_names = getGroupNamesForTablesInArray(all_tables, group_item.name);
    if (table_names.length == 0)
        return [];
    var selected_index = selectOne(message, table_names);
    return all_tables[selected_index];
}

function requestOneDataFileFromProject(null_if_cancelled, last_as_default, is_displayr) {
	if (typeof is_displayr == "undefined")
		var is_displayr = false;
	var doc_name = is_displayr ? "document" : "project";
    if (project.dataFiles.length == 0) {
		throw new UserError("There are no Data Sets in your " + doc_name + ". " + 
		                    "Please add a Data Set before attempting to run this script again.");
    } else if (project.dataFiles.length == 1)
        return project.dataFiles[0];
    else
		return selectOneDataFile('There is more than one Data Set in your ' + doc_name + '. ' + 
								 'Please select the Data Set to use:', project.dataFiles, last_as_default ? project.dataFiles.length - 1 : undefined);
}

function selectedTablesAndPlots(report) {
    if (!report.selectedItems)
        return selectManyTablesAndPlotsWithGroupNamesAndTypes('Select tables and plots', report).items;
    else {
        var selected_items = report.selectedItems();
        if (selected_items.length == 0) {
            if (confirm('You currently only have no tables and plots selected.  Would you like to have this script run on everything in the Report?')) {
                selected_items = [];
                recursiveGetAllTablesAndPlotsInGroup(report, selected_items);
            }
        } else if (selected_items.length == 1) {
            if (confirm('You currently only have one table/plot selected.  Would you like to have this script run on everything in the Report?')) {
                selected_items = [];
                recursiveGetAllTablesAndPlotsInGroup(report, selected_items);
            }
        }
        var selected_tables_and_plots = [];
        for (var i = 0; i < selected_items.length; i++) {
            var item = selected_items[i];
            if (item.type == "Table" || item.type == "Plot")
                selected_tables_and_plots.push(item);
        }
        return selected_tables_and_plots;
    }
}

// Try to add a data file to the project by asking the user to type the path.
// If the file can't be found then tell the user that the name is invalid
// and allow them to try again.
function filePromptWithVerification(message, override_file_name, options) {
	var given_valid_file = false;
	while (!given_valid_file) {
		var filename = prompt(message);
		try {
			file = project.addDataFile(filename, override_file_name, options);
			given_valid_file = true;
		} catch (e) {
			alert("Invalid file name!");
		}
	}
	return file;
}

function oneOrMoreQuestions(questions) {
    if (questions.length == 0) {
        alert('Select one or more questions.');
        return false;
    } else
        return true;
}


// Returns the indices of the objects in array_1 that are contained in array_2
// Can be arrays of questions or arrays of variables.
function indicesOfQObjectsInArray(array_1, array_2) {
    if (array_1.length == 0 || array_2.length == 0)
        return [];

    return array_2.map(function (obj_2) {
        var ind = -1;
        array_1.forEach(function (obj_1, index) {
            if (ind == -1)
                if (obj_1.equals(obj_2))
                    ind = index;
        });
        return ind;
    }).filter(function (x) { return x >= 0; });
}

function allQObjectsWithSameType(array) {
    var target_type = array[0].type;
    return array.every(function (x) { return x.type == target_type; });
}




function selectOneVariableOrQuestionUsingPreselections(message, object_array, label_array, use_preselections, help_page) {
    if (object_array == "undefined" || object_array.length == 0)
        throw new CallerError("selectOneVariableUsingPreselections: preselections 'object_array' is empty");
    var object_type = object_array[0].type;
    // No preselections for objects other than variables and questions
    if (object_type != "Variable" && object_type != "Question")
        throw("Expected array of variables or questions.");
    if (!allQObjectsWithSameType(object_array))
        throw("Expected an array of objects with the same type.");

    var selected_index;
    if (project.report.selectedQuestions && use_preselections) {  
        var preselections;
        if (object_type == "Variable")
            preselections = project.report.selectedVariables();
        else
            preselections = project.report.selectedQuestions();
        var preselected_index = null;
        if (preselections.length > 0)
            preselected_index = indicesOfQObjectsInArray(object_array, [preselections[0]])[0];
        selected_index = selectOne(message, label_array, help_page, preselected_index);
    } else
        selected_index = selectOne(message, label_array, help_page)  
    return selected_index;
}

function selectManyVariablesOrQuestionsUsingPreselections(message, object_array, label_array, use_preselections, help_page) {  
    if (object_array == "undefined" || object_array.length == 0)
        throw new CallerError("selectManyVariablesUsingPreselections: preselections 'object_array' is empty");
    var object_type = object_array[0].type;
    // No preselections for objects other than variables and questions
    if (object_type != "Variable" && object_type != "Question")
        throw("Expected array of variables or questions.");
    if (!allQObjectsWithSameType(object_array))
        throw("Expected an array of objects with the same type.");

    var selected_indices;
    if (project.report.selectedQuestions && use_preselections) {  
        var preselections;
        if (object_type == "Variable")
            preselections = project.report.selectedVariables();
        else
            preselections = project.report.selectedQuestions();
        var preselected_indices = null;
        if (preselections.length > 0)
            preselected_indices = indicesOfQObjectsInArray(object_array, preselections);
        selected_indices = selectMany(message, label_array, help_page, preselected_indices);
    } else
        selected_indices = selectMany(message, label_array, help_page)  
    return selected_indices;
}


// Keep prompting the use for more entries until they enter a blank string.
// The message should tell them that they can stop if they enter a blank.
function promptUntilBlank(message) {
	var answers = [];
	while (true) {
		var new_answer = prompt(message);
		if (new_answer == "")
			break;
		else
			answers.push(new_answer);
	}
	return answers;
}

function promptForDKLabels() {
    // Allow the user to enter additional strings for don't know labels
    var extra_dk_strings = [];
    if (askYesNo("This QScript will check for standard 'Don't Know' style responses, including: " + _DK_STRINGS_GLOBAL.join(", ") 
                 + ", and labels that are NA. Do you wish to add additional 'Don't Know' response labels?"))
        extra_dk_strings = promptUntilBlank("Enter an extra 'Don't Know' label. Enter a blank when finished.");
    extra_dk_strings = extra_dk_strings.map(function (s) { return s.toLowerCase(); });
    return extra_dk_strings;
}

// Search through all the select tables and plots and return an array of objects, each of which has
// two properties:
// * question - The question object
// * item - the table or plot which contains the question
// * position - a string telling us if the questions is in Primary (blue), Secondary (brown)
//              or Tertiary (second bule on some plots)

function getQuestionsSelectedInTables() {
    var selected_questions = [];
    var selected_items = project.report.selectedItems();
    selected_items.forEach(function (item) {
        if (item.type == "Table" || item.type == "Plot") {
            // Primary
            selected_questions.push( {question: item.primary, item: item, position: "Primary"} );
            // Secondary
            if (typeof item.secondary != "string" && item.secondary != null)
                selected_questions.push({question: item.secondary, item: item, postion: "Secondary"} );
            // Tertiary
            if (item.tertiary != null)
                selected_questions.push( {question: item.tertiary, item: item, position: "Tertiary"} );
        }
    });
    return selected_questions;
}

// Given an array of question pairs (originalQuestion, newQuestion),
// search through the tables and plots that were selected by the user
// at run time and replace originalQuestion with newQuestion wherever found.
// In case it is not a good idea to modify plots, pass replace_in_plots = false.
// If it is not appropriate for the user to select plots then it might be
// better to catch this earlier in the QScript.
function replaceQuestionsInSelectedTablesAndPlots(question_pairs, replace_in_plots) {

    // Get all questions that are in any of the selected items,
    // aong with the link back to the table;
    var item_questions = getQuestionsSelectedInTables();

    question_pairs.forEach(function (pair) {

        // Get all item objects which contain the original question
        // in this pair.
        var relevant_items = item_questions.filter(function (obj) {
            return obj.question.equals(pair.originalQuestion);
        });

        // Loop through those objbects and replace with new question
        // where we can.
        relevant_items.forEach(function (obj) {
            var current_item = obj.item;
            var current_type = current_item.type;
            if (current_type == "Table" || (current_type == "Plot" && replace_in_plots)) {
                if (obj.position == "Primary")
                    current_item.primary = pair.newQuestion;
                else if (obj.position == "Secondary")
                    current_item.secondary = pair.newQuestion;
                else if (obj.position == "Tertiary")
                    current_item.tertiary == pair.newQuestion;
            }
        })
    });
}

// Return either the selected item if it is an R output, or search the current page for a single
// R output and return that. If you want to restrict to only returning ROutputs of a specific class
// then set required_classes to an array of acceptable class names of the class as a string. If you 
// don't want to restrict, supply an empty array instead.
function getSelectedROutputFromPage(required_classes) {
    
    // When a single item is identified, use this function to check it is valid and the right class
    function checkLonelyROutput(selected_item) {
        if (!checkROutputIsValid(selected_item))
            return false;
        if (required_classes.length > 0 && !required_classes.some(function (required_class) { return selected_item.outputClasses.indexOf(required_class) > -1; }))
            return false;
        
        return true;
    }

    var selected_items = project.report.selectedRaw()
    if (selected_items.length == 0)
        return null;
 
    var selected_item = selected_items[0];
    
    // If a single R Output is selected return it if it is the right class
    // If it is the wrong class, return null
    if (selected_items.length == 1 && selected_item.type == "R Output") {
        if (checkLonelyROutput(selected_item))
            return selected_item;
        else
            return null;
    }

    // Not an ROutput selected. Search the group/page to see if
    // there is an ROutput
    var selected_group = selected_item.type == "ReportGroup" ? selected_item : selected_item.group;
    var items_in_group = selected_group.subItems;
    items_in_group = items_in_group.filter(function (item) { return item.group.equals(selected_group); }); // Filter out items from further down the tree
    items_in_group = items_in_group.filter(function (item) { return item.type == "R Output"; });

    // No R Outputs on the page
    if (items_in_group.length == 0)
        return null;

    // If there is a single R Output on the page, check it and return it if it is good to use
    if (items_in_group.length == 1) {
        if(checkLonelyROutput(items_in_group[0]))
            return items_in_group[0];
        else
            return null;
    }

    // Multiple R Outputs on the page, filter out any which are invalid or the wrong class
    items_in_group = items_in_group.filter(function (item) { return item.error === null && item.outputClasses !== null; } );

    if (items_in_group.length == 0)
        return null;

    // If a class has been specified, filter to that class
    if (required_classes.length > 0) {
        items_in_group = items_in_group.filter(function (item) {
            return required_classes.some(function (required_class) { return item.outputClasses.indexOf(required_class) > -1; });
        });
    }

    // If a single item remains, return it
    if (items_in_group.length == 1)
        return items_in_group[0];

    if (items_in_group.length > 1)
        log("There are multiple R Outputs available on this page, but this option requires a single R Output. Click the item you want to use and run this option again.");

    return null;
}

function questionsInSelectedItems() {
    var questions_in_selected_items = [];
    project.report.selectedItems().forEach(function (item) {
        if (item.type == "Table" || item.type == "Plot") {
            if (item.primary.type == "Question")
                questions_in_selected_items.push(item.primary);
            if (item.secondary.type == "Question")
                questions_in_selected_items.push(item.secondary);
            if (item.tertiary != null && item.tertiary.type == "Question")
                questions_in_selected_items.push(item.tertiary);
        }
    });
    return questions_in_selected_items;
}

See also