Google Apps Scripts trouble indexing values within a row - javascript

I have an array within a spreadsheet that has stock data. I want to find a column number "restock value", and the row number product/sku (Row) to eventually build a custom function. The column is indexing fine, but the Row keeps logging 0.
I've tested a few objects within my array. I've also tried removing the column index. but no dice. still logging 0.
I've also tried removing [0] from my code. It seems that this is part of the problem, because when I log lookupRowValues without [0] I get the value of each row with their own individual brackets, when I add [0] I only log the first row.
function UnitsToShip(){
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("US");
//Lookup Column
var lc = ss.getLastColumn();
var lookupColumnValues = ss.getRange(4,2,1,lc).getValues()[0];
var indexColumn = lookupColumnValues.indexOf("Restock Amount") + 1;
//Lookup Row
var lr = ss.getLastRow();
var lookupRowValues = ss.getRange(4,2,lr,1).getValues()[0];
var indexRow = lookupRowValues.indexOf("AH-POR-DIF-WHT")+1;
Logger.log(indexRow);
}
I'm testing it with a product in the array that should return 6.

Try this:
function UnitsToShip(){
var ss=SpreadsheetApp.getActiveSpreadsheet().getSheetByName("US");
//Lookup Column
var lc = ss.getLastColumn();
var lookupColumnValues = ss.getRange(4,2,1,lc-1).getValues()[0];
var indexColumn = lookupColumnValues.indexOf("Restock Amount") + 2;
Logger.log('indexColumn: %s',indexColumn);
//Lookup Row
var lr = ss.getLastRow();
var lookupRowValues = ss.getRange(4,2,lr-3,1).getValues().map(function(r){return r[0];});
var indexRow = lookupRowValues.indexOf("AH-POR-DIF-WHT")+4;
Logger.log('indexRow: %s',indexRow);
var restockAmount=ss.getRange(indexRow,indexColumn).getValue();
Logger.log('Restock Amount: %s',restockAmount);
}

Related

getRange('A1:B') keeps going for ages - part of delete rows when cell does not contain value

My sample sheet
I have 2 columns, A contains units & B data. I want to delete entire rows from A & B where text in B does not contain '-'.
I have other data in the other columns in the sheet, so this should only apply to A & B.
I normally manually filter and delete these rows.
Following the script in this question, when I used exactly this but with "| My Text Here" replace with "-", it worked, but a lot of the data that should be there were also deleted. So when I should have had 300 rows of data left, I only had 124 after running the script.
How to delete rows that do not containing specific text within A column using Google Sheets App?
function deleteFunction(){
//declarations
var ss = SpreadsheetApp.getActive();
var sheet = ss.getSheetByName('Sheet1');
var dataRange = sheet.getDataRange();
var values = dataRange.getValues();
for(var i=values.length-1;i>0;i--){
var myValue=values[i][0];
Logger.log(i);
if( myValue.indexOf("-")==-1){
Logger.log(myValue);
sheet.deleteRow(i+1);
}
}
}
When I change the dataRange to just getRange('A1:B'); the function keeps going for ages without results.
I changed it to .getRange(1, 2, dataRange.getLastRow(), dataRange.getLastColumn()); and that deletes ALL the data in the range.
Basically, at this point I'm confused, I keep changing the ranges to try to see results.
I'm a newbie to this so I'm not sure where I go wrong.
Try it this way:
function myFunction() {
const ss = SpreadsheetApp.getActive();
const shsr = 3;//data startrow atleast in your sample
const sh = ss.getSheetByName('Sheet1');
const rg = sh.getRange(shsr, 1 , sh.getLastRow() - shsr + 1, 2);
const vs = rg.getValues();
let d = 0;
vs.forEach((r, i) => {
if (!~r[1].toString().indexOf('-')) {
sh.deleteRow(i + shsr - d++)
}
});
}
You need to count the number of rows that you delete because all of the row move up as you delete them. So you have to keep subtracting a larger offset as you delete more and more lines because indexing of the array does not change. Also you were using values[i][0] which is column one and your data indicate it should have been column 2. And finally by using sh.getDataRange() you start running your code at line one instead of line 3 where your data starts.
or this way
This method is a little quicker because you are not deleting one row at a time.
function myFunction() {
const ss = SpreadsheetApp.getActive();
const shsr = 3;
const sh = ss.getSheetByName('Sheet1');
const rg = sh.getRange(shsr, 1 , sh.getLastRow() - shsr + 1, 2);
const vs = rg.getValues();
let d = 0;
let o = [];
vs.forEach((r, i) => {
if (~r[1].toString().indexOf('-')) {
o.push(r)
}
});
rg.clearContent();
sh.getRange(shsr, 1, o.length,o[0].length).setValues(o);
}
In the latter case we are keeping load the rows that we want into an output array and then removing all lines from the shsr to the bottom and replacing them with the wanted lines.

