Google Sheets Script - Search in Arrays and Replace Values in Next Column - javascript

For some reason I'm not really able to perform a trivial search and replace in google script (using google sheets).
What I want to do is
Read values form an array in Sheet1
Find the value in a 2D-array in sheet2 (two columns). When the value is found, the script adds '+1' to the value in the column next to the found value in sheet 2.
Repeat for the next value in sheet1
Assume I have a value "Muse" in Sheet 1. I want to search for "Muse" in sheet two and if the column next to this value has already a 1, it should add one and write it back. That's it. Then do the same with the next value in sheet1.
I searched for similar scripts but was unfortunately not able to adapt something to my needs.
Edit: It's driving me nuts, this is what I have at the moment:
function findingReplacing()
{
var sh1 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('sheet1');
var sh2 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('sheet2');
var searchfor = sh1.getRange('B2').getValues();
var replacewith = sh2.getDataRange().getValues();
for(var i=1;i<replacewith.length;i++)
{
if(replacewith[i][0]==searchfor)
{
replacewith[i][1]=1;
}
}
//sheet2.SetValues(replacewith);
sh2.setValues(replacewith);
}

just answered it myself: this works:
function findingReplacing()
{
var sh1 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('sheet1');
var sh2 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('sheet2');
var CustDunned = sh1.getRange('B2').getValues();
var range = sh2.getRange("A:B");
var rangeA = range.getValues()
for(var i=1;i<rangeA.length;i++)
{
if(rangeA[i][0]==CustDunned)
{
rangeA[i][1]=rangeA[i][1]+1;
}
}
range.setValues(rangeA);
}

Related

Google Apps Script - Usage of "indexOf" method

