Google Script (Forms) - Cannot Convert Array to Choice[] - javascript

I have looking into this issue for a while, but have yet to find a suitable answer (most involve switching to setChoiceValues() rather than addressing the "Cannot convert Array to Choice[]" issue with setChoices([])).
While attempting to generate form sections and questions via Google Script, I ran into the issue of not getting my answer selections to go to specific pages based on the user's answer. This appears to be the difference between setChoiceValues() and setChoices([]), with the latter allowing for page navigation as best as I can tell.
However, when attempting to put my array of new choices into setChoices([]), I get the error message "Cannot convert Array to Choice[]". My code works fine otherwise, but I need to use setChoices([]) (it seems) in order to get the page navigation that I want.
How can I loop values into an array or other container and be able to make them appear as a Choices[] object? How can I make something like this work? It seems like it should be much easier than it is, but I cannot see the solution.
Below is a segment of my code that is causing the issue:
//Form - globally accessible
var f = FormApp.openById(f_id);
//Date Iterator
var curr_date = 0;
//Time Iterator
var curr_time = 0;
//Array of Times
var Tchoices = [];
//Setting Time choices per date
while(curr_date < dates.length)
{
Tchoices = [];
curr_time = 0;
//dates is an array of objects with both d's (single date) and t's
// (array of times for that date)
var d = dates[curr_date].d;
var end_break = f.addPageBreakItem().setTitle("Times for " + d);
var f_time = f.addMultipleChoiceItem().setTitle(d);
while(curr_time < dates[curr_date].t.length)
{
end_break = end_break.setGoToPage(FormApp.PageNavigationType.SUBMIT);
Tchoices.push(f_time.createChoice(dates[curr_date].t[curr_time], end_break).getValue());
curr_time++;
}
f_time.setChoices([Tchoices]);
}

There was some minor issues with the building of your MultipleChoise object:
//Form - globally accessible
var f = FormApp.openById('someID');
//Date Iterator
var curr_date = 0;
//Time Iterator
var curr_time = 0;
//Array of Times
var Tchoices = [];
//Setting Time choices per date
while(curr_date < dates.length)
{
Tchoices = [];
curr_time = 0;
//dates is an array of objects with both d's (single date) and t's
// (array of times for that date)
var d = dates[curr_date].d;
var end_break = f.addPageBreakItem().setTitle("Times for " + d);
var f_time = f.addMultipleChoiceItem();
f.addMultipleChoiceItem().setTitle(d);
while(curr_time < dates[curr_date].t.length) //verify this while not sure what you have inside your dates array
{
end_break = end_break.setGoToPage(FormApp.PageNavigationType.SUBMIT);
Tchoices.push(f_time.createChoice(dates[curr_date].t[curr_time])); //You cannot add a pagebreak inside the elements of a choiseItem array
curr_time++;
}
Logger.log(Tchoices);
f_time.setChoices(Tchoices);
}
Check the values for dates[curr_date].t.length inside the Loop I'm not sure how you constructed the array.
You cannot add a pagebreak inside the elements of a choiseItem array

Related

Google apps script - using for loop to pull data from range based on condition