vlookup for different columns simultaneously filling the whole sheet

Below is a code that I found here : https://webapps.stackexchange.com/questions/123670/is-there-a-way-to-emulate-vlookup-in-google-script
I tried to optimize it to my use case in which
to vlookup from source sheet 'data', and fill in values in destination sheet 's'. The problem is that this code does this only for one row. Is there a way to loop over all rows and vlookup and fill in efficiently?
/* recall that we want the follwoing columns => E, F, G, H, M
/*/
function khalookup(){
var s = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var data = SpreadsheetApp.openById("mysheetid");
var searchValue = s.getRange("B2:B").getValues();
var dataValues = data.getRange("A3:A").getValues();
var dataList = dataValues.join("ღ").split("ღ");
var index = dataList.indexOf([searchValue]);
var newRange = []
var row = index + 3;
var foundValue = data.getRange("E"+row).getValue();
var foundValue1 = data.getRange("F"+row).getValue();
var foundValue2 = data.getRange("G"+row).getValue();
var foundValue3 = data.getRange("H"+row).getValue();
var foundValue4 = data.getRange("M"+row).getValue();
s.getRange("K2").setValue(foundValue);
s.getRange("L2").setValue(foundValue1);
s.getRange("M2").setValue(foundValue2);
s.getRange("N2").setValue(foundValue3);
s.getRange("O2").setValue(foundValue4);
}
here is the source sheet where the vlookup shall happen based on the ID "Column A"
And here is how the destination sheet shall look like after the vlookup based on ID "Column B" have been made.
You can do iterate with a loop, e.g. a for loop
Assuming you would like to loop through all rows from index + 3 to the last row, you can modify your code as following:
...
var row = index + 3;
var lastRow = s.getLastRow();
for (var i = row; i <= lastRow; i++){
var foundValue = data.getRange("E"+i).getValue();
var foundValue1 = data.getRange("F"+i).getValue();
var foundValue2 = data.getRange("G"+i).getValue();
var foundValue3 = data.getRange("H"+i).getValue();
var foundValue4 = data.getRange("M"+i).getValue();
s.getRange("K" + (2+i-row)).setValue(foundValue);
s.getRange("L" + (2+i-row)).setValue(foundValue1);
s.getRange("M" + (2+i-row)).setValue(foundValue2);
s.getRange("N" + (2+i-row)).setValue(foundValue3);
s.getRange("O" + (2+i-row)).setValue(foundValue4);
}
Note that later on you might want to progress from using getValue() and setValue() to getValues() and setValues() - that will make your code execution faster.

Google Script update cells based on new inputs

