How do I make a positive value be converted automatically into negative without adding formula to a cell when someone pastes/or added a value to a cell.
In this example I want that 5 2 and 3 be a negative value.
In your situation, how about using the simple trigger of onEdit as follows?
Sample script:
Please copy and paste the following script to the script editor of Spreadsheet and save the script. When you use this script, please put a positive value to a cell. By this, the positive value is converted to the negative value.
function onEdit(e) {
const value = e.range.getValue();
if (!isNaN(value) && value > 0) {
e.range.setValue(-value);
}
}
Note:
If you want to limit the sheet and the range, please tell me.
If you want to convert all cell values in a sheet by a script, how about the following script? When this script is run, the positive values in a sheet are converted to negative values.
function myFunction() {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1"); // Please set your sheet name.
const range = sheet.getDataRange();
const values = range.getValues().map(r => r.map(c => !isNaN(c) && c > 0 ? -c : c));
range.setValues(values);
}
Reference:
Simple Triggers
Added 1:
From your following additional request,
Is it possible to apply it with certain range only. Like to cell C45:AK76 and C84:AK115?
In this case, how about the following sample script?
Sample script:
function onEdit(e) {
const ranges = ["C45:AK76", "C84:AK115"]; // These ranges are from your reply.
const sheetName = "Sheet1"; // Please set your sheet name.
const { range } = e;
const sheet = range.getSheet();
const value = range.getValue();
const check = sheet.getRangeList(ranges).getRanges().some(r => {
const startRow = r.getRow();
const endRow = startRow + r.getNumRows() - 1;
const startCol = r.getColumn();
const endCol = startCol + r.getNumColumns() - 1;
return range.rowStart >= startRow && range.rowEnd <= endRow && range.columnStart >= startCol && range.columnEnd <= endCol;
});
if (sheet.getSheetName() != sheetName || !check || isNaN(value) || value < 0) return;
range.setValue(-value);
}
In this sample, the script is run when the cells "C45:AK76", "C84:AK115" are edited.
Added 2:
From your following additional request,
Im wondering if I can apply this to two other sheets with the same cell range?
In this case, how about the following sample script?
Sample script:
function onEdit(e) {
const ranges = ["C45:AK76", "C84:AK115"]; // These ranges are from your reply.
const sheetNames = ["Sheet1", "Sheet2",,,]; // Please set your sheet names.
const { range } = e;
const sheet = range.getSheet();
const value = range.getValue();
const check = sheet.getRangeList(ranges).getRanges().some(r => {
const startRow = r.getRow();
const endRow = startRow + r.getNumRows() - 1;
const startCol = r.getColumn();
const endCol = startCol + r.getNumColumns() - 1;
return range.rowStart >= startRow && range.rowEnd <= endRow && range.columnStart >= startCol && range.columnEnd <= endCol;
});
if (!sheetNames.includes(sheet.getSheetName()) || !check || isNaN(value) || value < 0) return;
range.setValue(-value);
}
Added 3:
From your following additional request,
If I paste for example these 3 different values into the cell., example the number 4 5 and 6. Instead of just having -4,-5, and -6. The result I'm getting is -4,-4 and -4.
In this case, how about the following sample script?
Sample script:
function onEdit(e) {
const ranges = ["C45:AK76", "C84:AK115"]; // These ranges are from your reply.
const sheetNames = ["Sheet1", "Sheet2",,,]; // Please set your sheet names.
const { range } = e;
const sheet = range.getSheet();
const values = range.getValues();
const check = sheet.getRangeList(ranges).getRanges().some(r => {
const startRow = r.getRow();
const endRow = startRow + r.getNumRows() - 1;
const startCol = r.getColumn();
const endCol = startCol + r.getNumColumns() - 1;
return range.rowStart >= startRow && range.rowEnd <= endRow && range.columnStart >= startCol && range.columnEnd <= endCol;
});
if (!sheetNames.includes(sheet.getSheetName()) || !check) return;
range.setValues(values.map(r => r.map(c => (!isNaN(c) && c > 0) ? -c : c)));
}
Related
I have this code which works for cells A2-A4:
//The function onEdit ensures that checkboxes deleted (by mistake) in the sheet are immediately re-created.
function onEdit(e) {
var spreadsheet = SpreadsheetApp.getActive();
if(spreadsheet.getSheetName()=='MySheet') { //to avoid executing for another sheet
var checkboxCells = [
'A2','A3','A4'];
var range = e.range;
var value = range.getValue();
var a1Notation = range.getA1Notation();
if (checkboxCells.indexOf(a1Notation) != -1 && value != 'TRUE' && value != 'FALSE') {
range.insertCheckboxes();
}
}
};
However I need to work with a 1D range (since there are many cells) and hence I am trying to use this:
//The function onEdit ensures that checkboxes deleted (by mistake) in the sheet are immediately re-created.
function onEdit(e) {
var spreadsheet = SpreadsheetApp.getActive();
if(spreadsheet.getSheetName()=='MySheet') { //to avoid executing for another sheet
var checkboxCells = spreadsheet.getSheetByName('MySheet')
.getRange('MyRange').getA1Notation();
var range = e.range;
var value = range.getValue();
var a1Notation = range.getA1Notation();
if (checkboxCells.indexOf(a1Notation) != -1 && value != 'TRUE' && value != 'FALSE') {
range.insertCheckboxes();
}
}
};
I think this second version should work, but it does not.
Why does it not work?
Modification points:
In order to check whether the edited cell is included in the specific range you expect, the following sample script can be used. (var { range } = e;)
var checkboxCells = sheet.getRange('MyRange');
var startRow = checkboxCells.getRow();
var endRow = startRow + checkboxCells.getNumRows() - 1;
var startCol = checkboxCells.getColumn();
var endCol = startCol + checkboxCells.getNumColumns() - 1;
var check = range.rowStart >= startRow && range.rowEnd <= endRow && range.columnStart >= startCol && range.columnEnd <= endCol;
In order to check whether the edited cell is the checkbox, isChecked() can be used. In this case, when the cell is the checkbox, true or false are returned. When the cell is not the checkbox, null is returned. I thought that this might be able to be used.
When these points are reflected in your script, how about the following modification?
Modified script:
Please set your sheet name and your range.
function onEdit(e) {
var { range } = e;
var sheet = range.getSheet();
if (sheet.getSheetName() == 'MySheet') {
var checkboxCells = sheet.getRange('MyRange');
var startRow = checkboxCells.getRow();
var endRow = startRow + checkboxCells.getNumRows() - 1;
var startCol = checkboxCells.getColumn();
var endCol = startCol + checkboxCells.getNumColumns() - 1;
var check = range.rowStart >= startRow && range.rowEnd <= endRow && range.columnStart >= startCol && range.columnEnd <= endCol;
if (check && range.isChecked() === null) {
range.insertCheckboxes();
}
}
}
In this modified script, when a cell is edited, when the edited cell is included in MyRange, it checks whether the edited cell is a checkbox when the edited cell is not a checkbox, the checkboxes are inserted into the edited cell.
Reference:
isChecked()
The goal is to create a script that automatically adds strikethrough to text across a row when the last cell in a row's value is changed to "FOUND!"
This is what I have so far:
function strikethroughRow(row) {
const ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Records");
const firstCell = "B" + row;
const secondCell = "A" + row;
firstCell.setFontLine("line-through");
secondCell.setFontLine("line-through");
}
function onEdit(e) {
const column = e.getColumn();
const value = e.getValue();
if(column === 3.0 && value === "FOUND!"){
const row = e.getRow();
strikethroughRow(row);
}
}
From your question and your showing script, I believe your goal is as follows.
When a value of "FOUND!" is put into column "C", you want to set the strikethrough to columns "A" and "B".
Modification points:
In your script, e.getColumn(), e.getValue() and e.getRow() are not correct. In this case, it is required to use e.range.
firstCell and secondCell are string value. By this, an error occurs.
I thought that in this case, the script might be able to be simpler. So, how about the following modification?
Modified script:
function onEdit(e) {
const sheetName = "Sheet1"; // Please set your sheet name.
const { range } = e;
const sheet = range.getSheet();
const value = range.getValue();
if (sheet.getSheetName() != sheetName || range.columnStart != 3 || value != "FOUND!") return;
sheet.getRange(range.rowStart, 1, 1, sheet.getLastColumn() - 1).setFontLine("line-through");
}
Note:
If the last column is not column "C", how about the following modified script?
function onEdit(e) {
const sheetName = "Sheet1"; // Please set your sheet name.
const { range } = e;
const sheet = range.getSheet();
const value = range.getValue();
const lastColumn = sheet.getLastColumn();
if (sheet.getSheetName() != sheetName || range.columnStart != lastColumn || value != "FOUND!") return;
sheet.getRange(range.rowStart, 1, 1, sheet.getLastColumn() - 1).setFontLine("line-through");
}
Reference:
Simple Triggers
I am trying to get Google Sheets show a popup message whenever a cell changes from FALSE to TRUE. I have found a few ways of doing it using onEdit functions. The problem here is that the Cell in question does not really get edited, but rather changes value due to another set of cells meeting certain criteria. That means onEdit functions won't run when the value changes.
I tried this script, but it does nothing:
//popup message
function alertMessageYesNoButton() {
var result = SpreadsheetApp.getUi().alert("Are you sure you want to send an alert about?", SpreadsheetApp.getUi().ButtonSet.YES_NO);
SpreadsheetApp.getActive().toast(result);
}
function sendMessageYesNo(e){
var sheet = SpreadsheetApp.getActive().getSheetByName('Sheet1');
var cellValue = sheet.getRange(4, 17).getValue();
if (cellValue == 'TRUE'){
alertMessageYesNoButton();
}else{}
}
If someone could come up with something that works, I would really appreciate it.
From your following reply,
cell D17 changes from FALSE to TRUE when three other checkboxes are checked using this formula: {"";SUMPRODUCT(B16:B18)=COUNTA(B16:B18)}.
In this case, how about checking the checkboxes of "B16", "B17", "B18"? When this is reflected in your showing script, how about the following modification? I thought that when those checkboxes are manually checked, onEdit trigger can be used.
In this modification, when all checkboxes of "B16:B18" of "Sheet1" are checked, your script of alertMessageYesNoButton() is run. So, when you test this, please check all checkboxes of "B16:B18" of "Sheet1". By this, the script is run.
Pattern 1:
function onEdit(e) {
const sheetName = "Sheet1"; // This is from your script.
const checkBoxRange = "B16:B18"; // This is from your reply.
const { range } = e;
const sheet = range.getSheet();
const r = sheet.getRange(checkBoxRange);
const rowStart = r.getRow();
const rowEnd = rowStart + r.getNumRows() - 1;
const colStart = r.getColumn();
const condition1 = sheet.getSheetName() == sheetName;
const condition2 = range.rowStart >= rowStart && range.rowEnd <= rowEnd && range.columnStart == colStart;
const condition3 = r.getValues().every(([b]) => b === true);
if (!condition1 || !condition2 || !condition3) return;
alertMessageYesNoButton(); // This is your script.
}
Pattern 2:
function onEdit(e) {
const sheetName = "Sheet1"; // This is from your script.
const checkBoxRange = "B16:B18"; // This is from your reply.
const { range } = e;
const sheet = range.getSheet();
const r = sheet.getRange(checkBoxRange);
const rowStart = r.getRow();
const rowEnd = rowStart + r.getNumRows() - 1;
const colStart = r.getColumn();
const condition1 = sheet.getSheetName() == sheetName;
const condition2 = range.rowStart >= rowStart && range.rowEnd <= rowEnd && range.columnStart == colStart;
const condition3 = r.getValues().every(([b]) => b === true);
if (!condition1 || !condition2 || !condition3) return;
// This is your script.
var cellValue = sheet.getRange(4, 17).getValue();
if (cellValue == 'TRUE') {
alertMessageYesNoButton();
}
}
Reference:
Simple Triggers
Added:
From your following reply,
this works very well for the example I laid out, but I tried using several multiple instances of the same script within the same sheet and it always works only in one of them. So let´s say I have for instances of the same situation (B16:B18), (B13:B15), (D13:D15) and (D16:D18). Only when I check the boxes corresponding to the last function (from top to bottom), the message pops up.
From your question and your reply, I proposed an answer as the checkboxes of "B16:B18". But from your reply, when your checkboxes are "B16:B18", "B13:B15", "D13:D15", "D16:D18", how about the following sample script?
Sample script:
function onEdit(e) {
const sheetName = "Sheet1"; // This is from your script.
const checkBoxRanges = ["B16:B18", "B13:B15", "D13:D15", "D16:D18"]; // This is from your reply.
const { range } = e;
const sheet = range.getSheet();
const actSheetName = sheet.getSheetName();
const res = checkBoxRanges.find(checkBoxRange => {
const r = sheet.getRange(checkBoxRange);
const rowStart = r.getRow();
const rowEnd = rowStart + r.getNumRows() - 1;
const colStart = r.getColumn();
const condition1 = actSheetName == sheetName;
const condition2 = range.rowStart >= rowStart && range.rowEnd <= rowEnd && range.columnStart == colStart;
const condition3 = r.getValues().every(([b]) => b === true);
return condition1 && condition2 && condition3;
});
if (!res) return;
Browser.msgBox(`Checked checkboxes of ${res}`); // Here, you can see the checkbox group that was checked.
alertMessageYesNoButton(); // This is your script.
}
In this case, by giving const checkBoxRanges = ["B16:B18", "B13:B15", "D13:D15", "D16:D18"], the script detects the checked checkboxe group, and show the range as a sample.
How can I get a cell in a cell and move into another file?
I'm trying something along below lines, like a dizzy cockroach, but unsuccessfull so far:
function moveOrders () {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const orderSheet = ss.getSheetByName('Orders');
const activeSheetName = e.source.getActiveSheet().getName();
const thisRow = e.range.getRow();
const thisCol = e.range.getColumn();
const cellValue = e.range.getValue();
const destinationSheet = ss.getSheetByName('Sheet31');
if (activeSheetName == orderSheet.getName() && thisRow > 5 && thisCol < 13 && cellValue == 'Confirmed') {
const rowValues = orderSheet.getRange(thisRow, 1, 1, 13).getValues();
const image = SpreadsheetApp.newCellImage(rowValues[2]).setAltTextDescription('Clothing Piece').toBuilder().build();
destinationSheet.getRange(destinationSheet+1,1,rowValues.length, rowValues[0].length).setValues(rowValues)
}
This is how I get the value, the image being highlighted in green (3rd element of the array:
It throws the following error:
Parametes null don't correspond to method signatures for SpreadsheetApp.newCellImage
Here's a sample file for tests: https://docs.google.com/spreadsheets/d/1gxhXeNCMZ8MnJds1LM7vGQqibYgfXDYPkA6q6VNHcN0/edit?usp=sharing
I thought that in your situation, this thread might be useful. Ref And, if this is reflected in your script, how about the following modification?
And, from const activeSheetName = e.source.getActiveSheet().getName();, const thisRow = e.range.getRow(); and so on, I guessed that you might want to use your script as the OnEdit trigger.
Modified script:
function onEdit(e) {
const orderSheet = e.source.getSheetByName('Orders');
const activeSheetName = e.source.getActiveSheet().getName();
const thisRow = e.range.getRow();
const thisCol = e.range.getColumn();
const cellValue = e.range.getValue();
const destinationSheet = e.source.getSheetByName('Sheet31');
if (activeSheetName == orderSheet.getName() && thisRow > 5 && thisCol < 13 && cellValue == 'Confirmed') {
const rowValues = orderSheet.getRange(thisRow, 1, 1, 13);
rowValues.copyTo(destinationSheet.getRange(destinationSheet.getLastRow() + 1, 1), { contentsOnly: true });
}
}
In your showing script, destinationSheet+ of destinationSheet+1 is Sheet object. In this case, an error occurs. I guessed that you might use destinationSheet.getLastRow() + 1.
When this script is run, the row is copied from "Orders" to "Sheet31" using copyTo. By this, the row including both the image and the values can be copied.
References:
Related thread.
Moving contents of a cell with app script in Google sheets
copyTo(destination)
Added
About Does this work between files, as well?, when the above script is modified, it becomes as follows. In this case, please install the OnEdit trigger to installedOnEdit.
Sample script:
function installedOnEdit(e) {
const dstSpreadsheetId = "###"; // Please set the destination Spreadsheet ID.
const orderSheet = e.source.getSheetByName('Orders');
const activeSheetName = e.source.getActiveSheet().getName();
const thisRow = e.range.getRow();
const thisCol = e.range.getColumn();
const cellValue = e.range.getValue();
if (activeSheetName == orderSheet.getName() && thisRow > 5 && thisCol < 13 && cellValue == 'Confirmed') {
const dstSS = SpreadsheetApp.openById(dstSpreadsheetId);
const destinationSheet = dstSS.getSheetByName('Sheet31');
const temp = orderSheet.copyTo(dstSS);
const rowValues = temp.getRange(thisRow, 1, 1, 13);
rowValues.copyTo(destinationSheet.getRange(destinationSheet.getLastRow() + 1, 1), { contentsOnly: true });
dstSS.deleteSheet(temp);
}
}
You can just use moveTo(), there's no need for getValues and setValues this will cut and paste both format and value from this range and target range.
Try this code instead:
function onEdit(e) {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const orderSheet = ss.getSheetByName('Orders');
const activeSheetName = e.source.getActiveSheet().getName();
const thisRow = e.range.getRow();
const thisCol = e.range.getColumn();
const cellValue = e.range.getValue();
const destinationSheet = e.source.getSheetByName('Destination');
if (activeSheetName == orderSheet.getName() && thisRow > 5 && thisCol < 13 && cellValue == 'Confirmed') {
const rowValues = orderSheet.getRange(thisRow, 1, 1, 13);
rowValues.moveTo(destinationSheet.getRange(destinationSheet.getLastRow() + 1, 1));
}
}
Let me know if this works!
Reference: https://developers.google.com/apps-script/reference/spreadsheet/range#movetotarget
I want to copy the data from G11:G22 to the last column of the sheet starting from row 11 by using onEdit function. So, the script will be triggered once I select Transfer in cell F7. After running the script, the data should appear in range H11:H22 since column H is the last column in the sheet. How should I achieved that? This is what I have tried:
function onEdit(e){
var ss = SpreadsheetApp.getActiveSheet();
var lastrow = ss.getLastRow()
for( var k = 2; k <= lastrow ; k++) {
var m = 1;
while( sheet.getRange(k,m).isBlank() == false) {
m = m +1;
}
var lastcolumn = m
}
var destRange = ss.getRange(11,lastcolumn+1)
if (e.range.columnStart == 6 && e.range.rowStart == 7){
if (e.value == 'Transfer'){
var source = ss.getRange("G11:G22")
source.copyTo (destRange, {contentsOnly: true})
e.range.clearContent()
}
}
}
I think the problem comes from the For loop but I have no idea what is wrong. Hope to get some helps on this, thank you.
Based on your screenshot I suppose you can use getLastColumn()
function onEdit(e) {
var ss = SpreadsheetApp.getActiveSheet();
var destRange = ss.getRange(11, ss.getLastColumn() + 1)
if (e.range.columnStart == 6 && e.range.rowStart == 7) {
if (e.value == 'Transfer') {
var source = ss.getRange("G11:G22")
source.copyTo(destRange, { contentsOnly: true })
e.range.clearContent()
}
}
}
No need for loop to get the last column if there is no more filled cels on the right side of the sheet.