Google App Script to Break out Query Parameters in Google Sheets - javascript

I have a Google Sheet with 100 https request URLs with query parameters. The URLs look like this:
https://122.2o7.net/b/ss/ryan1/1/JS-2.0.0/s12345678?AQB=1&ndh=1&pf=1&t=6%2F9%2F2018%208%3A48%3A34%206%20360&ts=1538837314190&vid=test&fid=1w23232-erwwwre&ce=UTF-8&ns=ryan&pageName=ryan%3Atest%3Apage&g=https%3A%2F%2Fryanpraski.com%2F&cc=USD&ch=home&events=event1&c1=D%3Dv1&v1=evar1value&h1=hier1value&v20=evar20value&bh=8&AQE=1
I want to use Google App Script to break out the query parameters and put them neatly into the Google Sheet like this:
I got as far as the code below to break query string and split the query string parameters by the & delimiter, but I am not sure what to do next.
A couple cases that I need to consider as well.
There could be URLs with more or fewer parameters than my sample URL, but there will always be some overlay. I want to have the column headers automatically update.
There could be values like c1=D%3Dv1 where the decoded value is c1=D=v1
Any help would be greatly appreciated!
function test() {
var url = "https://122.2o7.net/b/ss/ryan1/1/JS-2.0.0/s12345678?AQB=1&ndh=1&pf=1&t=6%2F9%2F2018%208%3A48%3A34%206%20360&ts=1538837314190&vid=test&fid=1w23232-erwwwre&ce=UTF-8&ns=ryan&pageName=ryan%3Atest%3Apage&g=https%3A%2F%2Fryanpraski.com%2F&cc=USD&ch=home&events=event1&c1=D%3Dv1&v1=evar1value&h1=hier1value&v20=evar20value&bh=8&AQE=1";
var cleanUrl = decodeURIComponent(url);
var params = cleanUrl.split('?')[1];
var s = params;
var t = s.split('&');
var output = [];
t.forEach(function(q) {
output.push([q]);
});
Logger.log(output);
}