I'm using Google Scripts with the aim of calling an already filled cell using the ID. Once the data has been called, the user would be able to update and send it back to the main "data", in case some update is necessary.
The code isn't giving any error, but is not updating the cells either. See below an example of the code:
function Update() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var formS = ss.getSheetByName("App"); // From sheet
var dataS = ss.getSheetByName("Results"); // Get the data already inputted
var str = formS.getRange("D6").getValue(); // The ID to find and to update in the "Inputs" sheet
var values = dataS.getDataRange().getValues();
for (var i = 0; i<=values.length; i++) {
var rowFinder = values[i];
if (rowFinder[columnIndex] == str) {
var rowNumber = i+1;
var updatedValues = [[formS.getRange("D9").getValue(),
formS.getRange("D13").getValue(),
formS.getRange("D14").getValue(),
formS.getRange("D15").getValue(),
formS.getRange("D16").getValue(),
formS.getRange("D17").getValue(),
formS.getRange("D18").getValue(),
formS.getRange("D19").getValue()]];
dataS.getRange(rowNumber, 2, 1, 17).setValues(updatedValues);
}
break;
}
}
Find also an example of the sheet that I'm using, where you could also see the whole code (everything else is working fine). Spreadhsheet here
Why function is not updating the data? I've tried to put the dataS.getRange(rowNumber, 2, 1, 17).setValues(updatedValues); in different parts of the code and it doesn't work either. Any ideas?
It is best to get the values of a range all at once and iterate through the entire range than pulling individual cells.
I tested this on your sample spreadsheet (Thanks for including!)
function SubmitData() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var formS = ss.getSheetByName("App"); // Add the name of the sheet where the data is going to be retrieved
var dataS = ss.getSheetByName("Results"); // Name of the sheet where the data should appear
//get the entire form range
var values = formS.getRange(6, 3, 14, 2).getValues();
//Create an array to paste into the Results tab
var pasteArr =[];
//iterate through the two columns on the App tab, only add values to the paste array
//if Col C is not blank and does not equal "Choose Params"
for (var i in values) {
//the values array looks like this [[row1Col1, row1Col2],[row2Col1, row2Col2]..]
//so to get to the value in row i, column 1 you use values[i][0]
let field = values[i][0];
if (field != "" && field != "Choose Params") {
let val = values[i][1];
pasteArr.push(val);
}
};
Logger.log(pasteArr);
var dataLastRow = dataS.getLastRow() + 1;
//Build the range to paste in - use the length of pasteArr to get the proper number of columns
//then to make pasteArr a 2D array put pasteArr inside another array by using square brackets
dataS.getRange(dataLastRow, 1, 1, pasteArr.length).setValues([pasteArr]);
Logger.log("Done");
ClearCell();
}
Your other functions have similar issues.
function Update() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var formS = ss.getSheetByName("App"); // From sheet
var dataS = ss.getSheetByName("Results"); // Get the data already inputted
var dataVals = dataS.getDataRange().getValues();
//get the entire form range
var values = formS.getRange(6, 3, 14, 2).getValues();
//Create an array to paste into the Results tab
var pasteArr =[];
//iterate through the two columns on the App tab, only add values to the paste array
//if Col C is not blank and does not equal "Choose Params"
for (var i in values) {
let field = values[i][0];
if (field != "" && field != "Choose Params") {
let val = values[i][1];
pasteArr.push(val);
}
}
let numCols = pasteArr.length;
//iterate through the rows on the data tab, update the row with the matching ID
for (var j in dataVals) {
let dataTableId = dataVals[j][0];
let formID = pasteArr[0];
Logger.log(dataTableId + " , "+formID);
if (dataTableId === formID){
//have to convert j from 0 index to columns by adding 1. The + before the j and the 1 force app script to treat it as a number.
dataS.getRange(+j+ +1, 1, 1, numCols).setValues([pasteArr]);
Logger.log("dataTableId matches");
}
}
Logger.log("Done");
}

Google App Script Concating Arrays change the values for the whole array

