I am trying to write a script that, when a cell in a sheet changes "Негатив отсутствует", will replace the value of a cell in another sheet with the value "Нерелевант". Help please, what did I do wrong?
function ChangeTone(event) {
if (event.source.getActiveRange().getValue()=="Негатив отсутствует" && event.source.getActiveSheet()=="Разметка ТОП100 по суду"){
var sheet = SpreadsheetApp.getActiveSheet();
var currRow = sheet.getActiveCell().getRow();
var value = sheet.getRange(currRow, 1).getDisplayValue();
var pasteSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Тональность");
var data = pasteSheet.getDataRange().getValues();
// if(currRow > 2){
// sheet.deleteRow(currRow);
// }
for(var i = 1; i<data.length;i++){
if(data[i][1] == value){
pasteSheet.getRange((i), 2).clear({contentsOnly: true});
pasteSheet.getRange((i), 2).setValue('Нерелевант');
break;
}
};
// sheet.getActiveCell().setValue("");
}
}
Explanation / Issues:
Issue:
There is a clear issue with your code and in particular here:
event.source.getActiveSheet()=="Разметка ТОП100 по суду"
You are comparing a sheet object with a string and this will always return false. The correct way to do it would be:
event.source.getActiveSheet().getName()=="Разметка ТОП100 по суду"
but here I also tried to optimize your code because it is quite inefficient.
Optimization:
You don't take full advantage of the event object.
SpreadsheetApp.getActiveSpreadsheet() can be replaced by e.source.
You also define the same variables multiple times when you only need to do that once:
For event.source.getActiveSheet() and var sheet = SpreadsheetApp.getActiveSheet(); you can define a single variable to store the active sheet object and call it whenever you need it.
Last but not least. I am not quite sure about your logic regarding the for loop since you haven't mentioned it in your question.
But I see you use a for loop, an if statement and a break line to escape the for loop as soon as there is a match between the source value and the value in the paste sheet.
Instead of using a for loop, you can use findIndex to find the value that matches the criterion data[i][1] == value.
Also the full getDataRange() is not needed if you intend to use only one column, therefore I change that part too.
Solution:
function onEdit(e){
const ss = e.source;
const ar = e.range;
const activeSheet = ss.getActiveSheet();
const pasteSheet = ss.getSheetByName("Тональность");
if (ar.getValue()=="Негатив отсутствует" && activeSheet.getName()=="Разметка ТОП100 по суду"){
const value = activeSheet.getRange(ar.getRow(), 1).getValue();
const data = pasteSheet.getRange('B1:B'+pasteSheet.getLastRow()).getValues().flat();
const indx = data.findIndex((element) => element == value);
if (indx>-1){
const pasteRng = pasteSheet.getRange(indx+1,2);
pasteRng.clearContent();
pasteRng.setValue('Нерелевант');
}
}
}
Let me know if that worked for you, otherwise I would like to modify it so it does.
Thank you very much! The script works =)
I also made a working script before. But this is my first script, so it is much slower and not so ... concise. Also it was fired with a trigger, and yours works as a simple event.
My old version:
function ChangeTone(event) {
if (event.source.getActiveRange().getValue()=="Негатив отсутствует" && event.source.getActiveSheet().getName() == "Разметка ТОП100 СУД"){
var sheet = SpreadsheetApp.getActiveSheet();
var currRow = sheet.getActiveCell().getRow();
sheet.getRange("A"+currRow+":F"+currRow).setBackground('#ff5a5a');
var value = sheet.getRange(currRow, 1).getDisplayValue();
var pasteSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Тональность");
var data = pasteSheet.getDataRange().getValues();
for(var i = 1; i<data.length;i++){
if(data[i][0] == value){
pasteSheet.getRange((i+1), 2).setValue('Нерелевант');
sheet.getRange("C"+currRow+":F"+currRow).deleteCells(SpreadsheetApp.Dimension.ROWS);
}
};
sheet.getRange("A"+currRow+":B"+currRow).setBackground('#ffffff');
}
}
So took your code and added row deletion after replacing cell value. Final version:
function onEdit(e){
const ss = e.source;
const ar = e.range;
const arRow = ar.getRow();
const activeSheet = ss.getActiveSheet();
const pasteSheet = ss.getSheetByName("Тональность");
if (ar.getValue()=="Негатив отсутствует" && activeSheet.getName()=="Разметка ТОП100 СУД"){
const value = activeSheet.getRange(arRow, 1).getValue();
const data = pasteSheet.getRange('A1:A'+pasteSheet.getLastRow()).getValues().flat();
const indx = data.findIndex((element) => element == value);
if (indx>-1){
const pasteRng = pasteSheet.getRange(indx+1,2);
pasteRng.clearContent();
pasteRng.setValue('Нерелевант');
activeSheet.getRange("C"+arRow+":F"+arRow).deleteCells(SpreadsheetApp.Dimension.ROWS);
}
}
}
Thanks again for your help =)
Related
I am trying to compare two columns and find a match then return value from adjacent cell. I searched for answers on google but I could not find an answer that I could use. Most everything I saw did not use script but a sheets formula. the few I functions I did see didn't fit either.
function findDuplicate() {
var sheet1 = SpreadsheetApp.getSheetByName('Working');
var s1 = sheet1.getRange('A:A').getValues();
var sheet2 = SpreadsheetApp.getSheetByName('Match');
var s2 = sheet1.getRange('A:A').getValues();
var mv = s1 === s2;
var matchedValue = mv.getvalue();
return matchedValue.offset(0, 1);
}
I am unsure how to fully do this. I understand my code is incomplete but I started down the path the best I know how. And for ease of time, yes I need this to be done in Apps Script as it will be part of another bunch of code.
example sheet link below
https://docs.google.com/spreadsheets/d/1OKFoS17le-Y5SAOecoLE4EJxiKqKVjRLRHtMzwHNwxM/edit?usp=sharing
Find match and return next column
function findDuplicate() {
const ss = SpreadsheetApp.getActive();
const sh1 = ss.getSheetByName('Working');
const vs1 = sh1.getRange('H1:H' + sh1.getLastRow()).getValues();
const sh2 = ss.getSheetByName('Match');
const vs2 = sh1.getRange('A1:B' + sh2.getLastRow()).getValues();
let o = [];
vs1.forEach((r,i) => {
if(r[0] == vs2[i][0]) {
o.push(vs2[i][1]);
}
});
Logger.log(JSON.stringify(o));
return o;
}
I am a beginner with JavaScript and have been taking courses for a few months. I have a couple of scripts I have written so far that are up and running. I have an issue that I can’t get past which is very important to find a resolution for, so I’m hoping someone can help.
I have google survey’s that are being submitted and an on even form generator script that creates a form and produces a link in google sheets. The problem I’m having is that I need to be able to filter forms that belong to certain person on to a different spreadsheet using a script. I have written the script a few ways to try to achieve this but have been unsuccessful. The script works as intended but it removes the form generator links when it’s copied or moved.
Here is the filter script I had written:
function myFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var trn = ss.getSheetByName('Hull - NY');
var originalData = trn.getRange(2, 1, trn.getLastRow() - 1, 12).getValues();
var centerManager = 'Brooks, Robert';
if (trn.getColumn() == 3 && trn.getValue() == 'Brooks, Robert') {
var r = originalData.getRow();
var data = originalData.filter(function (item) {
return item[1] === centerManager;
});
var targetSheet = ss.insertSheet(centerManager);
targetSheet.getRange(2, 1, data.length, data[0].length).setValues(data).getLastRow();
}
}
I appreciate any help that could be provided!
You must read the formulas and then use the formula instead of the cell value, where a formula exist.
function myFunction() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const trn = ss.getSheetByName('Hull - NY');
const sourceRange = trn.getDataRange()
const originalValues = sourceRange.getValues();
const originalFormulas = sourceRange.getFormulas()
const centerManager = 'Brooks, Robert';
const MANAGER_COL = 2
const data = []
originalValues.forEach((row,i)=>{
if(row[MANAGER_COL-1] === centerManager){
originalFormulas[i].forEach((formula,i)=>{
if(formula[i]){
row[i] = formula
}
})
data.push(row)
}
});
const targetSheet = ss.insertSheet(centerManager);
if(data.length>0){
targetSheet.getRange(2, 1, data.length, data[0].length).setValues(data).getLastRow();
}
}
My spreadsheet has a column (A) with over 1000 rows of values like 10.99€, 25.99 € and so on. for optimizing purposes, I am looping through this column and removing the "EUR" mark and replacing "." with ",". While the code works, my problem is that it takes super long to execute and for thousands of products it sometimes time outs. I know I am probably not following the best practices, but this was the best solution I could come up with because of my limited JavaScript skills. Any help?
function myFunction() {
var ss = SpreadsheetApp.getActive();
var sheet = ss.getSheetByName('Table');
var lastRow = sheet.getRange(1,1).getDataRegion(SpreadsheetApp.Dimension.ROWS).getLastRow();
for (var i = 1; i < lastRow +1; i++) {
var price = sheet.getRange(i,1).getValue();
var removeCur = price.toString().replace(" EUR","").replace(".",",");
sheet.getRange(i,1).setValue(removeCur);
}
}
It's a classic question. Classic answer -- you need to replace cell.getValue() with range.getValues(). To get this way 2D-array. Process the array with a loop (or map, etc). And then set all values of the array at once back on sheet with range.setValues()
https://developers.google.com/apps-script/guides/support/best-practices?hl=en
For this case it could be something like this:
function main() {
var ss = SpreadsheetApp.getActive();
var sheet = ss.getSheetByName('Table');
var range = sheet.getDataRange();
var data = range.getValues(); // get a 2d array
// process the array (make changes in first column)
const changes = x => x.toString().replace(" EUR","").replace(".",",");
data = data.map(x => [changes(x[0])].concat(x.slice(1,)));
range.setValues(data); // set the 2d array back to the sheet
}
Just in case here is the same code with loop for:
function main() {
var ss = SpreadsheetApp.getActive();
var sheet = ss.getSheetByName('Table');
var range = sheet.getDataRange();
var data = range.getValues();
for (var i=0; i<data.length; i++) {
data[i][0] = data[i][0].toString().replace(" EUR","").replace(".",",")
}
range.setValues(data);
}
Probably the loop for looks cleaner in this case than map.
And if you sure that all changes will be in column A you can make the script even faster if you change third line in the function this way:
var range = sheet.getRange("A1:A" + sheet.getLastRow());
It will narrow the range to one column.
Well, there's something you can do to improve your code, can't guarantee it will help you to make it faster, but we'll see.
Here's the updated version
function myFunction() {
var ss = SpreadsheetApp.getActive();
var sheet = ss.getSheetByName('Table');
var lastRow = sheet.getRange(1,1).getDataRegion(SpreadsheetApp.Dimension.ROWS).getLastRow() + 1;
var price;
var removeCur;
for (var i = 1; i < lastRow; i++) {
price = sheet.getRange(i,1).getValue();
removeCur = price.toString().replace(" EUR","").replace(".",",");
sheet.getRange(i,1).setValue(removeCur);
}
}
What I did:
Line 5: I removed the +1 in the loop and added on lastRow directly. If you have 1000 rows, you'll save 1000 assignments
Line 6-7: removed declarations in the loop. If you have 1000 rows, you'll save 2000 re-declarations (not sure if it does, but it's best practise anyway)
You could use regex for the replace, so you do it only once, but I think it's slower, so I kept the 2 replaces there
column B gets data updated
column C onwards is used to store consolidated data
im trying to copy a range of data to right next column
and update column C with data from column B
function doUpBCL() {
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('BLC');
var data_new = ss.getRange('B1').getDisplayValue();
var data_old = ss.getRange('C1').getDisplayValue();
if( data_new !== data_old ) // check if data changed
{
var lr = ss.getLastRow();
var lc = ss.getLastColumn();
var data = ss.getRange(1,3,lr,lc - 3).getValues();
ss.getRange(1,4,lr,lc - 4).setValues(data);
var data_ = ss.getRange(lr,2,1,1).getValues();
ss.getRange(lr,3,1,1).setValues(data_);
}
};
macro (created by Google Sheets) as EXAMPLE to ilustrate what i want is (i need help with above function, not this)
function upblc_macro() {
var spreadsheet = SpreadsheetApp.getActive();
spreadsheet.setActiveSheet(spreadsheet.getSheetByName('BLC'), true);
spreadsheet.getRange('D:D').activate();
spreadsheet.getRange('C:Z').copyTo(spreadsheet.getActiveRange(), SpreadsheetApp.CopyPasteType.PASTE_NORMAL, false);
spreadsheet.getRange('C:C').activate();
spreadsheet.getRange('B:B').copyTo(spreadsheet.getActiveRange(), SpreadsheetApp.CopyPasteType.PASTE_VALUES, false);
};
using last row cos im going to use similar funcions to other pages and im trying to funcion where i dont have to adjust everytime
simply add new column, move or append to last column wont work the way i need
help is much appreciated, thanks
with lots of tries and errors, with a few tweaks and minor changes, i managed to make it work as i wanted
function doUpBCL() {
const ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('BLC');
const data_new = ss.getRange('B1').getDisplayValue();
const data_old = ss.getRange('C1').getDisplayValue();
if( data_new !== data_old ) // check if data changed
{
var lr = ss.getLastRow();
var lc = ss.getLastColumn();
const data = ss.getRange(1,3,lr,lc-2).getValues();
ss.getRange(1,4,lr,lc-2).setValues(data);
const data_ = ss.getRange(1,2,lr,1).getValues();
ss.getRange(1,3,lr,1).setValues(data_);
}
else
{
}
};
not sure why of some stuff i changed, some other stuff i learned, but still learning
I have a code that dynamically creates new sheets based on the first-row value in the main sheet.
I would like to have the code to check the existence of the sheet name and overwrite the sheet or delete it first if it exists and then creates it afresh from the new data in main sheet.
I will appreciate your help in restructuring.
function newSheet() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var templateSheet = ss.getActiveSheet();
var sheet1 = ss.getSheetByName("main")
var getNames = [...new Set(sheet1.getRange("A2:A").getValues().filter(String).toString().split(","))];
for (var i = 0; i < getNames.length; i++) {
var copy = ss.getSheetByName(getNames[i]);
if (copy) { // This is where I am kind lost on how to structure it.
// if a copy exists delete or overwrite existing sheet here
} else {
var rowIndexes = sheet1.getRange("A:A").getValues()
.map((value, index) => [value[0], (index + 1)])
.filter(value => value[0] === getNames[i]);
var namedSheet = ss.insertSheet(getNames[i]);
rowIndexes.map(index => {
var rowValues = sheet1.getRange(index[1], 1, 1, sheet1.getLastColumn()).getValues();
namedSheet.appendRow(rowValues[0]);
});
ss.setActiveSheet(ss.getSheetByName(getNames[i]));
ss.moveActiveSheet(ss.getNumSheets());
}
}
}
I think there are multiple ways to achieve the solutions you are looking for
First:
Yes, you can replace it.
// This example assumes there is a sheet named "first"
var ss = SpreadsheetApp.getActiveSpreadsheet();
var first = ss.getSheetByName("first");
first.setName("not first anymore");
So in your case,
var copy = ss.getSheetByName(getNames[i]);
if (copy) { // This is where I am kind lost on how to structure it.
copy.setName("New name")
// if a copy exists delete or overwrite existing sheet here
}
Second:
Yes, you can delete the sheet as well.
// The code below deletes the specified sheet.
var ss = SpreadsheetApp.getActive();
var sheet = ss.getSheetByName('My Sheet');
ss.deleteSheet(sheet);
So in your case,
if (copy) { // This is where I am kind lost on how to structure it.
ss.deleteSheet(copy)
// if a copy exists delete or overwrite existing sheet here
}
Sorry if I have misunderstood your problem.