The following code breaks out the query parameters and puts them into a specific sheet. It also addresses a couple of possible scenarios:
1 There is no match for an existing code. In that case, a space is entered as a place holder.
2 The URL includes codes not included in the existing list. In that case, the "new" code(s) are added to the list, and their values are recorded also.
3 As the questioner pointed out, some URL parameters include multiple "Equals" signs ("="). Split can't be used in this case because though a parameter can be used to limit the number of split found, the left-over text is not returned in the new array. So I used indexOf (which returned the index of the first occurrence of searchValue) and subString to calculate the two parts of the URL component.
I assumed that the existing list of codes was in Row1, so I created a NamedRange to be able to manage them. If the code finds URL parameters that don't find a match with the codes in the Named Range, then the NamedRange is deleted and re-created to include the "new" codes.
The code outputs results to the "third sheet" (ss.getSheets()2;) in the spreadsheet; this is something that can be changed.
The last row containing data is determined, and the results of the analysis are set in the following row
Note: the url is hard coded.
function so_52825789() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var datasheet = ss.getSheets()[2];
// the codes are in Row1 in a Named Range called DataVariables
var urlvars = ss.getRangeByName('DataVariables').getValues();
// get the number of Columns for the Named Range
var datalen = urlvars[0].filter(String).length;
//Logger.log("Number of columns of codes = "+datalen); //DEBUG
//get the last row of containing data
var lastvals = ss.getRange("A1:A").getValues();
var lastrow = lastvals.filter(String).length;
//Logger.log("the last row is "+lastrow);// DEBUG
// Get the url
var url = "https://122.2o7.net/b/ss/ryan1/1/JS-2.0.0/s12345678?AQB=1&ndh=1&pf=1&t=6%2F9%2F2018%208%3A48%3A34%206%20360&ts=1538837314190&vid=test&fid=1w23232-erwwwre&ce=UTF-8&ns=ryan&pageName=ryan%3Atest%3Apage&g=https%3A%2F%2Fryanpraski.com%2F&cc=USD&ch=home&events=event1&c1=D%3Dv1&v1=evar1value&h1=hier1value&v20=evar20value&bh=8&AQE=1&ejb=1";
// Javascript function to remove the URL encoded charaters
var cleanUrl = decodeURIComponent(url);
// get the URL variables (the second half of the split)
var params = cleanUrl.split('?')[1];
var s = params;
// get the specific query variables by spliting on "&"
var t = s.split('&');
// get the number of query variables
var tlen = t.filter(String).length;
// setup some variables for use later
var output = [];
var mismatchcode = [];
var mismatchdata = [];
var tcount = [];
var nomatch = 0;
var ttest = 0;
var ztest = 0;
// Loop through the known codes from the Named Range
for (i = 0; i < datalen; i++) {
// set a variable value so that you can count how many named codes were found
ttest = 1;
// Start a loop though the query variables in the URL
for (z = 0; z < tlen; z++) {
// get the position of the Equals sign "="; there may be more than one but we only want the first one.
var n = t[z].indexOf("=");
if (n > 0) {
//var result="Equals appears at position = "+(n+1)+" (actual value = "+n+")";
//Logger.log(result);
//get the length of the element
var nstr = t[z].length;
//Logger.log("Length = "+nstr); //DEBUG
// break the element into two halves. The first half is the "Code" and the second half is the "value"
var code = t[z].substring(0, n);
var codevalue = t[z].substring((n + 1), nstr);
//Logger.log("z = "+z+", code is = "+code+", and the value is "+codevalue); // DEBUF
}
// test to whether there is a match between the Named Range Code and the URL
if (urlvars[0][i] == code) {
// set the variable to note a match was detected.
ttest = 0;
// push the code value into an array
output.push(codevalue);
// push the Named range code ID onto an array
tcount.push(z);
//Logger.log("Match "+urlvars[0][i]+" = "+code); //DEBUG
}
} // end of the URL variables loop
// having looped through the URL variables, test to see whether there was a match
// if not (ttest still equals One) then put an empty string in the output array, so ensure that every code has a value
// and keep count of the number of "nomatches"
if (ttest == 1) {
output.push(" ");
Logger.log("No match for " + urlvars[0][i]);
nomatch = nomatch + 1;
}
} // end of the Named Range loop
// create an array for 2d format
var outeroutput = [];
// put the loop array into the blank array. The result is a 2d array that can be read by the Google sheets script.
outeroutput.push(output);
// For the NamedRange analysis, we can now set the values from the loop
var targetrange = datasheet.getRange(lastrow + 1, 1, 1, datalen);
targetrange.setValues(outeroutput);
//Logger.log("targetrange = "+targetrange.getA1Notation()); //DEBUG
// count how matches were found for URL variables
var tcountlen = tcount.filter(String).length;
// compare the number of variables in the URL with the number of matches.
// If there is a difference, then we need to loop through the URL variables, find the ones that didn't match and do stuff with them.
if ((tlen - tcountlen) > 0) {
// starp loop for URL variables
for (z = 0; z < tlen; z++) {
// set the variable to detect whether or not a a match was made.
ztest = 1;
// Repeat the process of splitting the component code and value
var n = t[z].indexOf("=");
if (n > 0) {
// get the length of the variable
var nstr = t[z].length;
// get the componet parts
var code = t[z].substring(0, n);
var codevalue = t[z].substring((n + 1), nstr);
//Logger.log("z = "+z+", code is = "+code+", and the value is "+codevalue); //DEBUG
}
// start the loop for thecodes in the NamedRange
for (i = 0; i < datalen; i++) {
// If there's a match, chnage the value of the 'match testing' varuable
if (urlvars[0][i] == code) {
ztest = 0;
}
} // end of the loop for NamedRange codes
// if there hasn't been match, then
// push the url variable code and value onto some respective arrays
if (ztest == 1) {
mismatchcode.push(code);
mismatchdata.push(codevalue);
}
} // end of the URL variables loop
//Logger.log("Code fields = "+datalen+", data fields = "+tlen);// DEBUG
//Logger.log("Total no-matches for codes = "+nomatch); // DEBUG
// Logger.log("Total no-matches for URL fields = "+(tlen-tcountlen)); //DEBUG
// So, what shall we do if there the number of variables in the NAMED RANGE does equal the number of variables
// if((tlen-tcountlen) !=0){
// These rows are just for DEBUG assignstance.
// for (i=0;i<(tlen-tcountlen);i++){ //DEBUG
// Logger.log("URL field not found: code = "+mismatchcode[i]+", value = "+mismatchdata[i]); //DEBUG
// } //DEBUG
// create the arrays to act as 2d
var outermismatchcode = [];
var outermismatchdata = [];
// Push the mismatch arrays to the create the 2d arrays
outermismatchcode.push(mismatchcode);
outermismatchdata.push(mismatchdata);
// Identify the range for the addition URL Codes and values
// set the respective values
var extraurlcoderange = datasheet.getRange(1, datalen + 1, 1, (tlen - tcountlen));
extraurlcoderange.setValues(outermismatchcode);
var extraurldatarange = datasheet.getRange(lastrow + 1, datalen + 1, 1, (tlen - tcountlen));
extraurldatarange.setValues(outermismatchdata);
// We want to add the "new" codes found in the URL to the Named Range.
// Start by deletinging the existing NamedRange
ss.removeNamedRange("DataVariables");
// Define the parmeters for a new range.
// The main thing is that we need to add more columns
var newnamedrange = datasheet.getRange(1, 1, 1, (datalen + (tlen - tcountlen)))
// So, Create a new NamedRange using the same name as before.
ss.setNamedRange('DataVariables', newnamedrange);
// The following lines are just to check that everything worked OK
// var rangeCheck = ss.getRangeByName("DataVariables"); // DEBUG
// if (rangeCheck != null) { //DEBUG
// Logger.log("Columns in the new named range = "+rangeCheck.getNumColumns());//DEBUG
// } ,//DEBUG
// var rangeCheckName = rangeCheck.getA1Notation(); //DEBUG
// Logger.log("the new named range is = "+rangeCheckName);//DEBUG
} // end of the loop to identify URL variables that didn't match a code in the NamedRange
}
Note the addition value of the c1 code includes the relevant equals sign. Also the URL includes an additional parameter ("ejb=1") that is not in the existing list; this code and its value are added to the spreadsheet, and the NamedRange now includes the "new" code.