first: I really tried hard to get along, but I am more a supporter than a programmer.
I put some Text in Google Calc and wanted to check the amount of the occurances of "Mueller, Klaus" (It appears 5 times within the data range). The sheet contains 941 rows and 1 Column ("A").
Here is my code to find out:
function countKlaus() {
// Aktives Spreadsheet auswählen
var ss = SpreadsheetApp.getActiveSpreadsheet();
// Aktives Tabellenblatt auswählen
var sheet = ss.getSheetByName("Tabellenblatt1");
var start = 1;
var end = sheet.getLastRow();
var data = sheet.getRange(start,1,end,1).getValues();
var curRow = start;
var cntKlaus = 0;
for( x in data )
{
var value = daten[x];
//ui.alert(value);
if(value.indexOf("Mueller, Klaus")> -1){
cntKlaus = cntKlaus + 1;
}
}
ui.alert(cntKlaus);
}
The result message is "0" but should be "5".
Issues:
You are very close to the solution, except for these two issues:
daten[x] should be replaced by data[x].
ui.alert(cntKlaus) should be replaced by SpreadsheetApp.getUi().alert(cntKlaus).
Solution (optimized by me) - Recommended:
function countKlaus() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getSheetByName("Tabellenblatt1");
const cntKlaus = sheet
.getRange('A1:A' + sheet.getLastRow())
.getValues()
.flat()
.filter(r=>r.includes("Mueller, Klaus"))
.length;
SpreadsheetApp.getUi().alert(cntKlaus);
}
You can leave out this term + sheet.getLastRow() since we are filtering on a non-blank value. But I think it will be faster to have less data to use filter on in the first place.
References:
flat : convert the 2D array to 1D array.
filter : filter only on "Mueller, Klaus".
Array.prototype.length: get the length of the filtered data
which is the desired result.
includes: check if Mueller, Klaus is included in the text.
Bonus info
Just for your information, my solution can be rewritten in one line of code if that's important to you:
SpreadsheetApp.getUi().alert(SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Tabellenblatt1").getRange('A1:A').getValues().flat().filter(r=>r.includes("Mueller, Klaus")).length);

How to Dynamic Paste values after Last Row [Google Script]

I am building a script to copy a row in another spreadsheet. The idea is to have one sheet with the inputs that are going to be stored in a second spreadsheet. However, I am facing some real struggle in building the dynamic paste range. This is the point I was able to reach with my present knowledge:
function Export() {
var ss = SpreadsheetApp.getActiveSpreadsheet()
var database = SpreadsheetApp.openById("xxx");
var source = ss.getSheetByName('yyy');
var dataToCopy = source.getRange('bb').getValues();
var copyToSheet = database.getSheetByName("zzz");
var copyData = copyToSheet.getRange('bb').setValues(dataToCopy)
var Clean = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('yyy').getRange('bb');
Clean.clear({contentsOnly:true});
}
This script copy a range into a fixed range in a second spreadsheet, and it clears the values present in the source. My question is: How can I create a range that makes the script paste the data in the first blank row in the second spreadsheet?
I tried some combination of appendRow, getLastRow, insertRowAfter, but I was not able to get it done.
Thank you for your time!
This is what I was able to achieve with the help of a friend:
function Export2() {
var ss = SpreadsheetApp.getActiveSpreadsheet()
var database = SpreadsheetApp.openById("1UfKqXCMNIbjh3ge7s26SNkXyGez-bY3fvl6_3-RQKos");
var source = ss.getSheetByName('Sheet26');
var dataToCopy = source.getRange('A1:E1');
var copyToSheet = database.getSheetByName("TOT");
var lastRow = copyToSheet.getLastRow();
for (var i = 1; i<6 ;i++){
var Paste = copyToSheet.getRange(lastRow + 1,i).setValue(dataToCopy.getCell(1, i).getValue());
}
var Clean = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet26').getRange('A1:E1');
Clean.clear({contentsOnly:true});
}
Below is a script that will do the thing you want to do dynamically without the use of named ranges. It assumes that all the data on the source sheet should be copied over to the destination sheet. Let me know if you need any additional explanation beyond what is provided in the comments.
function Export() {
var ss = SpreadsheetApp.getActiveSpreadsheet()
var database = SpreadsheetApp.openById("xxx");
var source = ss.getSheetByName('yyy');
var dataToCopyRng = source.getDataRange(); //Gets range object of all data on source sheet
var dataToCopy = dataToCopyRng.getValues(); //Gets the values of the source range in a 2 dimensional array
var copyToSheet = database.getSheetByName("zzz");
var copyData = copyToSheet.getRange(copyToSheet.getLastRow()+1,1,dataToCopy.length,dataToCopy[0].length).setValues(dataToCopy)
//Explination of the above command is as follows getRange(row, column, numRows, numColumns)
//--row is copyToSheet.getLastRow()+1 -- finds last row with content and adds one
//--Column is 1 for Column A
//--numRows is the length of the array (how many rows are in the array of values)
//--numcolumns is the length of the first element of the 2 dimensional array (arrays start at zero). The length of the first element is how many columns are in the array
//--combine the above with .getRange and you get the range object that is an exact match to the array of source data rows and columns
//--then you simply set the values of this range with the source data
dataToCopyRng.clear({contentsOnly:true});
}
You can learn more about 2 dimensional arrays here.
Cheers!

Paste values from one Google Sheet to another and remove duplicates based on ID column

I have a similar situation to the one described on this question: two worksheets, with input data coming into the Feed sheet using the importxml function and a Data sheet where new rows get copied thanks to a script set to run daily.
However, the current script is creating daily duplicates. As such, I would like to adapt the answer provided on the question above so that the script checks the IDs on column F and only copies the rows with new IDs.
How should I update the section below that creates a hash to one that looks for the IDs on column F instead? Also my rows are consistent, so is it correct to assume I can just remove the relevant code lines towards the end?
The sample Google Sheet is available here.
function appendUniqueRows() {
var ss = SpreadsheetApp.getActive();
var sourceSheet = ss.getSheetByName('Get Data');
var destSheet = ss.getSheetByName('Final Data');
var sourceData = sourceSheet.getDataRange().getValues();
var destData = destSheet.getDataRange().getValues();
// Check whether destination sheet is empty
if (destData.length === 1 && "" === destData[0].join('')) {
// Empty, so ignore the phantom row
destData = [];
}
// Generate hash for comparisons
var destHash = {};
destData.forEach(function(row) {
destHash[row.join('')] = true; // could be anything
});
// Concatentate source rows to dest rows if they satisfy a uniqueness filter
var mergedData = destData.concat(sourceData.filter(function (row) {
var hashedRow = row.join('');
if (!destHash.hasOwnProperty(hashedRow)) {
// This row is unique
destHash[hashedRow] = true; // Add to hash for future comparisons
return true; // filter -> true
}
return false; // not unique, filter -> false
}));
// Check whether two data sets were the same width
var sourceWidth = (sourceData.length > 0) ? sourceData[0].length : 0;
var destWidth = (destData.length > 0) ? destData[0].length : 0;
if (sourceWidth !== destWidth) {
// Pad out all columns for the new row
var mergedWidth = Math.max(sourceWidth,destWidth);
for (var row=0; row<mergedData.length; row++) {
for (var col=mergedData[row].length; col<mergedWidth; col++)
mergedData[row].push('');
}
}
// Write merged data to destination sheet
destSheet.getRange(1, 1, mergedData.length, mergedData[0].length)
.setValues(mergedData);
}
I'm a novice in this world of Google Apps scripts, so do please let me know if I'm missing any crucial information. Thanks in advance for the help.
You want to copy the values from "Feed" sheet to "Data" sheet.
When the values are copied, you want to copy only new values which are not included in "Data" sheet.
You want to choose the new values using the values of column "F".
If my understanding for your question is correct, how about this modification? In this modification, I modified the script in your shared spreadsheet.
Modification points:
In your script, all values of "Feed" sheet are copied to "Data" sheet. So in order to choose only new values, I used the following flow.
Retrieve the values from column "F". This is used for choosing the new values.
Retrieve the new values using the values from column "F".
Put the new values to "Data" sheet.
The script which reflected above flow is as follows.
Modified script:
From:
This is your script in the shared spreadsheet. Please modify this script to below one.
function Copy() {
var sss = SpreadsheetApp.openById('#####'); // this is your Spreadsheet key
var ss = sss.getSheetByName('Feed'); // this is the name of your source Sheet tab
var range = ss.getRange('A3:H52'); //assign the range you want to copy
var data = range.getValues();
var tss = SpreadsheetApp.openById('#####'); //replace with destination ID
var ts = tss.getSheetByName('Data'); //replace with destination Sheet tab name
ts.getRange(ts.getLastRow()+1, 1,50,8).setValues(data);// 49 value refers to number of rows, 8 to columns
}
To:
function Copy() {
var sss = SpreadsheetApp.openById('#####'); // this is your Spreadsheet key
var ss = sss.getSheetByName('Feed'); // this is the name of your source Sheet tab
var range = ss.getRange('A3:H52'); //assign the range you want to copy
var data = range.getValues();
var tss = SpreadsheetApp.openById('#####'); //replace with destination ID
var ts = tss.getSheetByName('Data'); //replace with destination Sheet tab name
// Below script was added.
var values = ts.getRange("F3:F").getValues().filter(String);
var copiedValues = data.filter(function(e) {return !values.some(function(f){return f[0] == e[5]}) && e.filter(String).length > 0});
ts.getRange(ts.getLastRow() + 1, 1, copiedValues.length, copiedValues[0].length).setValues(copiedValues);
}

How to set column to plain text format for every sheet?

I've read through this post >> set format as plain text
I'm creating a new thread because that thread is a few years old already and I didn't want to revive something from a few years ago.
the code:
function A1format() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var mainsheet = ss.getSheetByName("Sheet1");
var G = mainsheet.getRange("C15:BH3000").getGridId();
var illa = mainsheet.getRange("A13");
Logger.log(G);
illa.copyFormatToRange(G, 16, 3,200, 30);
}
This code is supposed to set plain text format for the sheet named Sheet1
I've tried var mainsheet = ss.getSheetByName("Sheet1, Sheet2, Sheet3"); but this doesn't seem to work, I just get an error message.
This is the current code I have, this code works but is both inefficient and a real pain to maintain if something changes:
function setPlainTextDefault() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet1 = ss.getSheets()[0];
var sheet2 = ss.getSheets()[2];
var sheet3 = ss.getSheets()[4];
var sheet4 = ss.getSheets()[7];
var sheetColumn1 = sheet1.getRange("A1:A");
var sheetColumn2 = sheet2.getRange("A1:A");
var sheetColumn3 = sheet3.getRange("A1:A");
var sheetColumn4 = sheet4.getRange("A1:A");
sheetColumn1.setNumberFormat("#");
sheetColumn2.setNumberFormat("#");
sheetColumn3.setNumberFormat("#");
sheetColumn4.setNumberFormat("#");
}
Here I am changing each column A in every sheet to plain text by using the index number of the sheet, so I have to manually count the number for every sheet, this is a nightmare as I have a very large number of sheets, it will take too much time for me to manually count the sheets and then add it to my current code. I know there is a better more efficient way of doing this, but I don't know how due to my lack of knowledge in google apps scripting.
How do you do this for every sheet in the document regardless of how many sheets are present? I want to go through every sheet, from sheet1 till x number of sheets and then change every column A to plain text.
var mainsheet = ss.getSheetByName("Sheet1, Sheet2, Sheet3") throws an error because there isn't a sheet named "Sheet1, Sheet2, Sheet3". "There isn't a way to reduce the calls to the Spreadsheet Service on your code, but you could make that it "looks better" by using some JavaScript features like loops and array handling.
Example (untested)
On the following code snippet, arrays, a for and indexOf are used to reduce a bit the number of lines of the original code.
function setPlainTextDefault() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheets = ss.getSheets();
var sheetsToProcess = [0,2,4,7]; // Array holding the sheets indexes to process
for(var i = 0; i < sheets.length; i++){
/* If i is not in the sheetsToProccess indexOf returns -1 which is parsed as false
otherwise the result is parsed as true */
if(sheetsToProcess.indexOf(i)){
var sheet = ss.getSheets()[i];
var sheetColumn = sheet.getRange("A1:A");
sheetColumn.setNumberFormat("#");
}
}
}
To make the above work for every sheet, comment out or remove if(sheetsToProcess.indexOf(i)){ and the corresponding }.
It's worth to note that if you are looking help to find the "best" way to improve your code, you could try Code Review.
This was the code I needed:
function setPlainText() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheets = ss.getSheets();
for(var i = 0; i < sheets.length; i++) {
//Logger.log(sheets[i].getName());
var setPlainText = ss.getSheets()[i];
var sheetColumnA = setPlainText.getRange("A1:A");
sheetColumnA.setNumberFormat("#");
var sheetColumnB = setPlainText.getRange("B1:B");
sheetColumnB.setNumberFormat("#");
}
}
It will:
Iterate through a document with x number of sheets however big or small
Then for each iteration, set plain text format for every column A and B for every sheet in the document

