Compare 4 different columns in 2 different Google Sheets - javascript

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

Related

Automate Hyperlink Creations

I'm trying to automate hyperlink creations on my GSheet.
Here's my script:
function ticketURLGenerator() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Data");
var range = sheet.getRange("C2:C");
var ticketID = range.getValue();
Logger.log(ticketID);
for(i = 0; i < ticketID.length; i++){
if(ticketID.length === 0){
ticketID.setValue('')
} else if(ticketID.length > 4){
ticketID.setValue('=HYPERLINK' + '("https://mylink.com/'+ticketID+'";'+ticketID+')');
}
}
}
It does nothing but when I change ticketID.setValue by sheet.getRange("C2:C").setValue it put the whole range in the url. We can see with Logger.log(ticketID) that the whole range is selected.
So according to this result, i'm missing how to get the value of each cell individualy in the range and then check if they are long enought to create an individual url. Do I need to use something like range[i] somewhere? I'm lost.
I believe your goal as follows.
You want to retrieve the values from the cells "C2:C".
When the length of value is more than 4, you want to create a formula of HYPERLINK.
When the length of value is less than 4, you don't want to put the formula.
You want to put the formulas to the cells "C2:C".
Modification points:
When range of var range = sheet.getRange("C2:C") is used, the value of var ticketID = range.getValue() is the value of cell "C2". When you want to retrieve values from the cells "C2:C", please use getValues instead of getValue.
In this case, the retrieved value is 2 dimensional array.
When range.getValue() is the string value, ticketID of var ticketID = range.getValue() is also the string. So I think that when ticketID.setValue('##') is run, an error occurs.
In your script, setValue is used in a loop. In this case, the process cost will become high.
And, when sheet.getRange("C2:C" + sheet.getLastRow()) is used instead of sheet.getRange("C2:C"), the process cost will become low a little.
When above points are reflected to your script, it becomes as follows.
Modified script:
function ticketURLGenerator() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Data");
var range = sheet.getRange("C2:C" + sheet.getLastRow());
var ticketIDs = range.getValues();
var values = ticketIDs.map(([c]) => [c.toString().length > 4 ? `=HYPERLINK("https://mylink.com/${c}";"${c}")` : c]);
range.setValues(values);
}
In this modification, the values are retrieved from the cells of "C2:C" + sheet.getLastRow(), and an array including the formulas and values is created, and then, the array is put to the cells.
And I used the template literal for creating the formula.
Note:
In this case, please use this script with enabling V8 runtime.
References:
getLastRow()
getValues()
map()
Template literals
You just need to apply the HYPERLINK operation to the tickets that their length is more than 4. To achieve that, you can use map() to iterate over all the elements in your list.
Solution:
function ticketURLGenerator() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getSheetByName("Data");
const ticketR = sheet.getRange("C2:C"+sheet.getLastRow());
const ticketIDs = ticketR.getDisplayValues().flat();
const hLinks = ticketIDs.map(ti=>{
if(ti.length>4) {
return [`=HYPERLINK("https://mylink.com/${ti}"; ${ti})`]}
else {return [ti]}
})
ticketR.setValues(hLinks);
}

Sync row data based on unique ID in google sheets scripts