Related

Pasting multiple rows of data into single cell using JavaScript

So I have combinations of names to tasks in a table where several different task are associated with the same name. But I need to put the task into one cell next to the associated name. Using JavaScript. Heres what I got;
function Unique(){
var ss = SpreadsheetApp.openById("ID");
var dataRaw = ss.getSheetByName("Sheet1");
var destination = ss.getSheetByName("Sheet2");
var names2 = dataRaw.getRange(2,10,dataRaw.getLastRow(),1).getValues();
var names1 = names2.flat(1);
var names = names1;
//var names = ["name1","name1","name2", "name3", "name3"];
var uniqueNames = []; //empty array
var count = 0;
var found = false;
for (i = 0; i < names.length; i++){
for(y =0; y < uniqueNames.length; y++){
if(names[i] == uniqueNames[y]){
found = true;
}
}
count++;
if(count == 1 && found == false){
uniqueNames.push(names[i]);
}
count = 0;
found = false;
}
/* can I use this??? maybe it's not needed
var uniqueNames2 = uniqueNames.map(function(obj) {
return Object.keys(obj).sort().map(function(key) {
return obj[key];
});
});
*/
var dest = destination.getRange(1,2,uniqueNames.length,uniqueNames[0].length);
dest.setValue(uniqueNames); //maybe this is not needed
console.log(uniqueNames[0].length);
}
My approach is to;
take in names and output the unique names so there is no doubles
once i have unique names use some type of for() loop or map() function to find tasks and pair with names? maybe im wrong?
and then setValues() to the range that I need.
The problems that I'm running into are that My Unique() function needs a regular array not array of arrays, which i fix using
array.flat(1)
but then to paste the values javaScript needs the array or arrays to be just an array which I COULD fix with
Object.keys(obj).sort().map(function(key)
in the commented out section? to turn an array of arrays back into an array... but then my "width" is not consistent for my array, columns, and I get the error that my range is not the same number of columns as my data. I feel that this is fairly simple and I am grossly over complicating things. Any help would be great thank you. My google sheet below https://docs.google.com/spreadsheets/d/1rbz52kkzhVAGX21MUVoexzPUvWxjk-RCw-5PrRLoBBc/edit?usp=sharing
I believe your goal as follows.
You want to achieve the following conversion.
From
Task Names
Task 1 name one
Task 2 name one
Task 3 name one
Task 4 name one
Task 5 name one
Task 1 name two
Task 2 name two
Task 3 name two
Task 1 name three
Task 2 name three
Task 3 name three
To
task names
Task 1
Task 2
Task 3
Task 4
Task 5 Name one
Task 1
Task 2
Task 3 name two
Task 1
Task 2
Task 3 name three
For this, how about this answer?
Modification points:
In your script, for example, about var names2 = dataRaw.getRange(2,10,dataRaw.getLastRow(),1).getValues();, I thought that you might misunderstand the row and column for getRange. And, in this case, only one row Names of column "B" on "Sheet1" is retrieved. The row of Task is not retrieved in your script. And also, from dest.setValue(uniqueNames);, you might misuderstood setValue and setValues.
When above points are reflected to your script, it becomes as follows.
Modified script:
In this modification, at name2, the values from the cells "B2:B12" are retrieved, and the unique values are retrieved using your script. Then, the values from the cells "A2:B12" are retrieved, and the values for putting to Spreadsheet are created using the created unique values. Then, the created values are put to the Spreadsheet.
Modified script:
function Unique_org2(){
var ss = SpreadsheetApp.openById("ID");
var dataRaw = ss.getSheetByName("Sheet1");
var destination = ss.getSheetByName("Sheet2");
var names2 = dataRaw.getRange(2,2,dataRaw.getLastRow()-1,1).getValues(); // <--- Modified
var names1 = names2.flat(1);
var names = names1;
var uniqueNames = [];
var count = 0;
var found = false;
for (i = 0; i < names.length; i++){
for(y =0; y < uniqueNames.length; y++){
if(names[i] == uniqueNames[y]){
found = true;
}
}
count++;
if(count == 1 && found == false){
uniqueNames.push(names[i]);
}
count = 0;
found = false;
}
// --- I added below script.
var values = dataRaw.getRange(2, 1, dataRaw.getLastRow() - 1, 2).getValues(); // Added
var uniqueNames = uniqueNames.reduce((ar, e) => {
var temp = "";
values.forEach(([a, b]) => {
if (e == b) temp += a + "\n";
});
ar.push([temp.trim(), e]);
return ar;
}, []);
// ---
var dest = destination.getRange(2,1,uniqueNames.length,uniqueNames[0].length); // <--- Modified
dest.setValues(uniqueNames); // <--- Modified
}
Other pattern:
In this pattern, in order to achieve your goal, I would like to propose the other sample script of following flow. This flow might be able to reduce the process cost from above modified script.
Retrieve values from the cells "A2:B12" of "Sheet1".
Create an object from the retrieved values.
Convert the object to an array for putting to Spreadsheet.
Put the values to Spreadsheet to the destination sheet.
Sample script:
function Unique(){
var ss = SpreadsheetApp.openById("ID");
var dataRaw = ss.getSheetByName("Sheet1");
var destination = ss.getSheetByName("Sheet2");
// 1. Retrieve values from the cells "A2:B12" of "Sheet1".
const values = dataRaw.getRange(2, 1, dataRaw.getLastRow() - 1, 2).getValues();
// 2. Create an object from the retrieved values.
const obj = values.reduce((o, [a, b]) => Object.assign(o, {[b]: (o[b] ? o[b] + a : a) + "\n"}), {});
// 3. Convert the object to an array for putting to Spreadsheet.
const res = Object.entries(obj).map(([k, v]) => [v.trim(), k]);
// 4. Put the values to Spreadsheet to the destination sheet.
destination.getRange(2, 1, res.length, res[0].length).setValues(res);
}
References:
getRange(row, column, numRows, numColumns)
setValue(value)
setValues(values)

How to create a for loop to loop through JSON.stringified values determining "paste tabs" for values

Update: I need to check if a unique value is already in the pasteTab's appropriate column. My code for that is --
for (a = 0; a<coldChainRange.length;a++){
var fillWeeks = coldChainRange[a][12]
**var rxNumbers = coldChainRange[a][0]**
var pasteTab = ss.getSheetByName(fillWeeks)
//var range = pasteTab.getRange('A2:P'+pasteTab.getLastRow()).getDisplayValues()
**var array = [];
array.push(rxNumbers)**
Logger.log(array)
//Logger.log(fillWeeks)
if(fillWeeks != "Need Days Supply"){
if (pasteTab !== null && **array.indexOf(pasteTab[a][0]==-1**)){
var patientInfo = ([coldChainRange[a][0],coldChainRange[a][1],coldChainRange[a][2],coldChainRange[a][3],coldChainRange[a][4],
coldChainRange[a][5],coldChainRange[a][6],coldChainRange[a][7],coldChainRange[a][8],coldChainRange[a][9],
coldChainRange[a][10],coldChainRange[a][11],coldChainRange[a][12],coldChainRange[a][13],coldChainRange[a][14]])
pasteTab.appendRow(patientInfo)
}
}
}
}
I need to have the info not be appended if a number is already in the column, however I think the loop is iterating the length of the "pasteTab" which is determined by a week number which is two characters long
How can I create a loop that will go read JSON.stringifed values?
I am trying to loop through cell values to determine where the information should be appended to. For example, if a cell had a value of "23" it would be appended to the 23 tab.
function sendToFillWeek() {
var ss = SpreadsheetApp.getActiveSpreadsheet()
var coldChainPasteSheet = ss.getSheetByName('from looker')
var coldChainRange = coldChainPasteSheet.getRange('A2:P' + coldChainPasteSheet.getLastRow()).getDisplayValues()
for (a = 0; a<coldChainRange.length;a++){
var fillWeeks = JSON.stringify(coldChainRange[a][12])
var pasteTab = ss.getSheetByName(fillWeeks)
Logger.log(pasteTab)
}}
This is my code so far for determining the appropriate sheet. The fillWeeks produces the values I need, however the pasteTab outputs all "null" values.
for(b=0; b<fillWeeks.length;b++){
(if fillWeeks !== "Need Day Supply" ){
var patientInfo = ([coldChainRange[a][0],coldChainRange[a][1],coldChainRange[a][2],coldChainRange[a][3],coldChainRange[a][4],
coldChainRange[a][5],coldChainRange[a][6],coldChainRange[a][7],coldChainRange[a][8],coldChainRange[a][9],
coldChainRange[a][10],coldChainRange[a][11],coldChainRange[a][12],coldChainRange[a][13],coldChainRange[a][14],
coldChainRange[a][15]])
pasteTab.appendRow(patientInfo)
}
}
}
}
Essentially, I would like the information to be appended the appropriate tabs.

