Google Apps Script, Google Sheets, SpreadsheetApp, 2d arrays
In our last tutorial, we grabbed the Google Sheets count data from a single rating survey question. We then displayed it two ways; horizontally with the question item at the top, or vertically with the question item to the side.
In part 3 of our Google Sheets data transformation with Google Apps Script course we are going to expand on our current work and add two more rating question items from our survey. Each survey will have the same 1. Weak to 5. Strong survey items.
Our new list of question items will include:
- Rate your goat’s athleticism.
- Rate your goat’s agility.
- Rate you goat’s headbutt
All the information any self-respecting human companion for goats would know.
We’ll be updating our code to be able to take any number of item questions so long as they have the same choices.
Table of Contents
Goal: Count the frequency of each rating choice of each question item.
For this task, we will need to get the total frequency of all the choices for all the questions in our rating survey. Take a look at our response data.
To follow along with your own script, make a copy of the Google Sheet below:
As you can see, all the question types have the same choice item. We’ll be able to take advantage of this to quickly modify our old code and refit it for multiple question items.
We’ll be expanding onto our current display of the choice count data. It’s at this point that you will really start to see the two formats start to diverge. Here’s what you will end up with this time around:
Item question header, subheader choices, Choice count:
Check this monster out! Now you can really see why I’ve been calling this the horizontal arrangement of the choice count. Each question is displayed horizontally.
It doesn’t look the best in a post like this but it’s a nice flat display with all the question items as the main header and then each subheader is a choice item. This will be much more useful in Part 4 of our tutorial when we add groups to our mix, but mastering this stage will make for a smoother stepping stone.
Choices header, column A – questions:
This one is much more conducive to display data in a blog like this. The top header row consists of the choice items, while there is a nice neat column of questions in column A. We’ll discover in Part 4 some of this count transformation’s limitations.
Recycle and Refit
We will be using the core data structures we developed right up to Part 2 of this course. We still need to iterate through each question item and get the frequency count of each choice of that item. It just needs to be done for multiple questions.
We’ll also review some of our Part 2 code for any repetitions to see if we can put that portion of the code into a shared function.
Go ahead and grab your old code from Part 2 or you can copy it from below:
Part 2 files
Code.GS – Old
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
/* * Displaying a count of each selection based on a rating system with a set range. * * Contains a */ /*##### Display the count of a single question's selections ##### * This is the main run function. */ function runsies(){ //### INPUT ############### //### Add your choices here var QUESTION_RANGE = "B2"; var RESULT_RANGE = "B3:B26"; var CHOICES = ["1.Weak","2.Below Average","3.Average","4.Better Than Average","5.Strong"]; var DISPLAY_LOCATION_START = "D9"; var SOURCE_SHEET_NAME = "Rating Step 2"; var DESTINATION_SHEET_NAME = "Rating Step 2"; //######################### var ss = SpreadsheetApp.getActiveSpreadsheet(); var sourceSheet = ss.getSheetByName(SOURCE_SHEET_NAME); var resultsVals = sourceSheet.getRange(RESULT_RANGE).getValues(); var questionVals = sourceSheet.getRange(QUESTION_RANGE).getValue(); var displayResults = singleGroup_SingleQuestionVert_Rating(questionVals,resultsVals, CHOICES); //var displayResults = singleGroup_SingleQuestionHor_Rating(questionVals,resultsVals,CHOICES); var destinationSheet = ss.getSheetByName(DESTINATION_SHEET_NAME); pasteResults(DISPLAY_LOCATION_START,displayResults,destinationSheet); }; |
Trans.gs – OLD
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
/*##################################################################### * Choice for single group single question count of choices with a scaled rating. * * Creates a 1st header containing the question, a subheader with the response types and then a * row of count data. * * @param {string} question : The question item * @param {array} results : 2d array of question results. * * @returns {array} two row 2d array containing question, choices and count for each choice. */ /* From: [ [Question], [response], [response], [response] ] To: [ [Question , , , ], [Response type 1, Response type 2, ... , Response type #], [Response count1, Response count2, ... , Response count#] ] */ function singleGroup_SingleQuestionHor_Rating(question,results,choices){ var header = [question].concat(newArrayFill(choices.length-1, "")); var countRow = newArrayFill(choices.length,0); //Main result in the new order. Preset with Question on first row. var displayResults = [header,choices,countRow]; // Loop through list of results for(var res = 0; res < results.length; res++){ // If a response item already exists add 1 to corresponding column. for(var resp = 0; resp < displayResults[1].length; resp ++){ var choiceItemNumber = displayResults[1][resp][0]; if(results[res][0][0] === choiceItemNumber){ //Add to the count ++displayResults[2][resp]; }; }; }; return(displayResults); }; /*##################################################################### * Choice for single group single question count of choices with a scaled rating. * * Creates a header of row type and the next row of response count with the associated question in * the first colum. * * @param {string} question : The question item * @param {array} results : 2d array of question results. * * @returns {array} two row 2d array containing question, choices and count for each choice. */ /* From: [ [Question], [response], [response], [response] ] To [ [" ", Response type 1, Response type 2, ... , Response type #], [Question, Response count1, Response count2, ... , Response count#] ] */ function singleGroup_SingleQuestionVert_Rating(question,results,choices){ var header = [""].concat(choices); var countRow = [question].concat(newArrayFill(choices.length,0)); //Main result in the new order. Preset with space on first row and question on 2nd. var displayResults = [header,countRow]; // Loop through list of results for(var res = 0; res < results.length; res++){ // If a response item already exists add 1 to corresponding column. for(var resp = 1; resp < displayResults[0].length; resp ++){ var choiceItemNumber = displayResults[0][resp][0]; if(results[res][0][0] === choiceItemNumber){ //Add to the count ++displayResults[1][resp]; }; }; }; return(displayResults); }; |
ToolBox.gs – OLD
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
/*##################################################################### * Pasting results * * @param {string} StartLoc : The start cell of the location you want to paste to. * @param {array} results : 2d array of question results. * @param {object} sheet : The Sheet item. * */ function pasteResults(StartLoc, results, sheet){ var endLoc = sheet.getRange(StartLoc) .offset(results.length-1,results[0].length-1) .getA1Notation(); var range = sheet.getRange( StartLoc+":"+endLoc); range.setValues(results); }; /*##################################################################### * Creates a new array with selecte fill item. * * @param {number} len : Length of array. * @param {number|string} fillItem : Number or string to fill the array item. * @returns {array} The array the required length with required fill. * */ function newArrayFill(len, fillItem){ var arizzle = []; for(var i = 0; i < len; i++){arizzle[i] = fillItem}; return arizzle; }; |
The Code
Code.gs
Runsies()
I have some glorious news for you! All you will need to change here are your main variables – those ones we conveniently put in uppercase.
1 2 3 4 5 6 7 8 9 10 11 |
function runsies(){ //### Add your choices here var QUESTION_RANGE = "B2:D2"; var RESULT_RANGE = "B3:D26"; var CHOICES = ["1.Weak","2.Below Average","3.Average","4.Better Than Average","5.Strong"]; var DISPLAY_LOCATION_START = "G3"; var SOURCE_SHEET_NAME = "MultiQuestion 3"; var DESTINATION_SHEET_NAME = "MultiQuestion 3"; //######################### |
As you can see, we need to now grab all the questions in our Google Sheet data. We have three question items in our example so we will grab the range B2:D2. We’ll also need to drag our choice response range over to cover those three columns. We don’t need the item IDs so we will leave them out and start on column B.
Don’t forget to change the display location for where you want to paste the end result from your Google Apps Script to your sheet. We will slightly change the function names here and swap out the SingleQuestion
portion for the MultiQuestion
. Your two displayResults
variables should now look like this:
singleGroup_MultiQuestionHor_Rating()
singleGroup_MultiQuestionVert_Rating()
Lastly, make sure you update your source sheet (what you are copying from) and your destination sheet (the sheet you are pasting too).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
/* * Displaying a count of each question item's choices a survey. * * */ /*##### Display the count of each choice for each item (question) ##### * This is the main run function. */ function runsies(){ //### Add your choices here var QUESTION_RANGE = "B2:D2"; var RESULT_RANGE = "B3:D26"; var CHOICES = ["1.Weak","2.Below Average","3.Average","4.Better Than Average","5.Strong"]; var DISPLAY_LOCATION_START = "G3"; var SOURCE_SHEET_NAME = "MultiQuestion 3"; var DESTINATION_SHEET_NAME = "MultiQuestion 3"; //######################### var ss = SpreadsheetApp.getActiveSpreadsheet(); var sourceSheet = ss.getSheetByName(SOURCE_SHEET_NAME); var resultsVals = sourceSheet.getRange(RESULT_RANGE).getValues(); var questionVals = sourceSheet.getRange(QUESTION_RANGE).getValues(); //Loop through each Item //Count for each choice in each Item var displayResults = singleGroup_MultiQuestionHor_Rating(questionVals,resultsVals,CHOICES); //var displayResults = singleGroup_MultiQuestionVert_Rating(questionVals,resultsVals,CHOICES); var destinationSheet = ss.getSheetByName(DESTINATION_SHEET_NAME); pasteResults(DISPLAY_LOCATION_START,displayResults,destinationSheet); }; |
Trans.gs
We are going to shake things up a little bit in our Trans.gs file. After reviewing our old code we discovered a common pattern for our horizontal spread choice count data and vertical spread choice count data. Take a look at the similar patter in the Google Sheet:
If you look at the old Trans.gs code in part 2 you would see that there are two very similar sections of code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
function singleGroup_SingleQuestionHor_Rating(question,results,choices){ ... ... // Loop through list of results for(var res = 0; res < results.length; res++){ // If a response item already exists add 1 to corresponding column. for(var resp = 0; resp < displayResults[1].length; resp ++){ var choiceItemNumber = displayResults[1][resp][0]; if(results[res][0][0] === choiceItemNumber){ //Add to the count ++displayResults[2][resp]; }; }; }; ... ... }; function singleGroup_SingleQuestionVert_Rating(question,results,choices){ ... ... // Loop through list of results for(var res = 0; res < results.length; res++){ // If a response item already exists add 1 to corresponding column. for(var resp = 1; resp < displayResults[0].length; resp ++){ var choiceItemNumber = displayResults[0][resp][0]; if(results[res][0][0] === choiceItemNumber){ //Add to the count ++displayResults[1][resp]; }; }; }; ... ... }; |
These are essentially identical segments of code. The only difference is that they have different rows. This is just asking to be put in its own helper function and we are happy to oblige. We’ll call that helper function itemCount()
.
ItemCount(choices,results)
Let’s work a little backwards here. We’ll start off explaining our new universal helper function itemCount()
and then see how it works into both the horizontal and vertical choice count functions.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
/*##################################################################### * Get count for each choice of each question. * * gets the Item rating choice count for a single question Item. * * @param {array} results : 2d array of raw single question results. * @param {array} choices : array of choice options. * * @returns {array} : 2d array of count data for each choice in a single question item. */ /* From: [ [response A], [response A], [response A] ] To [ [Response count1, Response count2, ... , Response count#] ] */ function itemCount(choices, results) { var countResults = newArrayFill(choices.length,0); // Loop through list of results for(var res = 0; res < results.length; res++){ // If a response item already exists add 1 to corresponding column. for(var resp = 0; resp < choices.length; resp ++){ var choiceItemNumber = choices[resp][0]; if(results[res][0][0] === choiceItemNumber){ //Add to the count ++countResults[resp]; }; }; }; return(countResults); }; |
The itemCount()
function is called in either the singleGroup_MultiQuestionHor_Rating()
or singleGroup_MultiQuestionVert_Rating()
transformation functions. It requires two parameters:
- results: All the results data from a single item question. A single item row will be built before it is received in this function.
- choices: The choice items found in the primary variables at the top of the
runsies()
function.
First, we will set our array of count results for each choice to zero (Line 26).
Next, we will start our loop through each of the results.
Then we will loop through the choices and match the result to the choice. When a choice matches, we add one to the relevant column of our countResults
.
Finally, we will return all the count of each choice back to our transformation functions.
singleGroup_MultiQuestionHor_Rating()
Our 2d count array needs to look like this once we are done:
1 2 3 4 5 6 7 8 |
Q = Question Item R = Response Type Rc = Response count [ [Q A, , , ,Q B, , , Q C, , , , ], [R1 A, R2 A, ... , R# A,R1 B, R2 B, ... , R# C,R1 C, R2 C, ... , R# C], [Rc1A, Rc2A, ... , Rc#B,Rc1B, Rc2B, ... , Rc#C,Rc1C, Rc2C, ... , Rc#C] ] |
Now that we have shuffled off the actual counting of the responses to itemCount(), we will use the singleGroup_MultiQuestionHor_Rating()
function to simply set up the headers and create an iterator to loop through each question item.
The Variables
First of all, we will set up some variables:
1 2 3 4 5 6 7 8 |
function singleGroup_SingleQuestionHor_Rating(question,results,choices){ var numOfQuestions = results[0].length; var header = []; var subheader = []; var itemsCount = []; ... ... |
We are going get the numbers of questions so that we can loop through each question item. Our header, subheader and itemsCount
will all be set to zero.
The itemsCount
will accumulate the choice frequency data for all the question items as we iterate through each question.
Question Item Loop
Next, we will start our loop through each question item. Remember, our goal here each time we loop through the question item is to add the question item to the end of the row in the header along with some empty spaces the length of the choices minus one. Then in the subheader, we add a new array of our choices under each question item. Lastly, we add the item count for each choice to the end of the previous item count for the previous question item using the itemsCount
variable.
Take a look at the Google Sheet image again if you find this confusing. You can see that we need to add our block of data horizontally each iteration through the question items.
The Header
1 2 3 4 5 6 7 8 9 10 |
... //Paste Items horizontally after each iteration. for(var q = 0; q < numOfQuestions; q++){ //prep header row var emptyArray = newArrayFill(choices.length-1,""); var questionAndSpaces = [question[0][q]].concat(emptyArray); header = header.concat(questionAndSpaces); ... |
Just like in Part 2, we need to create our header with the question followed by 4 empty spaces to accommodate the 5 choices in the subheader. We’ll use our helper function newArrayFill()
we developed in Part 2 that is stored in your Toolbox.gs file. Next, we will concatenate the empty array to our iterated question. For example, our zeroeth question is “Rate your goat’s athleticism.” The header array will look like this:
["Rate your goat's athleticism.","","","",""]
Finally, each time we iterate through the questions we concatenate or add to the end of our current header. This will have the effect of adding each question plus 4 spaces to a single row.
The Subheader
1 2 3 4 5 6 |
... //prep subheader row subheader = subheader.concat(choices); ... |
Just like in part 2, our subheader is simply the list of choices. This will look like this:
["1.Weak","2.Below Average","3.Average","4.Better Than Average", "5.Strong"];
After each iteration through the question items, another list of choices will be added to the end of the array with concat.
Getting the item count of each question
1 2 3 4 5 6 7 8 9 10 |
... //prep count row var questionResults =results.map(function(row){ return row[q]; }); var countRes = itemCount(choices,questionResults); itemsCount = itemsCount.concat(countRes); ... |
Before we can use our dinky little itemCount()
helper function, we need to ensure that our function is only looking at the relevant question column. To do this we map the results array to only include the relevant question item column*.
The map method takes a function. It essentially loops through the array and can spit out any transformation array you design. For us, we want to create a new array of all the data of just one question item. We return each row with the iterator “q” being the question item column.
Now that we have our single set of choices for our question we can use our itemCount()
function.
Remember our itemCount
function will return just the frequency of the choices of the current question in the loop. For example:
[3, 3, 5, 6, 7]
In each loop through the questions, we will concatenate the itemCount
to the itemsCount
(Note the “s”). The end result will look like this for our example:
[3, 3, 5, 6, 7, 2, 2, 5, 7, 8, 1, 2, 6, 9, 6]
*I sometimes bounce from using loops to map, filter and reduce. I think that in some cases a ‘for’ loop is more readable than a method like map or filter. Also – but not in the case of this course – I will use loops over map, filter and reduce, just to a squeeze a few milliseconds to seconds out of a large data set in the hope to stay under the 6-minute limit for consumer accounts.
Putting it all together
return [header, subheader, itemsCount];
Finally, we return all three rows of data back to the runsies()
function.
Take a look at the full function.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
//####################### HORIZONTAL SPREAD ############################ //##################################################################### /*##################################################################### * Get count for each choice of each question. * * Creates a vertical choice spread of data from multiple questions. * * @param {array} questions : 2d array of question items. * @param {array} results : 2d array of raw results. * @param {array} choices : array of choice options. * * @returns {array} : 2d array of count data for each item choice. */ /* /* From: [ [Question A, Question B, Question C], [response A, response B, response C], [response A, response B, response C], [response A, response B, response C] ] To: Q = Question Item R = Response Type Rc = Response count [ [Q A, , , ,Q B, , , Q C, , , , ], [R1 A, R2 A, ... , R# A,R1 B, R2 B, ... , R# C,R1 C, R2 C, ... , R# C], [Rc1A, Rc2A, ... , Rc#B,Rc1B, Rc2B, ... , Rc#C,Rc1C, Rc2C, ... , Rc#C] ] */ function singleGroup_mulitQuestionHor_Rating(question,results,choices){ var numOfQuestions = results[0].length; var header = []; var subheader = []; var itemsCount = []; //Paste Items horizontally after each iteration. for(var q = 0; q < numOfQuestions; q++){ //prep header row var emptyArray = newArrayFill(choices.length-1,""); var questionAndSpaces = [question[0][q]].concat(emptyArray); header = header.concat(questionAndSpaces); //prep subheader row subheader = subheader.concat(choices); //prep count row var questionResults =results.map(function(row){ return row[q]; }); var countRes = itemCount(choices,questionResults); itemsCount = itemsCount.concat(countRes); }; return [header, subheader, itemsCount]; }; |
singleGroup_MultiQuestionVer_Rating()
Our 2d count array needs to look like this once we are done:
1 2 3 4 5 6 |
[ [" ", Choice type 1 , Choice type 2 , ... , Choice type # ], [Item A, Response count1, Response count2, ... , Response count#], [Item B, Response count1, Response count2, ... , Response count#], [Item C, Response count1, Response count2, ... , Response count#] ] |
As you can see, this one looks a little neater than the horizontal version. Our main header consists of the item choices, while each question item is listed to the left before the count results are displayed to the right.
The Variables
1 2 3 4 |
function singleGroup_SingleQuestionVert_Rating(question,results,choices){ var numOfQuestions = results[0].length; var header = [[""].concat(choices)]; //2d array var itemRows = []; |
First off, we are going to check how many questions we have in our list. Back in our runsies()
function, we called in our main question item results from our Google Sheet. These question results were stored as a 2d array. The number of questions run across each column of our question row. The column items are one level deep in our row, so we need to call results[0].length
instead of results.length
.
For our header, we will be using the choices. We need to keep in mind that in subsequent rows, we will be adding the question item to the first column. Add a single empty character element array ( [“”] ) and then concatenate the choices to it. Make sure you nest this inside an outer array. The reason why we are doing this is that we are going to use concat later to join this array to our count data which will also be a 2d array.
We will store all the count result of each choice as we iterate through each question in our itemRows
variable. We will create an empty array now to store these results.
Question Item Loop
Once we have all our variables all set up, we will loop through each of our question items. Each question item matches the row of the choices for that question, so we will be able to use this for our column reference.
First, we will set our itemQuestion
. In our first iteration, this would be “Rate your goat’s athleticism”.
Getting the item count of each question
Just like in our horizontal view of our question item choice count we will make use of our new itemCount()
variable to loop through each choice for each question and get the total frequency of each item selected.
1 2 3 4 5 |
//prep count row var questionResults =results.map(function(row){ return row[q]; }); var countRes = itemCount(choices,questionResults); |
We’ll first create a new 2d array with map that will contain only the variables for that item. Next, we will run our helper function itemCount
.
Adding the question item to the count data
1 2 |
//Add item question to count results itemRows.push(itemQuestion.concat(countRes)); |
Once we get the count responses for the current question, we want to add the current question to the start of the row’s array. Then we want to add the current iteration of the combined question and choice count results to the itemRows
variable. Each question iteration it will add another row to our data.
Putting it all together
Finally, we’ll add the itemRows
to the header
containing the choices and return it to the main runsies()
function.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
//####################### VERTICAL SPREAD ############################ //##################################################################### /*##################################################################### * Get count for each choice of each question. * * Creates a vertical choice spread of data from multiple questions. * * @param {array} questions : 2d array of question items. * @param {array} results : 2d array of raw results. * @param {array} choices : array of choice options. * * @returns {array} : 2d array of count data for each item choice. */ /* /* From: [ [Item A , Item B , Item C ], [response A, response B, response C], [response A, response B, response C], [response A, response B, response C] ] To [ [" ", Choice type 1 , Choice type 2 , ... , Choice type # ], [Item A, Response count1, Response count2, ... , Response count#], [Item B, Response count1, Response count2, ... , Response count#], [Item C, Response count1, Response count2, ... , Response count#] ] */ function singleGroup_MultiQuestionVert_Rating(question,results,choices){ var numOfQuestions = results[0].length; var header = [[""].concat(choices)]; //2d array var itemRows = []; for(var q = 0; q < numOfQuestions; q++){ var itemQuestion = [question[0][q]]; //prep count row var questionResults =results.map(function(row){ return row[q]; }); var countRes = itemCount(choices,questionResults); //Add item question to count results itemRows.push(itemQuestion.concat(countRes)); }; return header.concat(itemRows); } //##################################################################### |
Conclusion
The Complete Code
Code.gs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
/* * Displaying a count of each question item's choices a survey. * * */ /*##### Display the count of each choice for each item (question) ##### * This is the main run function. */ function runsies(){ //### Add your choices here var QUESTION_RANGE = "B2:D2"; var RESULT_RANGE = "B3:D26"; var CHOICES = ["1.Weak","2.Below Average","3.Average","4.Better Than Average","5.Strong"]; var DISPLAY_LOCATION_START = "G3"; var SOURCE_SHEET_NAME = "MultiQuestion 3"; var DESTINATION_SHEET_NAME = "MultiQuestion 3"; //######################### var ss = SpreadsheetApp.getActiveSpreadsheet(); var sourceSheet = ss.getSheetByName(SOURCE_SHEET_NAME); var resultsVals = sourceSheet.getRange(RESULT_RANGE).getValues(); var questionVals = sourceSheet.getRange(QUESTION_RANGE).getValues(); //Loop through each Item //Count for each choice in each Item var displayResults = singleGroup_MultiQuestionHor_Rating(questionVals,resultsVals,CHOICES); //var displayResults = singleGroup_MultiQuestionVert_Ratin(questionVals,resultsVals,CHOICES); var destinationSheet = ss.getSheetByName(DESTINATION_SHEET_NAME); pasteResults(DISPLAY_LOCATION_START,displayResults,destinationSheet); }; |
Trans.gs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 |
//####################### HORIZONTAL SPREAD ############################ //##################################################################### /*##################################################################### * Get count for each choice of each question. * * Creates a vertical choice spread of data from multiple questions. * * @param {array} questions : 2d array of question items. * @param {array} results : 2d array of raw results. * @param {array} choices : array of choice options. * * @returns {array} : 2d array of count data for each item choice. */ /* /* From: [ [Question A, Question B, Question C], [response A, response B, response C], [response A, response B, response C], [response A, response B, response C] ] To: Q = Question Item R = Response Type Rc = Response count [ [Q A, , , ,Q B, , , Q C, , , , ], [R1 A, R2 A, ... , R# A,R1 B, R2 B, ... , R# C,R1 C, R2 C, ... , R# C], [Rc1A, Rc2A, ... , Rc#B,Rc1B, Rc2B, ... , Rc#C,Rc1C, Rc2C, ... , Rc#C] ] */ function singleGroup_MultiQuestionHor_Rating(question,results,choices){ var numOfQuestions = results[0].length; var header = []; var subheader = []; var itemsCount = []; //Paste Items horizontally after each iteration. for(var q = 0; q < numOfQuestions; q++){ //prep header row var emptyArray = newArrayFill(choices.length-1,""); var questionAndSpaces = [question[0][q]].concat(emptyArray); header = header.concat(questionAndSpaces); //prep subheader row subheader = subheader.concat(choices); //prep count row var questionResults =results.map(function(row){ return row[q]; }); var countRes = itemCount(choices,questionResults); itemsCount = itemsCount.concat(countRes); }; return [header, subheader, itemsCount]; }; //####################### VERTICAL SPREAD ############################ //##################################################################### /*##################################################################### * Get count for each choice of each question. * * Creates a vertical choice spread of data from multiple questions. * * @param {array} questions : 2d array of question items. * @param {array} results : 2d array of raw results. * @param {array} choices : array of choice options. * * @returns {array} : 2d array of count data for each item choice. */ /* /* From: [ [Item A , Item B , Item C ], [response A, response B, response C], [response A, response B, response C], [response A, response B, response C] ] To [ [" ", Choice type 1 , Choice type 2 , ... , Choice type # ], [Item A, Response count1, Response count2, ... , Response count#], [Item B, Response count1, Response count2, ... , Response count#], [Item C, Response count1, Response count2, ... , Response count#] ] */ function singleGroup_MultiQuestionVert_Rating(question,results,choices){ var numOfQuestions = results[0].length; var header = [[""].concat(choices)]; //2d array var itemRows = []; for(var q = 0; q < numOfQuestions; q++){ var itemQuestion = [question[0][q]]; //prep count row var questionResults =results.map(function(row){ return row[q]; }); var countRes = itemCount(choices,questionResults); //Add item question to count results itemRows.push(itemQuestion.concat(countRes)); }; return header.concat(itemRows); } //##################################################################### /*##################################################################### * Get count for each choice of each question. * * gets the Item rating choice count for a single question Item. * * @param {array} results : 2d array of raw results. * @param {array} choices : array of choice options. * * @returns {array} : 2d array of count data for each choice in a single question item. */ /* From: [ [response A], [response A], [response A] ] To [ [Response count1, Response count2, ... , Response count#] ] */ function itemCount(choices, results) { var countResults = newArrayFill(choices.length,0); // Loop through list of results for(var res = 0; res < results.length; res++){ // If a response item already exists add 1 to corresponding column. for(var resp = 0; resp < choices.length; resp ++){ var choiceItemNumber = choices[resp][0]; if(results[res][0][0] === choiceItemNumber){ //Add to the count ++countResults[resp]; }; }; }; return(countResults); }; |
ToolBox.gs – No change
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
/*##################################################################### * Sort 2d Array by selected column. * * @param {array} results: 2d Array of range data. * @param {string} col: Selected column number to sort by. * * @param {returns} A sorted 2d array of data. */ function sortByColumn(results, col) { results.sort(function(a, b) { if (a[col] === b[col]) { return 0; } else { return (a[col] < b[col]) ? -1 : 1; } }); return results }; /*##################################################################### * Pasting results * * @param {string} StartLoc : The start cell of the location you want to paste to. * @param {array} results : 2d array of question results. * @param {object} sheet : The Sheet item. * */ function pasteResults(StartLoc, results, sheet){ var endLoc = sheet.getRange(StartLoc) .offset(results.length-1,results[0].length-1) .getA1Notation(); var range = sheet.getRange( StartLoc+":"+endLoc); range.setValues(results); }; /*##################################################################### * Creates a new array with selecte fill item. * * @param {number} len : Length of array. * @param {number|string} fillItem : Number or string to fill the array item. * @returns {array} The array the required length with required fill. * */ function newArrayFill(len, fillItem){ var arizzle = []; for(var i = 0; i < len; i++){arizzle[i] = fillItem}; return arizzle; }; |
Last Bit
To wrap up, we have now updated our code to accept multiple question items. We have shipped off collecting the frequency count for each choice of each question to the itemCount()
helper function for both the horizontal and vertical displays of the count data.
We haven’t made any additions to the ToolBox.gs file, but we still use it to create arrays and submit our end results to the Google Sheet.
In our last part of our Google Apps Script array data transformation course we will transform our frequency count to not only include multiple questions, but also multiple groups. Again, we will be working off the same code and modify it for our new purpose. We will also look at some of the limitations and challenges to simply modifying code as opposed to building it from scratch. Hopefully, this will give you some insight into the decision making you need to prepare your own projects.
Subscribe now (Up the top right) so get notified when Part 4 is out!
Need help with Google Workspace development?
My team of experts can help you with all of your needs, from custom app development to integrations and security. We have a proven track record of success in helping businesses of all sizes get the most out of Google Workspace.
Schedule a free consultation today to discuss your needs and get started or learn more about our services here.