I'm having an issue pulling the correct values out of a for loop in Google Sheets.
Here's my code:
Note: this is a snippet from a larger function
function sendEmails() {
var trackOriginSheet = SpreadsheetApp.getActiveSpreadsheet().getName();
var getMirSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Miranda");
//Set a new object to hold conditional data
var holdingData = new Object();
//Create function to get values from origin sheet
var returnedValues = function (trackOriginSheet) {
//Load dynamic variables into an object via returnedValues()
if (trackOriginSheet === getMirSheet) {
var startMirRow = 2; // First row of data to process
var numRowsMir = 506; // Number of rows to process
// Fetch the range of cells A2:Z506
var dataRangeMir = getMirSheet.getRange(startMirRow, 1, numRowsMir, 26);
// Fetch values for each cell in the Range.
var dataMir = dataRangeMir.getValues();
for (var k in dataMir) {
var secondRowMir = dataMir[k];
var intRefDescMir = secondRowMir[3];
var intAdminActionsMir = secondRowMir[4];
//Push returned data to holdingData Object
holdingData.selectedData = secondRowMir;
holdingData.refDesc = intRefDescMir;
holdingData.adminActions = intAdminActionsMir;
}
}
}
Here's a copy of the sheet I'm working on
What I need to have happened here first, is track the origin sheet, then create an object to hold data returned from the returnedValues() function. Later, I'll call the properties of this object into a send email function.
The problem is that I need to be able to pull data from the selected sheet dynamically (the "Miranda" sheet in this case.) In other words, when a user selects the "Yes" option in column I of the Miranda sheet, the first thing this script needs to do is pull the values of the variables at the top of the for loop within the same row that the user selected "Yes." Then, I'm pushing that data to a custom object to be called later.
It's apparent to me, that I'm doing it wrong. There's, at least, something wrong with my loop. What have I done? :)
EDIT:
After reviewing the suggestion by VyTautas, here's my attempt at a working loop:
for (var k = 0; k < dataMir.length; k++) {
var mirColI = dataMir[k][8];
var mirRefDesc = dataMir[k][2];
var mirAdminActions = dataMir[k][3];
var mirDates = dataMir[k][4];
if (mirColI === "Yes") {
var activeRowMir = mirColI.getActiveSelection.getRowIndex();
//Pull selected values from the active row when Yes is selected
var mirRefDescRange = getMirSheet.getRange(activeRowMir, mirRefDesc);
var mirRefDescValues = mirRefDescRange.getValues();
var mirAdminActionsRange = getMirSheet.getRange(activeRowMir, mirAdminActions);
var mirAdminActionsValues = mirAdminActionsRange.getValues();
var mirDatesRange = getMirSheet.getRange(activeRowMir, mirDates);
var mirDatesValues = mirAdminActionsRange.getValues();
var mirHoldingArray = [mirRefDescValues, mirAdminActionsValues, mirDatesValues];
//Push mirHoldingArray values to holdingData
holdingData.refDesc = mirHoldingArray[0];
holdingData.adminActions = mirHoldingArray[1];
holdingData.dates = mirHoldingArray[2];
}
}
Where did all that whitespace go in the actual script editor? :D
You already correctly use .getValues() to pull the entire table into an array. What you need to do now is have a for loop go through dataMir[k][8] and simply fetch the data if dataMir[k][8] === 'Yes'. I also feel that it's not quite necessary to use for (var k in dataMir) as for (var k = 0; k < dataMir.length; k++) is a lot cleaner and you have a for loop that guarantees control (though that's probably more a preference thing).
You can also reduce the number of variables you use by having
holdingData.selectedData = mirData[k]
holdingData.refDesc = mirData[k][2] //I assume you want the 3rd column for this variable, not the 4th
holdingData.adminActions = mirData[k][3] //same as above
remember, that the array starts with 0, so if you mirData[k][0] is column A, mirData[k][1] is column B and so on.
EDIT: what you wrote in your edits seems like doubling down on the code. You already have the data, but you are trying to pull it again and some variables you use should give you an error. I will cut the code from the if, although I don't really see why you need to both get the active sheet and sheet by name. If you know the name will be constant, then just always get the correct sheet by name (or index) thus eliminating the possibility of working with the wrong sheet.
var titleMirRows = 1; // First row of data to process
var numRowsMir = getMirSheet.getLastRow(); // Number of rows to process
// Fetch the range of cells A2:Z506
var dataRangeMir = getMirSheet.getRange(titleMirRows + 1, 1, numRowsMir - titleMirRows, 26); // might need adjusting but now it will only get as many rows as there is data, you can do the same for columns too
// Fetch values for each cell in the Range.
var dataMir = dataRangeMir.getValues();
for (var k = 0; k < dataMir.length; k++) {
if (dataMir[k][7] === 'Yes') { //I assume you meant column i
holdingData.refDesc = dataMir[k] //this will store the entire row
holdingData.adminActions = dataMir[k][3] //this stores column D
holdingData.dates = dataMir[k][4] //stores column E
}
}
Double check if the columns I have added to those variables are what you want. As I understood the object stores the entire row array, the value in column called Administrative Actions and the value in column Dates/Periods if Applicable. If not please adjust accordingly, but as you can see, we minimize the work we do with the sheet itself by simply manipulating the entire data array. Always make as few calls to Google Services as possible.

Merging Multiple Arrays Evenly/Alternating with Javascript and Google AppScript