I have a list of 290 items with 4 columns, which I need to duplicate. I have in a Spreadsheet Row some departements and under it a lot of systems. for each system I need to duplicated the 290 values and add one index column at the front, the department in column 6 and the system in column 7.
I am using the following code:
const ssOrg = SS.getSheetByName("OrgStructure");
function myFunction() {
var afinal = [];
var aDevs = ssDeliverables.getDataRange().getValues();
aDevs.shift();
var lastRow = ssOrg.getLastRow();
var lastColum = ssOrg.getLastColumn();
var count = 1
for (var spalte = 1; spalte <lastColum; spalte++){
var squad = ssOrg.getRange(3,spalte).getValue();
for (var reihe=5; reihe <lastRow; reihe++) {
var system = ssOrg.getRange(reihe,spalte).getValue();
if (system !== ""){
aDevs.map(function(row){
row[0] = count;
row[5] = squad;
row[6] = system;
count ++
return row
})
Logger.log(system);
afinal = afinal.concat(aDevs);
}
}
}
var lastDataRow = ssAssessmentLogic.getLastRow();
ssAssessmentLogic.getRange(2,1,lastDataRow-1,10).clearContent();
var rngResult = ssAssessmentLogic.getRange(2,1,afinal.length,7);
rngResult.setValues(afinal);
}
The problem is that the array at the end (16000 rows) has the same value for each row in column 6 and 7. It is allways the last system & department combination that appears in all 16000 rows.
Where am I wrong?
The question was a little confusing for me but I followed your specifics in the comments section where you explain what exactly info to copy and how and where to paste it.
This gets the job done:
const ss = SpreadsheetApp.getActive();
// this gets the "deliverables" and the "departments", this last ones in a list
// and for each department runs the function to add the complete new modified array to the spreadsheet
function fillsNewSheet(){
var newSheet = ss.getSheetByName('List_DeliverablesALL');
// hardcoded the titles
newSheet.getRange(1, 1, 1, 6).setValues([['taskHasDeliverableNumber', 'Deliverable Description', 'SDP Task', 'SDP Milestone', 'Group', 'Department']])
var deliverables = getDeliverables();
var departments = getDepartments();
for(i=0;i<departments.length;i++){
var departmentsGroup = departments[i];
for(j=0;j<departmentsGroup.length;j++){
addsNewSection(deliverables, departmentsGroup[j])
}
}
}
// this just gets de array of values we are gonna paste for each department in the structure sheet.
function getDeliverables(){
var deliSheet =ss.getSheetByName('Deliverables1');
var deliValues = deliSheet.getRange(2, 2, deliSheet.getLastRow()-1, 4).getValues();
return deliValues;
}
// As the departments are in different columns with different row counts,
// I go over the whole columns and rows and create a single list with all "department" and "group" pairs
function getDepartments(){
var structureSheet = ss.getSheetByName('OrgStructure');
var cols = structureSheet.getLastColumn();
var groups = structureSheet.getRange(3, 1, 1, cols).getValues()[0]
var departments = [];
for(i=1;i<=cols;i++){
var group = groups[i-1];
var groupDeps = structureSheet.getRange(5, i, structureSheet.getLastRow(), 1).getValues();
var subDeps = []
for(j=0;j<groupDeps.length;j++){
subDeps.push([group, groupDeps[j][0]])
}
var filtered = subDeps.filter( function (data) { return data[1] != "" });
departments.push(filtered);
}
return departments;
}
// finally this gets the complete list of "deliverables" from the first sheet, and one specific department.
function addsNewSection(deliverables, department){
var newSheet = ss.getSheetByName('List_DeliverablesALL');
// and in every row of the deliverables list we add the corresponding department/group pair to get the new modified array.
var newSection = []
for(k=0;k<deliverables.length;k++){
var newRow = deliverables[k].concat(department)
newSection.push(newRow)
}
// when this is complete I paste the whole new array in the third sheet.
newSheet.getRange(newSheet.getLastRow()+1, 1, newSection.length, 6).setValues(newSection)
}

