I need to overwrite column C with the data in column F... But I want the headers in Row1 of each column to stay the same. This is the closest code that I have found as a starting point. It only moves shuffles two columns to the left. Whereas I am looking to overwrite one column with anoter while preserving the hearders
function moveColumns() {
// get sheet
var sheet = SpreadsheetApp.getActiveSheet();
// select columns to be moved
var columnsToMove = sheet.getRange("E1:F1");
// move columns to the left
sheet.moveColumns(columnsToMove, 4);
}
the code above, I have been unable to alter
Sheet.moveColumn() moves an entire column. You need to use Range.moveTo().
function moveColumns() {
// get sheet
var sheet = SpreadsheetApp.getActiveSheet();
// get range of column F (6) starting with row 2
var columnsToMove = sheet.getRange(2,6,sheet.getLastRow()-1,1);
// move range to column C (3) starting at row 2
columnsToMove.moveTo(sheet.getRange(2,3));
}
Reference
Sheet.getRange()
Range.moveTo()
Try this:
function moveColumns() {
const ss = SpreadsheetApp.getActive();
const sh = ss.getActiveSheet();
const vs = sh.getRange("C2:F" + sh.getLastRow()).getValues();
let o = vs.map(([c,d,e,f]) => [f]);
sh.getRange(2,3, o.length,o[0].length).setValues(o);
}
Related
I am trying to compare values on 2 different columns values to ensure that they exist(on Sheet 1)
Once the script is aware that they exist I need to access sheet1's column for quantity and add that value to sheet 2's quantity column. The problem is I am unsure of how to just get the location/index of the foreach loop and offset a setValue to another column without setting the value to the entire column(I dont want to do that if the product name of column A does not exist in Sheet1)
Here is the code example of how i am trying to do it
I have included it in a pastebin because I could not figure out how to format the code to paste ( sorry i'm super new at this!)
<https://pastebin.com/EKB2n9kA>
Sheet1 incoming data https://drive.google.com/file/d/1eLNeOZZbdeCDfMMImksVRnBXwKxpHIO_/view?usp=sharing
Sheet2 'base' data to add quantity values to https://drive.google.com/file/d/1h26H9eQgZapd2Y0LVamhRPYme-8LmVF0/view?usp=sharing
example of expected/wanted results https://drive.google.com/file/d/1-0ozD5PrbIq-otG4j7kAyLufQFjDR5Hi/view?usp=sharing
I have also attached 3 different reference photos
Sheet 1 is 'incoming' data to read
Sheet 2 is our 'base' data and where Sheet1's quantity column needs to be added to
the third screenshot is the expected result(having the script skip over rows that do not contain matching data but still being able to get the quantity value based on the index/location the value was found)
Any insight on how to achieve this would be sincerely appreciated
I have tried pushing the results into an empty array but it does not seem to give much useful info the way I am doing it.
I have also tried just getting an offset range (getRange("G2:G").offset(0,3).setValues() to set the results but it sets the value of the entire column instead of only where the values match for each column being compared.
From your <https://pastebin.com/EKB2n9kA>, it seems that your current script is as follows.
function compareCol() {
var ss = SpreadsheetApp.getActiveSpreadSheet();
var s1 = ss.getSheetByName("Sheet1");
var s2 = ss.getSheetByName("Sheet2");
var datA = s1.getRange("A2:A").getValues();
var datB = s2.getRange("A2:A").getValues();
var quantityA = s1.getRange("C2:C").getValues();
var quantityB = s2.getRange("D2:D");
let matchingCol = [];
for (var i in datA) {
for (var x in datB[i]) {
if (datA[i][0] === datB[x][0]) {
}
}
}
}
Modification points:
In your script, the if statement in the loop has no script. And, quantityA, quantityB, and matchingCol are not used.
When your script is modified for achieving your goal, how about the following modification?
Modified script:
function compareCol() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s1 = ss.getSheetByName("Sheet1");
var s2 = ss.getSheetByName("Sheet2");
var datA = s1.getRange("A2:C" + s1.getLastRow()).getValues().reduce((o, [a, , c]) => (o[a] = c, o), {});
var datB = s2.getRange("A2:D" + s2.getLastRow()).getValues();
var matchingCol = datB.map(([a, , , d]) => [datA[a] || d]);
s2.getRange(2, 4, matchingCol.length).setValues(matchingCol);
}
In this modification, when the value of column "A" in "Sheet2" is not found from column "A" of "Sheet1", the value of column "D" of "Sheet2" is used. If you want to remove this, please modify it as follows.
From
var matchingCol = datB.map(([a, , , d]) => [datA[a] || d]);
To
var matchingCol = datB.map(([a]) => [datA[a] || null]);
References:
reduce()
map()
Hopefully this will help
function comparingColumns() {
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName("Sheet0");
const osh = ss.getSheetByName("Sheet1");
const [h,...vs] = sh.getDataRange().getValues();
vs.forEach((r, i) => {
//comparing column A to Column D
if (r[0] == r[3]) {
//getting column G of same row in sheet1
osh.getRange(i + 2, 7).setValue(r[6]);
}
})
}
So I have a table that will have data to the last row except column B. (for example, cols A, C, D etc. stop at row 40, but B stops at maybe row 25). I want to programmatically keep the series going starting at the last cell with data in B and autofill down to the last row in the spreadsheet. (Hopefully a script doing this will recognize the series and not just copy the same data to all the empty cells. When I do it manually it works.) I have something started here but I can't figure out how to call out the range of where to start the series. I get an error on line 7 "Exception: Range not found".
function fillDownFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var sc = ss.getRange("B1:B").getValues();
var scr = sc.filter(String).length;
var lr = ss.getLastRow();
var fillDownRange = ss.getRange(scr,2,lr)
ss.getRange(scr).copyTo(fillDownRange);
}
In addition to the answers already provided, Apps Script already has an autoFill() method that you can use to do this. With this you just have to define a source and a destination range. This works the same as selecting the range and dragging down your mouse manually on the corner.
function fillDownFunction() {
var ss= SpreadsheetApp.getActiveSpreadsheet().getActiveSheet()
var lastsheetrow = ss.getLastRow() //last row with data
//last row in the autofill range with data
var lastrangerow = ss.getRange("B1:B").getNextDataCell(SpreadsheetApp.Direction.DOWN).getRow()
var sourcerange = ss.getRange(2, 2,lastrangerow-1) //in your screenshot, this would be B2:B6
var destinationrange = ss.getRange(2, 2, lastsheetrow-1) //This would be B2:B12
sourcerange.autoFill(destinationrange, SpreadsheetApp.AutoFillSeries.DEFAULT_SERIES)
}
Note that I skipped the header row and offset the range lengths by -1 to compensate. This is because the autofill logic uses the entire range, so it would also take into account "Item2" and repeat it every 6 rows as "Item3", "Item4" and so on. A disadvantage in this case, but may prove useful if you plan to use autofill in more complex ways in the future.
Here is one way you could have the last fill value copy down. The function grabs all of the values, maps the row that needs to fill down and then copies that last value down the rest of the sheet.
function fillDownFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var tab = ss.getActiveSheet();
var sc = tab.getDataRange().getValues();
var lastRow = sc.length;
var values = sc.map(function(r) { return r[1]; }).filter(i => i);
var lastFillRow = values.length;
var fillDownValue = values.slice(-1);
tab.getRange(lastFillRow + 1, 2, lastRow - lastFillRow).setValue([fillDownValue]);
}
I'm trying to paste from source range to target range but skipping one column in target and keeping the values already there (the ones other than those in source range). One code I was using before was able to keep the existing values and just append the ones not matched from source to last row. But with an improvement to skip columns, the values are pasted over the ones I actually wanna keep (not to the last row with no values).
First code snippet:
function appendSheet1ToSheet2() {
const ss = SpreadsheetApp.getActive();
const source = ss.getRange('Sheet1!A1:C');
const target = ss.getRange('Sheet2!A1:D');
appendUniquesToRange_(source, target);
}
function appendUniquesToRange_(sourceRange, targetRange) {
const dataToAppend = sourceRange.getValues().map(row => [row[0], row[1], ,row[2]]);
const existingData = targetRange.getValues()
const newData = existingData
.concat(dataToAppend)
.filter((row, rowIndex, array) =>
array
.map(tuple => tuple[0])
.indexOf(row[0]) === rowIndex && row[0] !== ''
);
targetRange
.offset(0, 0, newData.length, newData[0].length)
.setValues(newData);
}
Improvement to appendSheet1ToSheet2 which skips column, but doesn't seem to keep values in range that I want kept, just pastes over.
function appendSheet1ToSheet2() {
const ss = SpreadsheetApp.getActive();
const source = ss.getSheetByName("Sheet1").getRangeList(["A1:B", "C1:C"]).getRanges();
const target = ss.getSheetByName('Sheet2').getRangeList(["A2", "D2"]).getRanges();
source.forEach((r, i) => r.copyTo(target[i]));
}
Here is the sample. What I want is values/count/link 2&3 to be appended to sheet2 in row 6 and 7 (column A,B,D). How can I basically combine the two codes to make this improvement? Hope that's clear.
Thanks
So, do you want to keep all the values in column C of the target sheet intact?
It can be done pretty easy with the three additional lines in the function appendSheet1ToSheet2():
function appendSheet1ToSheet2() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const source = ss.getRange('Sheet1!A1:C');
const target = ss.getRange('Sheet2!A1:D');
const col_c = ss.getRange('Sheet2!C1:C'); // <--- get the column
const col_c_values = col_c.getValues(); // <--- get the old values
appendUniquesToRange_(source, target);
col_c.setValues(col_c_values); // <--- put the old values back
}
But I'm not sure if it makes sense. Because it's hard to tell what the columns A, B and D will contain after append the new data from the source sheet.
First of all I'm completly new to Google Sheets and Scripts so I'm trying to learn all by myself, so sorry if I'm asking something 'stupid'
So my problem is: I've created a Script to basically add a Background color to a cell, based in a certain condition. What I wanted to do now was identify all cells with that added Background color, select the entire row in which they are contained and copy all the information to another sheet.
I was trying to do it with an if condition, but I have no idea how to select the entire row or if the condition I'm using to get the Background color is right. Can anybody help me? Thank you so much!
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = ss.getSheetByName('Sheet1');
var r = s.getRange('F:F');
var v = r.getBackgrounds();
for(var i=v.length-1;i>=0;i--)
if(v =='B7E1CD')
s.getCurrentCell.copyTo(spreadsheet.getRange(20,1)); //don't mind this instruction, I know it isn't right
Explanation:
Your goal is to filter only the rows for which the background color in column F is '#b7e1cd' and copy these values to a different sheet.
You don't need a loop to achieve this goal, you can directly filter the rows for which the background color in F is '#b7e1cd'.
const new_values = values.filter((_,i)=>colors[i]=='#b7e1cd');
where values is defined as the full datarange of Sheet1:
const values = sh.getDataRange().getValues();
You can use copyTo in case you want to move data within the same spreadsheet file. This is why I prefer getRange/setValues because it does not have this limitation.
Solution:
The following script will paste the values starting from the cell A1 of Sheet2. If you want a different starting cell, modify 1,1 in target_sh.getRange(1,1,..) to a different cell reference.
function myFunction() {
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName('Sheet1'); // put the name of the source sheet
const target_sh = ss.getSheetByName('Sheet2'); // put the name of the target sheet
const colors = sh.getRange('F1:F').getBackgrounds().flat();
const values = sh.getDataRange().getValues();
const new_values = values.filter((_,i)=>colors[i]=='#b7e1cd');
target_sh.getRange(1,1,new_values.length,new_values[0].length).setValues(new_values);
}
Updated code based on your sheet:
function iftest() {
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName('HH 2 - Main Database'); // put the name of the source sheet
const target_sh = ss.getSheetByName('HH 1 - New Database'); // put the name of the target sheet
const colors = sh.getRange('F1:F').getBackgrounds().flat();
console.log(colors);
const values = sh.getDataRange().getValues();
let LastRow = target_sh.getLastRow();
const new_values = values.filter((_, i) => colors[i]=='#ff6d01');
target_sh.getRange(LastRow+1,1,new_values.length,new_values[0].length).setValues(new_values);
}
I want to merge several sheets into 1 spreadsheeet and want to paste all data below each other.
My current code looks like that:
function updateMaster() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var allSheets = ss.getSheets();
var repArray = new Array();
// build array of all sheets
for (i in allSheets) {
repArray.push(allSheets[i].getName());
}
// store all sheets in array
var sheetArray = [];
// loop through all rep sheets
for (var j in allSheets) {
// get each sheet
var tempSheet = ss.getSheetByName(repArray[j]);
// get sheet data
var dataRange = tempSheet.getDataRange().getValues();
// remove the first header row
dataRange.splice(parseInt(0), 1);
// append sheet data to array
var sheetArray = sheetArray.concat(dataRange);
}
// Time to update the master sheet
var mSheet = ss.getSheetByName("Master");
// clear the whole sheet
mSheet.clear({contentsOnly:true});
// write to the Master sheet via the array
mSheet.getRange(1, 1, sheetArray.length, 4).setValues(sheetArray); //I GET THE ERROR BECAUSE OF THIS LINE
// force spreadsheet updates
SpreadsheetApp.flush();
// pause (1,000 milliseconds = 1 second)
Utilities.sleep("200");
// delete empty rows at bottom
var last = mSheet.getLastRow();
var max = mSheet.getMaxRows();
if (last !== max) {mSheet.deleteRows(last+1,max-last);}
}
However, I get an error, because other sheets have not the same column length.
The error message:
Incorrect range width, was 5 but should be 4
Here I get the error:
mSheet.getRange(1, 1, sheetArray.length, 4).setValues(sheetArray);
I basically, just want to post the data below. Any recommendation how I could make the column length variable?
Here is a link to my spreadsheet:
https://docs.google.com/spreadsheets/d/1TuHaqs20PvNwOJ46bQOAwN3Nt5vyRl7Win1Lul4m-e8/edit?usp=sharing
I appreciate your replies!
The sheetArray array has inner row elements of different lengths. Some inner arrays have 4 elements, some have 5 elements. You can't set values from an array with different inner element lengths. If you change the column width to 5, the error states that it should be 4, if you change it to 4, it says that it should be 5. You need to either add an extra empty element to the range with 4 columns, or write each sheet to the master individually.
Get one of the inner arrays, and find the length of an inner array. That is your column length.
On this line:
var dataRange = tempSheet.getDataRange().getValues();
You are using the getDataRange() method. That causes the range to be different dimensions for each sheet that has a different number of columns. If you set the column width to the biggest number of columns in the sheet with the most columns, then all your inner elements would be the same length. Your code would write a lot of empty cells. But don't know if there is any downside to that .
var columsToWriteTo = sheetArray[0].length
mSheet.getRange(1, 1, sheetArray.length, columsToWriteTo).setValues(sheetArray);
The above code gets the first inner array of the two dimensional sheetArray, and gets the length of the inner array.
The other answer pointed the issue but did not provide a solution.
What you should simply do is to make the final array homogeneous before setting its value with setValues().
Below is a code demo that does it by adding empty items in rows that are too short. I added comments in code to explain
function ArrayNormalize() {
var sh = SpreadsheetApp.getActive();
var data1 = sh.getSheets()[0].getDataRange().getValues(); // get data from sheet1
var data2 = sh.getSheets()[1].getDataRange().getValues();// same from sheet2 that has a different width
var combine = data1.concat(data2);
Logger.log("combine = "+JSON.stringify(combine));
//get Max array width
var maxWidth=0
for(var n=0;n<combine.length;n++){
Logger.log(n+' - '+combine[n]+" length = "+combine[n].length);
if(combine[n].length>=maxWidth){maxWidth=combine[n].length};
}
Logger.log("maxWidth = "+maxWidth);
//normalize
for(var n=0;n<combine.length;n++){
while(combine[n].length<maxWidth){
combine[n].push('');// while row is shorter add empty "cells" (=array elements)
}
}
sh.insertSheet().getRange(1,1,combine.length,combine[0].length).setValues(combine);// insert the result in new sheet to check the result but you can insert it anywhere else of course
}