I'm trying to merge multiple arrays evenly/alternating in javascript/Google appScript. There are several arrays (5 or 6). I've tried 2 different methods, but neither worked. I don't work a lot with javascript honestly and I've managed to get the code to this point, but I can't get it merge properly; and most of them said merging two arrays to one (might be my problem).
I've seen plenty on php examples that were on how to do this and they are pretty straight forward in logic reading and I understand them better, but all javascript methods I've looked at and tried so far have failed to produce the results I want. I'm not sure if it's the way AppScript is formatting the arrays or they're just no made to handle more that 2.
My data looks similar to this at the moment:
var title = ["sometitle1","sometitle2","sometitle3"];
var link = ["somelink1","somelink2","somelink3"];
var date = ["somedate1","somedate2","somedate3"];
var data = ["somedata1","somedata2","somedata3"];
var all = [title,link,date,data];
var mix = [];
Note: all the variable data will/should be the same length since the data is being pulled from a spreadsheet.
My desired output is:
mix = ["sometitle1","somelink1","somedate1","somedata1","sometitle2","somelink2","somedate2","somedata2","sometitle3","somelink3","somedate3","somedata3"];
I tried using appscript to merge them with this: return ContentService.createTextOutput(title + link + data + date), but it didn't work out properly, it printed them in that order instead of merging the way I'd like them too.
Then I tried using a loop merge that I found here on sstackoverflow:
for (var i = 0; all.length !== 0; i++) {
var j = 0;
while (j < all.length) {
if (i >= all[j].length) {
all.splice(j, 1);
} else {
mix.push(all[j][i]);
j += 1;
}
}
}
But it splice merges every letter with a comma
mix = [s,o,m,e,t,i,t,l,e,1,s,o,m,e,t,i,t,l,e,2,s,o,m,e,t,i,t,l,e,3,s,o,m,e,l,i,n,k,1,...]
and doesn't alternate data either.
The code (2 version) I'm working on is: here with Output
&
Here with Output
(Also, dumb question, but do I use title[i] + \n OR title[i] + "\n" for adding new lines?)
Use a for loop and the push() method like this :
function test(){
var title = ["sometitle1","sometitle2","sometitle3"];
var link = ["somelink1","somelink2","somelink3"];
var date = ["somedate1","somedate2","somedate3"];
var data = ["somedata1","somedata2","somedata3"];
//var all = [title,link,date,data];
var mix = [];
for(var n=0;n<title.length;n++){
mix.push(title[n],link[n],date[n],data[n]);
}
Logger.log(JSON.stringify(mix));
}
And also : title[i] + "\n" for adding new lines
Edit following comments :
Your code should end like this :
...
for(var n=0;n<titles.length;n++){
mix.push(titles[n],links[n],descriptions[n],pubdates[n],authors[n]);
}
var mixString = mix.join('');// convert the array to a string without separator or choose the separator you want by changing the argument.
//Print data and set mimetype
return ContentService.createTextOutput(mixString)
.setMimeType(ContentService.MimeType.RSS);
}

Pushing object to array results in same value

I have the following javascript code that does not work as I would expect it to. I have a list of checkboxes of which two of the items are "TestDuration" and "AssessmentScores". I'm trying to iterate through the list (which works fine) and have it add the values that are checked to the array.
var SAIndex = 0;
var SSIndex = 0;
var ScoresIndex = 0;
var SubAssessments = [];
var SubAssessmentScores = [];
//Get to the container element
var SSList = document.getElementById("islSubAssessmentScore_container");
//turn it into an array of the checkbox inputs
SSList = SSList.getElementsByTagName("input");
//create a temporary object to store my values
var tempPair = new Object();
//iterate through the checkbox lists
for(var i = 1; i < SSList.length;i++)
{
//if the value is checked add it to the array
if (SSList[i].checked)
{
var P = SubAssessments[SAIndex];
var V = SSList[i].value;
//tempPair.Parent = SubAssessments[SAIndex];
tempPair.Parent = P;
//tempPair.Value = SSList[i].value;
tempPair.Value = V;
//show me the values as they exist on the page
alert(tempPair.Parent + "|" + tempPair.Value);
SubAssessmentScores.push(tempPair);
//show me the values I just added to the array
alert(SubAssessmentScores.length-1 + "|" + SubAssessmentScores[SubAssessmentScores.length-1].Parent + "|" + SubAssessmentScores[SubAssessmentScores.length-1].Value);
//uncheck the values so when I refresh that section of the page the list is empty
SSList[i].checked = false;
}
}
//output the list of objects I just created
for (i = 0;i < SubAssessmentScores.length;i++)
alert(i + "|" + SubAssessmentScores[i].Parent + "|" + SubAssessmentScores[i].Value)
Now what happens is that when I iterate through the list I get the following alerts:
-first pass-
StudentID|TestDuration
0|StudentID|TestDuration
-second pass-
StudentID|AssessmentScores
1|StudentID|AssessmentScores
This is what I expect to output... However at the end of the code snippet when it runs the for loops to spit out all the values I get the following alerts...
0|StudentID|AssessmentScores
1|StudentID|AssessmentScores
I can't for the life of me figure out why it's replacing the first value with the second value. I thought it might be using a reference variable which is why I added in the P and V variables to try to get around that if that was the case, but the results are the same.
This is because you are adding the same variable every iteration of the loop.
Try changing your push like this:
SubAssessmentScores.push({
Parent: P,
Value: V
});
That said, I recommend you study a little more javascript and conventions in the language, for example your variable naming is frowned upon because you should only use capital letters on the beginning of a name for constructor functions.
A good book is Javascript the good parts by Douglas Crockford.

