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.
Related
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);
}
I want to remove duplicates across 2 different sheets.
I have my active sheet, and I want to remove duplicates that already exist in my sheet "Blacklist". I want to run this process for both Column A and Column B (or simply for any values across the entire sheets). When a duplicate is found, I want to leave the row in tact but replace the value with '' (e.g. an empty cell).
I have a working version I mangled together, but only for the active sheet.
N.B. it's the findDuplicate function that I use, the removeDuplicate function I left there not to mess anything up :)
// this is a Google Apps Script project
function onOpen() {
var spreadsheet = SpreadsheetApp.getActive();
var menuItems = [
{ name: 'Find duplicates...', functionName: 'findDuplicate' },
{ name: 'Remove duplicates...', functionName: 'removeDuplicate' }
];
spreadsheet.addMenu('Duplicates', menuItems);
}
function removeDuplicate() {
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getActiveRange();
var data = range.getValues();
var rowNum = range.getRow();
var columnNum = range.getColumn();
var columnLength = data[0].length;
var uniqueData = [];
var duplicateData = [];
// iterate through each 'row' of the selected range
// x is
// y is
var x = 0;
var y = data.length;
// when row is
while (x < y) {
var row = data[x];
var duplicate = false;
// iterate through the uniqueData array to see if 'row' already exists
for (var j = 0; j < uniqueData.length; j++) {
if (row.join() == uniqueData[j].join()) {
// if there is a duplicate, delete the 'row' from the sheet and add it to the duplicateData array
duplicate = true;
var duplicateRange = sheet.getRange(
rowNum + x,
columnNum,
1,
columnLength
);
duplicateRange.deleteCells(SpreadsheetApp.Dimension.ROWS);
duplicateData.push(row);
// rows shift up by one when duplicate is deleted
// in effect, it skips a line
// so we need to decrement x to stay in the same line
x--;
y--;
range = sheet.getActiveRange();
data = range.getValues();
// return;
}
}
// if there are no duplicates, add 'row' to the uniqueData array
if (!duplicate) {
uniqueData.push(row);
}
x++;
}
}
function findDuplicate() {
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getActiveRange();
var data = range.getValues();
var rowNum = range.getRow();
var columnNum = range.getColumn();
var columnLength = data[0].length;
var uniqueData = [];
// iterate through each 'row' of the selected range
for (var i = 0; i < data.length; i++) {
var row = data[i];
var duplicate = false;
// iterate through the uniqueData array to see if 'row' already exists
for (var j = 0; j < uniqueData.length; j++) {
if (row.join() == uniqueData[j].join()) {
// if there is a duplicate, highlight the 'row' from the sheet
duplicate = true;
var duplicateRange = sheet.getRange(
rowNum + i,
columnNum,
1,
columnLength
);
duplicateRange.setValue('');
}
}
// if there are no duplicates, add 'row' to the uniqueData array
if (!duplicate) {
uniqueData.push(row);
}
}
}
Thanks so much for your help! I've been at this for a few hours and figured I should just ask the experts for advice :)
The first lines of both your removeDuplicate and findDuplicate function seems indeed to indicate that you refer to the active spreadsheet / sheet / range
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getActiveRange();
var data = range.getValues();
If you want to be able to use the same function for a given spreadsheet / sheet / range which is not the active one, you will need to use other functions than the getActiveXXX().
For example, to get the sheet named "Blacklist", you should use
sheet = spreadsheet.getSheetByName("Blacklist")
(see also https://developers.google.com/apps-script/reference/spreadsheet/spreadsheet#getsheetbynamename)
If you want to access a specific range which differs from the active range, you should use the getRange method (see also https://developers.google.com/apps-script/reference/spreadsheet/spreadsheet#getrangea1notation)
Note that getRange method can be used in different ways, e.g.
getRange("A1:D4"), getRange(1, 1, 3, 3) (the parameters being respectively startRow, startColumn, numRows,numColumns)
Additionally, if you don't want to hardcode the last line of your 2 columns, you will most probably need this function to find the last line in the code :
https://developers.google.com/apps-script/reference/spreadsheet/spreadsheet#getlastrow
(there is also an example there showing how to use getRange() in combination with getLastRow()).
I hope this will help you going further.
Please note that I didn't check the rest of your code and just assumed that your deduplication logic works fine as you mentioned it in your commment.
Good luck !
I need to move data from two sheets to one separate sheet. My code works provided the sheet is laid out as follows:
However, the sheet must be laid out like this:
I need to insert data above the row containing Amount Total but below the first row containing the headers. I feel this may be possible by adding rows above the Amount Total using the script but my current code uses getLastRow() to skip over the existing data in exports sheet. This does not work when Amount Total is placed as shown in the 2nd image. How can I achieve this? My existing code is below:
function exportData() {
var ards = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Newtownards");
var bangor = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Bangor");
var export = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Export");
var lastRow = export.getLastRow();
var nextCell = lastRow + 1;
//get values for export
var ardsRefValues = ards.getRange("B12:G12").getValues();
var ardsAmountValues = ards.getRange("B13:G13").getValues();
var bangorRefValues = bangor.getRange("B12:G12").getValues();
var bangorAmountValues = bangor.getRange("B13:G13").getValues();
for(var i = 0; i<=6; i++){
var a = ardsRefValues.join().split(',').filter(Boolean);
var b = ardsAmountValues.join().split(',').filter(Boolean);
var c = bangorRefValues.join().split(',').filter(Boolean);
var d = bangorAmountValues.join().split(',').filter(Boolean);
}//close for loop
//find length of the arrays
var aLength = a.length - 1;
var cLength = c.length - 1;
//loop through ards data arrays
for(i = 0; i<=aLength; i++){
export.getRange(nextCell, 5).setValue(a[i]);
export.getRange(nextCell, 4).setValue(b[i]);
nextCell++
}//close for loop
//loop through bangor data arrays
for(i = 0; i<=cLength; i++){
export.getRange(nextCell, 5).setValue(c[i]);
export.getRange(nextCell, 4).setValue(d[i]);
nextCell++
}//close for loop
var data = new Array();
var lastRow = export.getLastRow();
var total = 0;
for(var i = 2; i<=lastRow; i++){
var range = export.getRange(i, 4);
data = range.getValue();
total += data;
}
export.getRange(2, 7).setValue(total);
}//close function
Update: Good news is that these sheets will be updated every week. So the amount Total must be added into the row just below the last row of data
I modified the code this way... it gathers data from all the sheets and finds only the rows that have data, BUT now I am having a problem modifying the range with each pass so that it is equal to the number of rows that do have value (found with (values[row][0] != '')). I have put a ??? in the spot where I am trying to have a variable height.
function getAllData() {
var folder = DocsList.getFolderById("folderid");
var contents = folder.getFiles();
Logger.log("file length: " + contents.length);
var file;
var data;
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Base")
sheet.clearContents();
var numOfFiles = contents.length;
for (var i = 0; i < numOfFiles; i++) {
file = contents[i];
Logger.log("count: " + i);
var theFileType = file.getFileType();
Logger.log("theFileType: " + theFileType);
if (theFileType==DocsList.FileType.SPREADSHEET) {
var sheet2 = SpreadsheetApp.open(file).getSheetByName("Sheet 1");
var lastLine = sheet2.getLastRow();
var values = sheet2.getRange('A3:J').getValues();
var formulas = sheet2.getRange('A3:J').getFormulas();
var data = [];
for(var row = 0 ; row < (values).length ; row++){
var lastrow = sheet.getLastRow()+1;
if (values[row][0] != '') {
for(var col = 0 ; col < formulas[row].length ; col++){
if(formulas[row][col] != '')
{values[row][col] = formulas[row][col]};
data.push(values[row]);}
if(data.length > 0)
sheet.getRange(lastrow, 1, ???, data[0].length).setValues(data);
}
}
};
}}
You are using getValue() as opposed to getValues() (With a letter "s" on the end)
var onecell = posheet.getRange('B4').getValue();
The documentation states:
getValue() - Returns the value of the top-left cell in the range.
The parameter for getRange() is kind of tricky and not well documented.
For example this:
getRange(2, 3, 6, 4)
gets a range from C2 to G8. Figure that out. The first number is the number 2, which is for the row 2. The second number is 3, for the third column (which is C). The third and fourth numbers are relative to the first two numbers.
Also, you are using: appendRow([array]) which uses an array for the parameter. So you must make sure that the data is in the form of an array, or use something else.
Here is the link for getValues:
Google Documentation - getValues
The example is this code:
// The code below will get the values for the range C2:G8
// in the active spreadsheet. Note that this will be a javascript array.
var values = SpreadsheetApp.getActiveSheet().getRange(2, 3, 6, 4).getValues();
Logger.log(values[0][0]);
Here is code that seems to work:
function getAllData() {
var folder = DocsList.getFolderById("Your file ID");
var contents = folder.getFiles();
Logger.log("file length: " + contents.length);
var file;
var data;
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1")
sheet.clearContents();
sheet.appendRow(["Value from Sheet One", "Range of values from Sheet Two"]);
var numOfFiles = contents.length;
for (var i = 0; i < numOfFiles; i++) {
file = contents[i];
Logger.log("count: " + i);
//Reset to null on every iteration
var onecell = null;
var theRange = null;
var theFileType = file.getFileType();
Logger.log("theFileType: " + theFileType);
if (theFileType==DocsList.FileType.SPREADSHEET) {
var sheet1 = SpreadsheetApp.open(file).getSheetByName("Sheet1");
var sheet2 = SpreadsheetApp.open(file).getSheetByName("Sheet2");
// The code below will get the values for the range A3:A9
// in the active spreadsheet. Note that this will be a javascript array.
onecell = sheet1.getRange('B4').getValue();
theRange = sheet2.getRange(1,3,1,6).getValues();
Logger.log('onecell: ' + onecell);
Logger.log('onecell[0][0]: ' + onecell[0][0]);
Logger.log('theRange: ' + theRange)
Logger.log('theRange[0][0]: ' + theRange[0][0])
var multipleValues = [theRange[0][0], theRange[0][1], theRange[0][2], theRange[0][3], theRange[0][4]];
Logger.log('multipleValues: ' + multipleValues);
sheet.appendRow([onecell, "'" + multipleValues]);
};
}
}
In the first column, it only enters one value into the sheet cell. In the second column, the cell gets multiple values put into it from the row. In other words, and entire rows values, and combined and put into one cell. I think that's what you want from the code.
If you try to put an array into a spreadsheet cell, instead of showing the array of values as text, it shows something like an object. So I put a quote in front of the values so the cell formatting would default to text.
I have a timesheet spreadsheet for our company and I need to sort the employees by each timesheet block (15 rows by 20 columns). I have the following code which I had help with, but the array quits sorting once it comes to a block without an employee name (I would like these to be shuffled to the bottom). Another complication I am having is there are numerous formulas in these cells and when I run it as is, it removes them. I would like to keep these intact if at all possible. Here's the code:
function sortSections()
{
var activeSheet = SpreadsheetApp.getActiveSheet();
//SETTINGS
var sheetName = activeSheet.getSheetName(); //name of sheet to be sorted
var headerRows = 53; //number of header rows
var pageHeaderRows = 5; //page totals to top of next emp section
var sortColumn = 11; //index of column to be sorted by; 1 = column A
var pageSize = 65;
var sectionSize = 15; //number of rows in each section
var col = sortColumn-1;
var sheet = SpreadsheetApp.getActive().getSheetByName(sheetName);
var data = sheet.getRange(headerRows+1, 1, sheet.getMaxRows()-headerRows, sheet.getLastColumn()).getValues();
var data3d = [];
var dataLength = data.length/sectionSize;
for (var i = 0; i < dataLength; i++) {
data3d[i] = data.splice(0, sectionSize);
}
data3d.sort(function(a,b){return(((a[0][col]<b[0][col])&&a[0][col])?-1:((a[0][col]>b[0][col])?1:0))});
var sortedData = [];
for (var k in data3d) {
for (var l in data3d[k]) {
sortedData.push(data3d[k][l]);
}
}
sheet.getRange(headerRows+1, 1, sortedData.length, sortedData[0].length).setValues(sortedData);
I think to solve your problems is possible to use the Range.sort function instead of the custom code. The sort function relocates also the formulas but in a tricky way - if a cell formula contains a cell reference, the sort function changes the row index in relocated cell to have the new cell row index, for instance, initially the cell C1 contains the =A1*B1 formula, after the sort operation the row 1 relocated to the row 3 and the cell 'C3' will contain not =A1*B1, but =A3*B3.
With this modification your code should looks something like this
function sortSections()
{
var activeSheet = SpreadsheetApp.getActiveSheet();
//SETTINGS
var sheetName = activeSheet.getSheetName(); //name of sheet to be sorted
var headerRows = 53; //number of header rows
var pageHeaderRows = 5; //page totals to top of next emp section
var sortColumn = 11; //index of column to be sorted by; 1 = column A
var pageSize = 65;
var sectionSize = 15; //number of rows in each section
var col = sortColumn-1;
var sheet = SpreadsheetApp.getActive().getSheetByName(sheetName);
var range = sheet.getRange(headerRows+1, 1, sheet.getMaxRows()-headerRows, sheet.getLastColumn());
range.sort({column: sortColumn, ascending: true});
...
}