I currently have a code that does something very similar to this, but im not sure the small change i need to make to have it work correctly. Right now, the code below compares two rows of unique IDs and if the IDs are the same, it copies the cell in the "Comments" column to the other sheet.
function setComments() {
var ss = SpreadsheetApp.getActive(),
compare1 = "", compare2 = "",
outputSheet = ss.getSheetByName("Sheet2"),
sourceSheet = ss.getSheetByName("Sheet1"),
range1 = outputSheet.getDataRange(),
range2 = sourceSheet.getDataRange(),
lastCol1 = range1.getNumColumns(),
lastCol2 = range2.getNumColumns(),
values1 = range1.getValues(),
values2 = range2.getValues(),
// get the range of the titles
titleSection1 = outputSheet.getRange(1,1,1, lastCol1),
titleSection2 = sourceSheet.getRange(1,1,1, lastCol2),
// get the values from the titles
titles1 = titleSection1.getValues(),
titles2 = titleSection2.getValues(),
// get the column # for "ID" and "comment"
idCol1 = titles1[0].indexOf("ID"),
idCol2 = titles2[0].indexOf("ID"),
commentsCol1 = titles1[0].indexOf("comment"),
commentsCol2 = titles2[0].indexOf("comment");
// get the IDs from range1
for (i = 1; i < values1.length; i++) {
compare1 = values1[i][idCol1];
// get the IDs from range2
for (j = 1; j< values2.length; j++){
compare2 = values2[j][idCol2];
// if same ID, change the values array
if (compare1 == compare2) {
values1[i][commentsCol1] = values2[j][commentsCol2];
}
}
}
// set values based on the values array
range1.setValues(values1);
}
Instead, if there is a change made to any cell on sheet 1, it will find the identical cell based on unique ID in the other sheet and sync the change. What change do i need to make to have this work?
For example, if I change what the office is in the row of ID 1 of sheet 1, it will make the identical change for ID 1 in sheet 2.
Here is an example sheet of what im working with:
Sheet 1:
ID Comment Number Office Clinician
1 good 22345 Dallas
2 bad 12345 Denton
3 good 95954 Lubbock
4 bad 20204 FT.W
5 bad 11111 Denton
6 good 02944 Preston
Sheet 2:
ID Comment Number Office Clinician
1 good 22345 Dallas
3 good 95954 Lubbock
5 bad 11111 Denton
You have one data set on Sheet 1, and a subset of that data set on Sheet1. The key field is the "ID". If there is a change to data on Sheet 1, and the ID relating to that change is found on Sheet 2, then you want to update the relevant dataset on Sheet 2.
The key aspects of this answer are:
onEdit(e): This is an simple Trigger.
e.range: "range" is an Event Object. By using the attribute 'e'; it is possible to recover a substantial amount of information about the changes. In addition, the event objects can be further used to obtain more information - such as (in this case) the Row, Column and Sheet Name.
filter(String).length: Sometimes getting the last row of data is problematic. The answer gets all the data in Column A, and uses the Javascript "array.filter" method. In this case is simply counts the values as strings, andf the resulting value is equal to the Last Row of data.
the "IF" statement evaluates for several attributes:
Is the edit on Sheet1?
Is the edit on a row between the header and last row?
Is the edit in a column that contains data?
the operator is "&&" which requires that each of the attributes must return true.
targetdata.map(function(e){return e[0];});: the script gets ALL the data on Sheet2, but uses the Javascript array.map method to generate a subject of just the values in Column 1 (the ID column).
targetCol1.indexOf(sourceID);: The script uses the Javascript "array.indexOf"; if the ID is found, it will return the index number of the row on Sheet2; if the value isn't found, it will return "-1". The enables a logic statement to be written that will be executed only if the value is not "-1"
target.getRange(+result+1,1,1,sourceLC).setValues(sourcedatatocopy): the last lines of the script get the values in the edited row on Sheet 1, and then update the values on Sheet2. Note: it updates all the values for the matched ID - rather than identifying the changed field and updating only that field.
function onEdit(e) {
// setup spreadsheet and sheet names
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sourceSN = "Sheet1";
var source = ss.getSheetByName(sourceSN);
var targetSN = "Sheet2";
var target = ss.getSheetByName(targetSN);
// get row, column and sheet of the edited sheet
var row = e.range.getRow();
var col = e.range.getColumn();
var editedsheet = e.range.getSheet().getSheetName();
// Logger.log("DEBUG: editedsheet = "+editedsheet)
// get the ID column from Sheet 1
var SourceCol1Vals = ss.getRange("A2:A").getValues();
var SourceLR = SourceCol1Vals.filter(String).length;
// assign value to last column
var sourceLC = 5;
// test for sheet, row range and column range
if (editedsheet == sourceSN && row >=2 && row <=SourceLR && col <=5){
//Logger.log("DEBUG: this is sheet 1 & the right row and the right Col")
// get the data for this row
var sourcedata = source.getRange(row,1,1,5).getValues();
// get the data from target
var targetdata = target.getDataRange().getValues();
// get only the ID column
var targetCol1 = targetdata.map(function(e){return e[0];});
// get the sourse ID
var sourceID = sourcedata[0][0];
// Logger.log("DEBUG: source ID = "+sourceID)
// search for source ID on the Target list
var result = targetCol1.indexOf(sourceID);
// Logger.log("DEBUG: id was found at "+result);
if (result !=-1){
// if -1 then couldn't find the ID, otherwise it returns the index number where it finds the match
// get the data for the Source ID
var sourcedatatocopy = source.getRange(row,1,1,sourceLC).getValues();
// update the darget for the sourceID data.
target.getRange(+result+1,1,1,sourceLC).setValues(sourcedatatocopy)
}
} else{
// Logger.log("DEBUG: not sheet 1 or right row or right col");
}
return;
}