how to define array size inside objects in JavaScript?

Is there a way to define array size inside objects in Java Script?
Like we do in C or C++, when we want user to enter values in array, we define a size and populate it using indexes according to user. Display is done using something like n[4] (assuming n is an array.)
function activity(id, name, description, prior, post, start, end, current, status) {
this.id = id; //activity id
this.name = name; //activity name
this.description = description; //activity description
**this.prior = prior[]; // prior activities
this.post = post[]; //post activities**
this.start = start; //activity start date
this.end = end; //activity end date
this.currentWork = currentWork; //current work that has been done
this.status = status; //current status
}
I want prior and post to be arrays of size 3. I am making 18 instances of above object, each one with different value. Tell me how to access these values how to enter values in this array?
You can create an array of a specified size with:
this.prior = new Array(3);
However, Javascript arrays are not a fixed size -- if you assign beyond the end, it will automatically grow. So normally just just create an empty array:
this.prior = [];
and then assign elements or use this.prior.push(newElement) to add to it.
see this
array in javascript grows dynamically so according to that post you can just store the length in some variable and use it as for looping. like this
var data = [];
var arrlength = 10 ; // user-defined length
for(var i = 0; i < arrlength; i++) {
}

Array sorting creating linked arrays?

I am creating a Chrome extension and am getting some weird results with sorted arrays. I have two global arrays called "timearray" and "timearrayorig" (timearray is the sorted version of timearrayorig). In a function, I set a bunch of values in timearrayorig and then copy the entire array to timearray and sort timearray. For some reason, this also sorts timearrayorig. I would greatly appreciate it if someone could explain why this is the case.
for (var i = 0; i < triparray.length; i++) {
for (var j = 0; j < trainsfeed.length; j++) {
if (trainsfeed[j].getElementsByTagName('Trip')[0].childNodes[0].nodeValue == triparray[i]) {
if (timearrayorig.length < i + 1 || timearrayorig[i] > Number(trainsfeed[j].getElementsByTagName('Scheduled')[0].childNodes[0].nodeValue)) {
timearrayorig.push(Number(trainsfeed[j].getElementsByTagName('Scheduled')[0].childNodes[0].nodeValue));
}
}
}
}
timearray = timearrayorig;
//timearray.sort();
(trainsfeed is a bunch of XML separated by messages and triparray is the list of all the different values for the "Trip" field. timearrayorig and timearray are the earliest times for each element of triparray from the elements of trainsfeed.)
If I run this script and find the value of timearrayorig and timearray in the debug console, they are the same, for example [1365801720, 1365801180, 1365801600, 1365802800, 1365800940]. But when I sort timearray, they both become [1365800940, 1365801180, 1365801600, 1365801720, 1365802800].
timearray = timearrayorig;
This doesn't copy the array; it creates a second variable which refers to the same array. There is still only one array, which is why sorting it affects both variables. To copy the array, do:
var timearray = timearrayorig.slice();
For more details, see: Copying array by value in JavaScript.
timearrayorig is holding a reference to the array, so when you assign timearray = timearrayorig; both labels reference the same memory space.
If you want to copy the array you can do something like:
timearray = [];
for(var i = 0; i < timearrayorig.length; i++) timerray[i] = timearrayorig[i];

Categories