Use Google Apps Script to loop through the whole column

I am trying to loop through the whole row in my google sheet and copy some of the data from one sheet to another. The list will get longer over time.
More specifically: If input in column B equals "blue", than copy the values from column A and C into another sheet.
Do this for all columns till the end of the column.
Link to my spreadsheet: https://docs.google.com/spreadsheets/d/1xnLygpuJnpDfnF6LdR41gN74gWy8mxhVnQJ7i3hv1NA/edit?usp=sharing
The loop stops when the colour does not equal blue. Why?
As you can see I used a for loop. Is that even the way to go?
Can I do anything about the speed of the code execution?
Any comments, hints or help are highly appreciated.
Regards!
You had the input sheet named "List" and I named the output sheet "Output". And here's the code.
function condCopy()
{
var s = SpreadsheetApp.getActiveSpreadsheet();
var sht = s.getSheetByName('List')
var drng = sht.getDataRange();
var rng = sht.getRange(2,1, drng.getLastRow()-1,drng.getLastColumn());
var rngA = rng.getValues();//Array of input values
var rngB = [];//Array where values that past the condition will go
var b = 0;//Output iterator
for(var i = 0; i < rngA.length; i++)
{
if(rngA[i][1] == 'blue')
{
rngB[b]=[];//Initial new array
rngB[b].push(rngA[i][0],rngA[i][2]);
b++;
}
}
var shtout = s.getSheetByName('Output');
var outrng = shtout.getRange(2,1,rngB.length,2);//Make the output range the same size as the output array
outrng.setValues(rngB);
}
You have 2 options. The first is to use the standard query() function from Google Sheets to get the values. The downside here is that it is only a reference of the values. So you cannot reorder them, etc. To use this, place this in cell A1 and it will pull the Headers and retrieve the values from column A and C:
=QUERY(A:C, "select A, C where B = 'blue'", 1)
For a Google Apps Script answer:
This will loop through your List sheet and for every row where column B is blue it will save the values in column A and C to column A and B of the new sheet:
function doIt(){
var activeSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet4");
var lastRow = activeSheet.getLastRow();
var lastCol = activeSheet.getLastColumn();
var targetValues = [];
var sourceSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("List");
var lastSourceRow = sourceSheet.getLastRow();
var lastSourceCol = sourceSheet.getLastColumn();
var sourceRange = sourceSheet.getRange(1, 1, lastSourceRow, lastSourceCol);
var sourceData = sourceRange.getValues();
var activeRow = 0;
//Loop through every retrieved row from the Source
for (row in sourceData) {
//IF Column B in this row has 'blue', then work on it.
if (sourceData[row][1] === 'blue') {
//Save it ta a temporary variable
var tempvalue = [sourceData[row][0], sourceData[row][2]];
//then push that into the variables which holds all the new values to be returned
targetValues.push(tempvalue);
}
}
//Save the new range to the appropriate sheet starting at the last empty row
activeSheet.getRange(lastRow + 1, 1 , targetValues.length, 2).setValues(targetValues);
}
Of course, you could pass the value to test to the function by replacing 2 lines. The first, defining the function:
function doIt(testingvar){
to pass a variable called testingvar, and the test line to replace the hard coded test with the passed variable:
if (sourceData[row][1] === testingvar) {

Categories

Resources