Is it possible to pull different data sets from one column?

I've been trying to write some code that looks down one column with strings based on some simple formulas. I can't seem to get it to recognize the different sets of data and paste them where I want them.
I have tried re writing the code a few different ways in which is looks at all the data and just offsets the destination row by 1. But it does not recognize that it is pull different data.
Below is the code that works. What it does is starts from the 1st column 2nd row (where my data starts). The data is a list like;
A
1 Customer1
2 item1
3 item2
4 Item3
5
6 Customer2
7 Item1
The formulas that I have in those cells just concatenates some other cells.
Using a loop it looks through column A and find the blank space. It then "breaks" whatever number it stops on, the numerical A1 notation of the cell, it then finds the values for those cells and transposes them In another sheet in the correct row.
The issue I am having with the code this code that has worked the best is it doesn't read any of the cells as blank
(because of the formulas?) and it transposes all to the same row.
function transpose(){
var data = SpreadsheetApp.getActiveSpreadsheet();
var input =data.getSheetByName("EMAIL INPUT");
var output = data.getSheetByName("EMAIL OUTPUT");
var lr =input.getLastRow();
for (var i=2;i<20;i++){
var cell = input.getRange(i, 1).getValue();
if (cell == ""){
break
}
}
var set = input.getRange(2, 1, i-1).getValues();
output.getRange(2,1,set[0].length,set.length) .
.setValues(Object.keys(set[0]).map ( function (columnNumber) {
return set.map( function (row) {
return row[columnNumber];
});
}));
Logger.log(i);
Logger.log(set);
}
What I need the code to do is look through all the data and separate the sets of data by a condition.
Then Transpose that information on another sheet. Each set (or array) of data will go into a different row. With each component filling across the column (["customer1", "Item1","Item2"].
EDIT:
Is it Possible to pull different data sets from a single column and turn them into arrays? I believe being able to do that will work if I use "appendrow" to tranpose my different arrays to where I need them.
Test for the length of cell. Even if it is a formula, it will evaluate the result based on the value.
if (cell.length !=0){
// the cell is NOT empty, so do this
}
else
{
// the cell IS empty, so do this instead
}
EXTRA
This code takes your objective and completes the transposition of data.
The code is not as efficient as it might/should because it includes getRange and setValues inside the loop.
Ideally the entire Output Range could/should be set in one command, but the (unanswered) challenge to this is knowing in advance the maximum number rows per contiguous range so that blank values can be set for rows that have less than the maximum number of rows.
This would be a worthwhile change to make.
function so5671809203() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var inputsheetname = "EMAIL_INPUT";
var inputsheet = ss.getSheetByName(inputsheetname);
var outputsheetname = "EMAIL_OUTPUT";
var outputsheet = ss.getSheetByName(outputsheetname);
var inputLR =inputsheet.getLastRow();
Logger.log("DEBUG: the last row = "+inputLR);
var inputrange = inputsheet.getRange(1, 1,inputLR+1);
Logger.log("the input range = "+inputrange.getA1Notation());
var values = inputrange.getValues();
var outputval=[];
var outputrow=[];
var counter = 0; // to count number of columns in array
for (i=0;i<inputLR+1;i++){
Logger.log("DEBUG: Row:"+i+", Value = "+values [i][0]+", Length = "+values [i][0].length);
if (values [i][0].length !=0){
// add this to the output sheet
outputrow.push(values [i][0]);
counter = counter+1;
Logger.log("DEBUG: value = "+values [i][0]+" to be added to array. New Array Value = "+outputrow+", counter = "+counter);
}
else
{
// do nothing with the cell, but add the existing values to the output sheet
Logger.log("DEBUG: Found a space - time to update output");
// push the values onto an clean array
outputval.push(outputrow);
// reset the row array
outputrow = [];
// get the last row of the output sheet
var outputLR =outputsheet.getLastRow();
Logger.log("DEBUG: output last row = "+outputLR);
// defie the output range
var outputrange = outputsheet.getRange((+outputLR+1),1,1,counter);
Logger.log("DEBUG: the output range = "+outputrange.getA1Notation());
// update the values with array
outputrange.setValues(outputval);
// reset the row counter
counter = 0;
//reset the output value array
outputval=[];
}
}
}
Email Input and Output Sheets