drive.properties for loop with google sheets

I have two functions in a google sheet that are meant to loop through a single column containing the file IDs of files in google drive, issuing a Properties.get to retrieve a single property "LastReview" for each document and paste all of the LastReview times in the next available column.
I'm having trouble getting the loop in "loopForMetadata" to work. I want it to acquire a list of all the LastReview times associated with each fileID and then post that to the next available column so that all of the LastReview times align with the fileIDs.
function getProperty(fileId) {
var propertyKey = 'LastReview'
var fileId = '1UaQkJU8r1kE9sxpFg6OD8aOuUCoRnSpB9Agg_R9HJ3s'
var response = JSON.stringify(Drive.Properties.get(fileId, 'LastReview', { visibility: 'PUBLIC' }).value);
var key = "value";
var resposeNoQuote = response.replace(/\"/g, "")
Logger.log(resposeNoQuote);
}
function loopForMetadata() {
var columnNeeded, data, lastColumn, sh;
sh = SpreadsheetApp.getActiveSheet();
lastColumn = sh.getLastColumn();
data = sh.getRange(1, 1, 1, lastColumn).getValues();//Get 2D array of all values in row one
data = data[0];//Get the first and only inner array
columnNeeded = data.indexOf('ID') + 1;//Arrays are zero indexed- add 1
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var rangeData = sheet.getDataRange();
var lastColumn = rangeData.getLastColumn();
var lastRow = rangeData.getLastRow();
var searchRange = sheet.getRange(2, columnNeeded, lastRow - 1, 1);
// Get array of values in the search Range
var rangeValues = searchRange.getValues();
// Loop through array and if condition met, add relevant
// background color.
var resultValues = []
for (i = 0; i < rangeValues.length; i++) {
resultValues.push(getProperty(rangeValues[i]));
Utilities.sleep(10);
}
Logger.log(resultValues);
};
I believe your goal as situation as follows.
You want to retrieve the values using the key of LastReview in the property from the files of fileId.
You want to put the retrieved values to the same row of fileId in the next available column.
You want to achieve this using Google Apps Script.
Modification point:
In your script,
getProperty() doesn't return the values.
resultValues is not put to the Spreadsheet.
When you want to retrieve the value of the key LastReview, Drive.Properties.get(fileId, 'LastReview', { visibility: 'PUBLIC' }).value directly returns the value.
As an important point, when the file of fileId has not property of the key LastReview, it seems that Drive.Properties.get() occurs an error. So in this modification, as a simple workaround, I used the try catch.
When above points are reflected to your script, it becomes as follows.
Sample script:
function loopForMetadata() {
var columnNeeded, data, lastColumn, sh;
sh = SpreadsheetApp.getActiveSheet();
lastColumn = sh.getLastColumn();
data = sh.getRange(1, 1, 1, lastColumn).getValues();//Get 2D array of all values in row one
data = data[0];//Get the first and only inner array
columnNeeded = data.indexOf('ID') + 1;//Arrays are zero indexed- add 1
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var rangeData = sheet.getDataRange();
var lastColumn = rangeData.getLastColumn();
var lastRow = rangeData.getLastRow();
var searchRange = sheet.getRange(2, columnNeeded, lastRow - 1, 1);
var rangeValues = searchRange.getValues();
var resultValues = []
// I modified below script.
for (i = 0; i < rangeValues.length; i++) {
var fileId = rangeValues[i];
var value = "";
try {
value = Drive.Properties.get(fileId, 'LastReview', { visibility: 'PUBLIC' }).value;
} catch(e) {}
resultValues.push([value]);
// Utilities.sleep(10); // I'm not sure whether this is required.
}
sheet.getRange(2, lastColumn + 1, resultValues.length, 1).setValues(resultValues);
Logger.log(resultValues);
};
Note:
Please confirm whether Drive API is enabled at Advanced Google services, again.
Reference:
Properties: get

Categories