Compare 4 different columns in 2 different Google Sheets

I am trying to compare the data from 2 google sheets. Each sheet has a column that is the identifier (sheet1:H and sheet2:C), if these match then I want to change sheet1:I to the value in sheet2:E. I'm running this code, but get no errors. It's not working though.
I tried to see similar posts this issue but they all seem to be lacking the compare a different column method I am using.
function changestatus() {
// gets spreadsheet A and the range of data
ssA = SpreadsheetApp.openById('IDHERE');
sheetA = ssA.getSheetByName('Sheet1');
dataA = sheetA.getRange('H2:H').getValues();
dataD = sheetA.getRange('I2:I').getValues();
// gets spreadsheet B and the range of data
ssB = SpreadsheetApp.openById('IDHERE');
sheetB = ssB.getSheetByName('responses');
dataB = sheetB.getRange('C2:C').getValues();
dataC = sheetB.getRange('E2:E').getValues();
for (var i = 0; i > sheetA.getLastRow(); i++) {
if (dataA[1][i] == dataB[1][i] && dataC[1][i] != dataD[1][i]){
var value = sheetA.getRange(i+1, 2).getValue(dataD);
sheetB.getRange(i+1, 2).setValue(value);
} // end if
} // end i
Starting results of sheets files would be something like:
Sheet 1
H:(ID) 1 I:(grade) pass
Sheet 2
C:(ID) 1 E:(grade) fail
After Function:
Sheet 1
H:(ID) 1 I:(grade) fail
#tehhowch is quite right; you need to review JavaScript comparison operators, for loop syntax, the format of object returned by Range#getValues, and how to access JavaScript array indices. Each of these contributes to your code problems, but it's reasonable that that we help you along the road a little more.
Loop syntax
This is an easy one. Instead of "i > sheetA.getLastRow()", it should read i < sheetA.getLastRow(). i starts with a value of zero, and its value increases by one at the end of each loop; so you want the loop to process all the values of i that are less than the value of the last row.
Array values
getValues returns a two-dimensional array but the IF statement fails because the array values are back to front.
For example, instead of "dataA[1][i]", it should be dataA[i][0]. There are two changes here:
1 - "i" moves to the first half of the array value (the 'row' value); and
2 - the second half of the array value is [0] (not "[1]"). This is because each variable is only one column wide. For example, dataA only returns the value of column H; same is true for dataB, dataC and dataD - they all return the value of just one column.
Troubleshooting
How could you tell whether the IF statement was a problem? It "looks" OK. One way is to display (or log) the values being returned.
I use Logger.log() (there are other options) to display information in the script editor under "View, Logs". Each time the script is run, the "Logger" statements are updated and you can check their value.
For example, you could insert this code at line 13 (before the loop) to check some values of the data variables.
Logger.log("dataA[1][0] = "+dataA[1][0]);
That line will show: "dataA[1][0] = 2". That's a valid result but you might notice that it is reporting ID=2 but, say, you were expecting a result of ID=1.
So change the line to:
Logger.log("dataA[1][1] = "+dataA[1][1]);
This line shows "dataA[1][1] = undefined". OK, something definitely wrong.
So, let's try:
Logger.log("dataA[0][0] = "+dataA[0][0]);
This line shows "dataA[0][0] = 1". Now that's more like it.
You can make Logger long or short; for example, you might want to evaluate the results of of the variables in one line. So the Logger might look like this:
Logger.log("dataA[0][0] = "+dataA[0][0]+", dataB[0][0] = "+dataB[0][0]+", dataC[0][0] = "+dataC[0][0]+", dataD[0][0] = "+dataD[0][0]);
And it would return:
"dataA[0][0] = 1, dataB[0][0] = 1, dataC[0][0] = Fail, dataD[0][0] = Pass".
This might confirm that you are on the right track, or that you need to debug further
The Failing IF statement
Original line = "(dataA[1][i] == dataB[1][i] && dataC[1][i] != dataD[1][i])"
Corrected line = (dataA[i][0] == dataB[i][0] && dataC[i][0] != dataD[i][0])
Updating the results on Sheet 1
The code here is:
var value = sheetA.getRange(i+1, 2).getValue(dataD);
sheetB.getRange(i+1, 2).setValue(value);
This is confusing and complicates a couple of things.
1 - the value just needs to be "the value in sheet2:E - this was in the IF statement: dataC[i][0]. So value = dataC[i][0]
2 - The goal is "change sheet1:I to the value in sheet2:E". You've already got the value, so focus now on sheet1:I.
Some times it is more simple to define the range and then, on a second line, update the value for that range.
the target sheet is sheetA;
the target row is: i+1 (that was correct);
the target column is: I (or column 9).
So, var range = sheetA.getRange(i+2, 9);
You could check this with "Logger":
Logger.log("range = "+range.getA1Notation()); might return "range = I2".
Then update the value:
range.setValue(value);
Meaningful variable names
It helps (a LOT) to use meaningful variable names. For example, the original code uses:
"dataA" = Sheet1, Column H (contains ID); so maybe this could be "data1_H" or even "targetID.
"dataD" = Sheet1, Column I (contains grade); so maybe this could be "data1_I" or targetGrade.
"dataB" = Sheet2, Column C (contains ID), so maybe this could be "data2_C" or sourceID.
"dataC" = Sheet2, Column E (contains grade); so maybe this could be "data2_E" or sourceGrade.
Summary of changes
function so_changestatus() {
// gets spreadsheet A and the range of data
ssA = SpreadsheetApp.openById('IDHERE');
sheetA = ssA.getSheetByName('Sheet1');
dataA = sheetA.getRange('H2:H').getValues();
dataD = sheetA.getRange('I2:I').getValues();
// gets spreadsheet B and the range of data
ssB = SpreadsheetApp.openById('IDHERE');
sheetB = ssB.getSheetByName('responses');
dataB = sheetB.getRange('C2:C').getValues();
dataC = sheetB.getRange('E2:E').getValues();
for (var i = 0; i < sheetA.getLastRow(); i++) {
if (dataA[i][0] == dataB[i][0] && dataC[i][0] != dataD[i][0]){
var value = dataC[i][0];
var range = sheetA.getRange(i+2, 9);
range.setValue(value);
} // end if
}
}
UPDATE - 1 April 2019
ID on SheetA vs SheetB does NOT match row-by-row
The original code was written on the basis that the ID matched on a row-by-row basis. This is not the case. So a variation in the code is needed to test whether the ID on SheetA exists on SheetB, and then test the respective status.
The evaluation of the sheetA ID on sheetB is done with [indexof] Docs reference.
In this code, I also took the opportunity to make the variable names of the data ranges more meaningful.
Note also: the loop continues while i is less than the lastrow minus one "i < (lastrow-1);". This is necessary because the first row are headers and the data range starts on row 2, so the number of data rows will be the "lastrow minus one" (to a allow for the header row).
function ejb2so_changestatus() {
// gets spreadsheet A and the range of data
// ssA = SpreadsheetApp.openById('IDHERE');
ssA = SpreadsheetApp.getActive();
sheetA = ssA.getSheetByName('Sheet1');
dataA_ID = sheetA.getRange('H2:H').getValues();
data_Status = sheetA.getRange('I2:I').getValues();
//Logger.log("DEBUG: H3 = "+dataA_ID[4][0]+", I3 = "+data_Status[4][0]);//DEBUG
// gets spreadsheet B and the range of data
//ssB = SpreadsheetApp.openById('IDHERE');
ssB = SpreadsheetApp.getActive();
sheetB = ssB.getSheetByName('Responses');
dataB_ID = sheetB.getRange('C2:C').getValues();
dataB_Status = sheetB.getRange('E2:E').getValues();
// Logger.log("DEBUG: C3 = "+dataB_ID[0][0]+", E3 = "+dataB_Status[0][0]);//DEBUG
var lastrow = sheetA.getLastRow()
// Logger.log("DEBUG: sheetA last row = "+lastrow);//DEBUG
// Flatten the array
var dataB_IDFlat = dataB_ID.map(function(row) {
return row[0];
});
//Loop through values on sheetA; check if they exist on sheetB
for (var i = 0; i < (lastrow - 1); i++) {
var A_ID = dataA_ID[i][0];
// Logger.log("DEBUG: id = "+A_ID);//DEBUG
// assign variable to return value index
var result = dataB_IDFlat.indexOf(A_ID);
if (result != -1) {
// it's there
// Logger.log("DEBUG: i: "+i+", ID: "+A_ID+", it's there"+", result#: "+result);//DEBUG
// Logger.log("DEBUG: Sheet1 status: "+data_Status[i][0]+" Vs Sheet2 status = "+dataB_Status[result][0]);//DEBUG
// compare status from sheetsA to sheetB
if (data_Status[i][0] != dataB_Status[result][0]) {
// Logger.log("DEBUG: status change to: "+dataB_Status[result][0]);//DEBUG
var range = sheetA.getRange(i + 2, 9);
//Logger.log("DEBUG: value = "+value);//DEBUG
//Logger.log("DEBUG: range = "+range.getA1Notation());//DEBUG
range.setValue(dataB_Status[result][0]);
}
} else {
// it's not there
// Logger.log("DEBUG: i: "+i+", ID: "+A_ID+", it's not there");//DEBUG
}
}
}
// Credit: Flatten array: https://stackoverflow.com/a/49354635/1330560

unable to search for array items

In this program I am unable to search for items in an array. The db variable is already defined. I am unable to get into the search function and run it. Just curious why this might be happening. I am able to run the first validate function but then stops and will not perform the rest of the code.
// Create privatized scope using a self-executing function
(function() {
console.log("hello");
// Variable initialization (DO NOT FIX ANY OF THE BELOW VAR's)
var resultsDIV = document.getElementById("results"),
searchInput = document.forms[0].search,
currentSearch = '';
// Validates search query
var validate = function (query) {
console.log("validate");
// Trim whitespace from start and end of search query
query = query.trim();
// Check search length, must have 3 characters
if (query.length < 3) {
alert("Your search query is too small, try again.");
}else{
search(query);
// (DO NOT FIX THE LINE DIRECTLY BELOW)
searchInput.focus();
}
console.log("test");
};
console.log("outside search function");
// Finds search matches
var search = function (query) {
console.log("In search function");
// split the user's search query string into an array
var queryArray = query.split(" ");
// array to store matched results from database.js
var results = [];
// loop through each index of db array
for (var i = 0, j = db.length; i < j; i++) {
console.log(i);
// each db[i] is a single video item, each title ends with a pipe "|"
// save a lowercase variable of the video title
var dbTitleEnd = db[i].indexOf('|');
var dbItems = db[i].toLowerCase().substring(0, dbTitleEnd);
}
// loop through the user's search query words
// save a lowercase variable of the search keyword
for (var ii = 0, jj = queryArray.length; ii < jj; ii++) {
var qItem = queryArray[ii].toLowerCase();
}
// is the keyword anywhere in the video title?
// If a match is found, push full db[i] into results array
var compare = dbItems.indexOf(qItem);
if (compare !== -1) {
results = results.push(db[i]);
}
results.sort();
// Check that matches were found, and run output functions
if (results.length === 0) {
noMatch();
} else {
showMatches(results);
}
};
// Put "No Results" message into page (DO NOT FIX THE HTML VAR NOR THE innerHTML)
var noMatch = function() {
var html = '' +
'<p>No Results found.</p>' +
'<p style="font-size:10px;">Try searching for "JavaScript". Just an idea.</p>'
;
resultsDIV.innerHTML = html;
};
// Put matches into page as paragraphs with anchors
var showMatches = function (results) {
// THE NEXT 4 LINES ARE CORRECT.
var html = '<p>Results</p>',
title,
url
;
// loop through all the results search() function
for (var i = 0, j = results.length; i < j; i++) {
// title of video ends with pipe
// pull the title's string using index numbers
var titleEnd = results[i].indexOf('|');
title = results[i].subString(0, titleEnd);
// pull the video url after the title
url = results[i].substring(results[i].indexOf('|') + 1, results[i].length);
// make the video link - THE NEXT LINE IS CORRECT.
html += '<p><a href=' + url + '>' + title + '</a></p>';
resultsDIV.innerHTML = html; //THIS LINE IS CORRECT.
}
};
console.log("start of program");
/***** start of program *******/
// The onsubmit event will be reviewed in upcoming Course Material.
// THE LINE DIRECTLY BELOW IS CORRECT
document.forms[0].onsubmit = function(){
var query = searchInput.value;
validate(query);
// return false is needed for most events - this will be reviewed in upcoming course material
// THE LINE DIRECTLY BELOW IS CORRECT
return false;
};
})();
I checked your code and everything is there but somehow all wrongly connected. It seems that you have completely wrong concept about for loops. I did several minor changes:
query is trimmed on reading because we need it not just in validator
validator:
if query is too short set focus back
it validated but also allowed operation to continue in case of error - changed
changed to isValid() and checked in onsubmit handler
search:
wrong concept of for loops
you retrieve dbTitleEnd/dbItems and than overwrite them by the next one
there is no need to do results = results.push(db[i]); but just `results.push(db[i]);
subString() corrected to substring()
console.log() messages are left in
See Example in jsFiddle.

Cant figure out search error

working on this, too. I've fixed the spelling and (i think) the bracket errors. Also fixed a couple errors I saw that stood out, but didn't get too far. I'm still stumped as to where to go with it next.
(function(){
// Variable initialization (DO NOT FIX ANY OF THE BELOW VAR's)
var resultsDIV = document.getElementById("results"),
searchInput = document.forms[0].search,
currentSearch = ''
;
// Validates search query
var validate = function(query){
// Trim whitespace from start and end of search query
while (query.charAt[0] === " "){
query = query.substring(1, query.length);
};
while (query.charAt(query.length-1) === ""){
query = query.substring(0, query.length - 1);
};
// Check search length, must have 3 characters
if (query.length < 3){
alert ("Your search query is too small, try again.");
// (DO NOT FIX THE LINE DIRECTLY BELOW)
searchInput.focus();
return;
};
search (query);
};
// Finds search matches
var search = function (query){
// split the user's search query string into an array
var queryArray = query.join(" ");
// array to store matched results from database.js
var results = [];
// loop through each index of db array
for(var i=0, j=db.length; i<j; i++){
// each db[i] is a single video item, each title ends with a pipe "|"
// save a lowercase variable of the video title
var dbTitleEnd = db[i].indexOf('|');
var dbitem = db[i].tolowercase().substring(0, dbTitleEnd);
// loop through the user's search query words
// save a lowercase variable of the search keyword
for(var ii=0, jj=queryArray.length; ii<jj; ii++){
var qitem = queryArray[ii].tolowercase();
// is the keyword anywhere in the video title?
// If a match is found, push full db[i] into results array
var compare = dbitem.indexOf(qitem);
if(compare !== -1){
results.push(db[i]);
};
};
};
};
results.sort();
// Check that matches were found, and run output functions
if(results.length = );{
noMatch();
}else{
showMatches(results);
};
// Put "No Results" message into page (DO NOT FIX THE HTML VAR NOR THE innerHTML)
var noMatch = function(){
var html = ''+
'<p>No Results found.</p>'+
'<p style="font-size:10px;">Try searching for "JavaScript". Just an idea.</p>'
;
resultsDIV.innerHTML = html;
};
// Put matches into page as paragraphs with anchors
var showMatches = function(results){
// THE NEXT 4 LINES ARE CORRECT.
var html = '<p>Results</p>',
title,
url
;
// loop through all the results search() function
for(var i=0, j=results.length; i<j; i++){
// title of video ends with pipe
// pull the title's string using index numbers
titleEnd = results[i].indexOf('|');
title = results[i].subString(0, titleEnd);
// pull the video url after the title
url = results[i].substring(results[i].indexOf('|')+1, results[i].length);
// make the video link - THE NEXT LINE IS CORRECT.
html += '<p><a href=' + url + '>' + title + '</a></p>';
};
resultsDIV.innerHTML = html; //THIS LINE IS CORRECT.
};
// The onsubmit event will be reviewed in upcoming Course Material.
// THE LINE DIRECTLY BELOW IS CORRECT
document.forms[0].onsubmit = function(){
var query = searchInput.value;
validqte(query);
// return false is needed for most events - this will be reviewed in upcoming course material
// THE LINE DIRECTLY BELOW IS CORRECT
return false;
;
})();
There are a few syntax errors which should prevent this from running at all.
// syntax error
var results = ();
// should be
var results = [];
Also it appears that the definition of showMatches ends with an unmatched right parenthesis.
Given that somehow the code is running with these syntax errors (and possibly others I didn't notice), the (a) glaring issue is:
if (results.length = 0) {
Yeah, that's an assignment, not a comparison. You just set the array's length to zero, which effectively clears it. This both fails the length test here (returning 0), then hands the now empty array off to be displayed as results, but of course it's now an array of length 0.
On another note, probably not an actual problem but I can't help it. The while loop business to trim the string? Yeah, don't do that. Use trim. If you need a polyfill there's always:
if (!String.prototype.trim) {
String.prototype.trim = function () {
return this.replace(/^\s+|\s+$/g, '');
};
}

Categories

Resources