Google App Script to Break out Query Parameters in Google Sheets

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.

Match text strings of two columns on different sheet and post back using google script

I am trying to match two columns on two separate worksheets in the same workbook
I'm am trying to match column A in both sheets
SheetA = FindReplace SheetB = Test
If a match exists then post back the value to the right of the match cell in SheetA to the same row in SheetB but in Column B
The ranges in the matching columns may be different in size
The function runs but NO matches are found
Also, I think I am posting back each row that is found but I would like to post back only once after ALL matches are found
Thank you for any help with this
If is helpful here a GS with some data and the function in it:
https://docs.google.com/spreadsheets/d/1cVyCmnFYy_4Ghmx1r-dvCGhxrnu-tCPaKZSX_BRHfnM/edit?usp=sharing
function MatchColumns(){
// gets spreadsheet A and the range of data
var sheetA =SpreadsheetApp.getActiveSpreadsheet().getSheetByName("FindReplace");
var dataA = sheetA.getRange('A:B').getValues();
// gets spreadsheet B and the range of data
var sheetB = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Test");
var dataB = sheetB.getRange('A:B').getValues();
// loops through column A of spreadsheet A & B and compare
for(var i = 0; i > sheetA.getLastRow(); i++){
// checks to see if ith row value in 1st Column is the same
if(dataA[i][0] == dataB[i][0]){
//if match then get cell value in ajacent cell column B to right in sheetA
var value = sheetA.getRange(i+1, 2).getValue();
//Post Back the value in cell to column B in sheet B
sheetB.getRange(i+1, 2).setValue(value);
};
};
}
How about this modification? Please think of this as one of several solutions.
It processed the data as array.
If the value of column A of sheetB is not existing to the column A of sheetA, if (b != res.length - 1) res.push([""]); is used.
The length of created array is necessary to be the same to the length of dataB. I used this.
Modified script :
function MatchColumns(){
// gets spreadsheet A and the range of data
var sheetA =SpreadsheetApp.getActiveSpreadsheet().getSheetByName("FindReplace");
var dataA = sheetA.getRange(2, 1, sheetA.getLastRow(), 2).getValues();
// gets spreadsheet B and the range of data
var sheetB = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Test");
var dataB = sheetB.getRange(2, 1, sheetB.getLastRow(), 1).getValues();
// Added
var res = [];
for (var b in dataB) {
for (var a in dataA) {
if (dataA[a][0] == dataB[b][0]) res.push([dataA[a][1]]);
}
if (b != res.length - 1) res.push([""]);
}
sheetB.getRange(2, 2, res.length, res[0].length).setValues(res);
}
Result :
If I misunderstand your question, please tell me. I would like to modify.
The function did run after changing the > for <

Categories

Resources