AppScript to strikethrough partial text on match - google-apps-script

I'm looking to get some help with Google Docs and Scripts. I have a workflow list that shows names of employees assigned to a task. There is also another field that indicates employees off of the day. I would like a script that can be run that would strikethrough the names of the individuals identified as off for the day. There could be multiple individuals off, so it would need to include a series of cells to reference. Results to look something like this.
[Requested outcome1
The problem I am running into is I cannot successfully find any code for even a starting point. I have seen pieces here and there, but nothing that is complete enough for me to even determine a starting point. I'm reasonably technical, but am not familiar with script writing. I have been unable to find a decent writeup on something like this so am requesting assistance if possible. Thank you!
Here is the code attempted where I am getting illegal argument on Line 27 currently. I will have it linked to a button. The individual in charge of updating the sheet daily will make all the daily changes, then once done click to button to clear any strikethrough and initiate based on new names input, if there are any.
Sample sheet link here.
https://docs.google.com/spreadsheets/d/1chSTd7Zy1qqu32qu4spSJJanwTI1SnH6rJtoMxb7iEc/edit?usp=sharing
function myFunction()
{
var spreadsheet = SpreadsheetApp.getActive();
spreadsheet.getRange('B:B').activate();
spreadsheet.getActiveRangeList().setFontLine(null);
const sheetName = "Sheet1"; // Please set the sheet name.
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
const range = sheet.getRange('B2:B')
const textsForStrikethrough = sheet.getRange("A15:A20").getValues().flat(); // Added
const modify = range.getValues().reduce((ar, e, r) => {
e.forEach((f, c) => {
textsForStrikethrough.forEach(g => {
const idx = f.indexOf(g);
if (idx > -1) ar.push({start: idx, end: idx + g.length, row: r, col: c});
});
});
return ar;
}, []);
const textStyle = SpreadsheetApp.newTextStyle().setStrikethrough(true).build();
const richTextValues = range.getRichTextValues();
modify.forEach(({start, end, row, col}) => richTextValues[row][col] = richTextValues[row][col].copy().setTextStyle(start, end, textStyle).build());
range.setRichTextValues(richTextValues);
}

I believe your goal as follows.
You want to reflect the strikethrough to the partial text in a cell using Google Apps Script as follows. (The sample image is from your question.)
In this case, I would like to propose to use RichTextValueBuilder. The sample script is as follows.
Sample script:
function myFunction() {
const textsForStrikethrough = ["John"]; // Please set the texts you want to reflect the strikethrough.
const sheetName = "Sheet1"; // Please set the sheet name.
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
const range = sheet.getDataRange();
const modify = range.getValues().reduce((ar, e, r) => {
e.forEach((f, c) => {
textsForStrikethrough.forEach(g => {
const idx = f.indexOf(g);
if (idx > -1) ar.push({start: idx, end: idx + g.length, row: r, col: c});
});
});
return ar;
}, []);
const textStyle = SpreadsheetApp.newTextStyle().setStrikethrough(true).build();
const richTextValues = range.getRichTextValues();
modify.forEach(({start, end, row, col}) => richTextValues[row][col] = richTextValues[row][col].copy().setTextStyle(start, end, textStyle).build());
range.setRichTextValues(richTextValues);
}
Result:
When above script is run, the following results are obtained.
Input situation:
This is the sample input situation before the script is run.
Output situation 1:
In this case, const textsForStrikethrough = ["John"]; is used for the input situation.
Output situation 2:
In this case, const textsForStrikethrough = ["John", "Amy"]; is used for the input situation.
Note:
In this sample script, all values are retrieved from the sheet and search the texts and reflect the strikethrough. So when you want to use this script to the specific range, please modify const range = sheet.getDataRange(); for your situation.
For example, from your sample image, when you want to use this script to the column "B", please modify it to const range = sheet.getRange("B1:B" + sheet.getLastRow());.
References:
Class RichTextValue
Class RichTextValueBuilder
Added:
About your following 2nd question,
This is perfect! Only other request I have, is how would we modify it to where it is referencing a series of other cells to lookup the names? The list of names changes daily, so looking to have all inputs be able to update by a simple change of the names on the sheet rather than modifying the code. So say the names could be input in cell A10:A15. Pulling that list of names and updating the "textsForStrikethrough" logic.
in this case, I think that at first, how about retrieving the values of cells "A10:A15", and use them as textsForStrikethrough?
Sample script 2:
function myFunction2() {
// const textsForStrikethrough = ["John"]; // Please set the texts you want to reflect the strikethrough.
const sheetName = "Sheet1"; // Please set the sheet name.
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
const range = sheet.getDataRange();
const textsForStrikethrough = sheet.getRange("A10:A15").getValues().flat(); // Added
const modify = range.getValues().reduce((ar, e, r) => {
e.forEach((f, c) => {
textsForStrikethrough.forEach(g => {
const idx = f.indexOf(g);
if (idx > -1) ar.push({start: idx, end: idx + g.length, row: r, col: c});
});
});
return ar;
}, []);
const textStyle = SpreadsheetApp.newTextStyle().setStrikethrough(true).build();
const richTextValues = range.getRichTextValues();
modify.forEach(({start, end, row, col}) => richTextValues[row][col] = richTextValues[row][col].copy().setTextStyle(start, end, textStyle).build());
range.setRichTextValues(richTextValues);
}
In this script, the values of cells "A10:A15" in the sheet of sheetName are used as textsForStrikethrough.
Note:
Unfortunately, I cannot understand about your actual situation. So when above script cannot be used for your actual situation, can you provide your sample Spreadsheet for replicating the issue? By this, I would like to confirm it.

Related

App Script in Google Sheets - problem with data auto complete

I have two sheets. In one, the data of the manufactured devices is already entered. In the second, new warranty claims are added daily. To shorten the working time, I automatically copy the production data from the sheet Production to sheet Complaints. Currently I am solving this with VLOOKUP or INDEX function. But it works very slow when number of rows >20k.
Is it possible to write a script (using AppScripts) where after entering the ID number in the complaint sheet and selecting this cell, script will independently fill in the appropriate columns with data from 'Production'? (the order of the columns is different in both sheet)
Link to my sheet with example
I've tried all option with built-in function, I'm expecting to know and understand a possible solution using AppScript
In your situation, how about the following sample script?
Sample script:
Please copy and paste the following script to the script editor of Spreadsheet and save the script. And please confirm the sheet names again.
function myFunction() {
// Retrieve sheets.
const ss = SpreadsheetApp.getActiveSpreadsheet();
const productionSheet = ss.getSheetByName("Production");
const complaintsSheet = ss.getSheetByName("Complaints");
// Retrieve values from sheets.
const [h1, ...v1] = productionSheet.getDataRange().getValues();
const range = complaintsSheet.getRange("A2:I" + complaintsSheet.getLastRow());
const [h2, ...v2] = range.getValues();
// Create an array for putting to Complaints sheet by converting columns.
const hconv = h2.map(h => h1.indexOf(h));
const obj = v1.reduce((o, r) => (o[r[0]] = hconv.map(i => i > -1 ? r[i] : null), o), {});
const len = Object.values(obj)[0].length;
const res = v2.map(([a]) => obj[a] || Array(len).fill(null));
// Put the created array to Complaints sheet.
range.offset(1, 0, res.length, res[0].length).setValues(res);
}
When this script is run, the values are retrieved from both sheets. And, an array is created for putting to "Complaints" sheet by checking the ID of column "A" and converting the columns. And, the array is put to "Complaints" sheet.
Note:
In this sample, I tested it using your sample Spreadsheet. If you change the spreadsheet, this script might not be able to be used. Please be careful about this.
I thought that in this case, the custom function might be able to be used. But, from your question, if the data is large, the custom function cannot be used. So, I proposed the above script.
In this sample, in order to convert the columns, the header titles are used. So, if you change the header titles, please be careful about this.
As another approach, if you want to put the value to the row when you put a value to the column "A" of "Complaints", how about the following script? In this script, the script is automatically run by a simple trigger of OnEdit. So, in this case, when you put a value to the column "A" of "Complaints" sheet, this script is automatically run, and the value is put into the edited row. Please select one of 2 scripts for your actual situation. As an important point, when you use this, please don't directly run this function because of no event object. Please be careful about this.
function onEdit(e) {
const { range, source } = e;
const complaintsSheet = range.getSheet();
if (complaintsSheet.getSheetName() != "Complaints" || range.columnStart != 1 || range.rowStart <= 2) return;
const value = range.getValue();
const productionSheet = source.getSheetByName("Production");
const [h1, ...v1] = productionSheet.getDataRange().getValues();
const [h2] = complaintsSheet.getRange("A2:I2").getValues();
const hconv = h2.map(h => h1.indexOf(h));
const obj = v1.reduce((o, r) => (o[r[0]] = hconv.map(i => i > -1 ? r[i] : null), o), {});
const len = Object.values(obj)[0].length;
const res = [obj[value] || Array(len).fill(null)];
range.offset(0, 0, res.length, res[0].length).setValues(res);
}
References:
reduce()
map()

Problem changing the color of a cell partially with Apps Script

I need to put 2 rows of data in a single cell, and color code them like so:
[endgoal]
So I decided to make a custom function with apps script, so far i've only been able to make the text be on top of each other, but can't for the life of me make it change color, so I was hoping to get some help here.
The function I made is the following (it's extremely simple but i'm a noob)
function pileUp(val1,val2) {
return val1+"\n"+val2
}
I've been searching here and found some people partially coloring cells, but none I tried worked for me, would appreciate any help.
P.D: The length of both bottom and top rows varies cell by cell, so anything that uses a range with a fixed amount of characters to color won't work
P.D.2: I do realize I didn't post my approach to actually changing the color, here it is:
function pileUp(val1,val2) {
var color="#b82f2f";
var format =SpreadsheetApp.newTextStyle()
.setForegroundColor(color)
.build();
var final=SpreadsheetApp
.newRichTextValue()
.setText('val1"\n"+val2')
.setTextStyle(0, 3, format);
return final }
I intended to see if this worked, then change the 3 in the penultimate line for a variable that was equal to the length of var1, but it didn't work.
I believe your goal is as follows.
You have the values like sample1\nsample2 in the cells. In this case, 2 lines are existing in a cell.
You want to change the text style of the 2nd line to the font color of #b82f2f.
You want to achieve this using Google Apps Script. In this case, you can directly run the script without using the custom function.
In this case, how about the following sample scripts?
Sample script 1:
In this case, the script can be run from the custom menu. When you reopened Spreadsheet, the custom menu is created by OnOpen trigger.
When you use this script, please select the cell and run the script from the custom menu. By this, the script works.
function onOpen() {
SpreadsheetApp.getUi().createMenu("sample").addItem("Run script", "myFunction").addToUi();
}
function myFunction() {
var color = "#b82f2f"; // This is from your script.
const range = SpreadsheetApp.getActiveRange();
const format = SpreadsheetApp.newTextStyle().setForegroundColor(color).build();
const v = range.getRichTextValues().map(r =>
r.map(c => {
const delimiter = "\n";
const text = c.getText();
const idx = text.indexOf(delimiter);
if (idx != -1) {
const temp = text.split(delimiter);
return c.copy().setTextStyle(idx, idx + delimiter.length + temp[1].length, format).build();
}
return c;
})
);
range.setRichTextValues(v);
}
Sample script 2:
In this case, by giving the variables of rangeA1Notation and sheetName, you can directly run the script with the script editor.
function myFunction() {
const rangeA1Notation = "A1:A3"; // Please set the cell range as A1Notation.
const sheetName = "Sheet1"; // Please set the sheet name.
const color = "#b82f2f"; // This is from your script.
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
const range = sheet.getRange(rangeA1Notation);
const format = SpreadsheetApp.newTextStyle().setForegroundColor(color).build();
const v = range.getRichTextValues().map(r =>
r.map(c => {
const delimiter = "\n";
const text = c.getText();
const idx = text.indexOf(delimiter);
if (idx != -1) {
const temp = text.split(delimiter);
return c.copy().setTextStyle(idx, idx + delimiter.length + temp[1].length, format).build();
}
return c;
})
);
range.setRichTextValues(v);
}
References:
getRichTextValues()
setRichTextValues(values)
Class RichTextValue

App Script - Nested ForEach looping on first and second col data

I have a set of areas where multiple tests were ran at different locations. If a test doesn't pass, locations are retested on a different date.
Currently, below function that was posted as a solution by #TheWizEd to my question (Google App Script - Summarizes Result from Multiple Rows and Columns with Same Area Name) is not looping for dates and recording in the output, how can I add a second loop to achieve that or if there is a better way?
function Summary() {
try {
let sheet = SpreadsheetApp.openById(SpreadsheetID).getSheetByName(SheetName03);
let sheet04 = SpreadsheetApp.openById(SpreadsheetID).getSheetByName(SheetName04);
let values = sheet.getDataRange().getValues();
values.shift(); // remove the headers
// get unique sites
let areas = values.map( row => row[0] );
areas = [...new Set(areas)]
// find results for each area
let results = areas.map( area => [area,"Pass"]);
values.forEach( row => { let index = areas.findIndex( area => area === row[0] );
let tests = row.slice(2);
if( tests.indexOf("Fail") >= 0 ) {
results[index][1] = "Fail";
return;
}
else if( tests.indexOf( "" ) >= 0 ) {
results[index][1] = "Data Missing";
return;
}
}
);
sheet04.getRange(2,1,results.length,results[0].length).setValues(results);
}
catch(err) {
console.log(err);
}
}
This is the format of the data:
Present Output:
Intended Output:
Modification points:
In your script, the 1st column of the source sheet is checked. In order to achieve your goal, I thought that 1st and 2nd columns are required to be checked.
About the output values in your script, only 2 elements are included in each array of results values. In order to achieve your goal, 3 elements are required to be included in each array.
When these points are reflected in your script, how about the following modification?
Modified script:
function Summary() {
const SheetName03 = "Sheet1"; // Please set the sheet name of source sheet.
const SheetName04 = "Sheet2"; // Please set the sheet name of destination sheet.
const SpreadsheetID = "###"; // Please set your Spreadsheet ID.
const ss = SpreadsheetApp.openById(SpreadsheetID);
const sheet = ss.getSheetByName(SheetName03);
const sheet04 = ss.getSheetByName(SheetName04);
const [, ...values] = sheet.getDataRange().getValues();
const obj = values.reduce((m, [a, b, c, ...d]) => {
const k = `${a}###${b.toISOString()}`;
return m.set(k, m.has(k) ? [...m.get(k), ...d] : d);
}, new Map());
const results = [...obj].map(([k, v]) => {
const [a, b] = k.split("###");
return [a, new Date(b), v.includes("Fail") ? "Fail" : [...new Set(v)][0] == "Pass" ? "Pass" : "Data Missing"]
});
sheet04.getRange(2, 1, results.length, results[0].length).setValues(results);
}
When this script is run, the columns "A" and "B" of the source sheet are checked, and an array for putting to the destination sheet is created and the array is put to the destination sheet.
Note:
In this modified script, it supposes that the values of column "B" of the source sheet are the date object. Please be careful about this.
In this modified script, your showing sample Spreadsheet is used. So, when you change the sheet or your actual sheet is different from your showing sheet, this script might not be able to be used. Please be careful about this.
References:
reduce()
map()

Is there a way to iterate down rows and across columns and compile that data into a cell?

I am trying to compile data from one sheet containing order information to another sheets cell based on each customers name. I'd like to take the title of the column as well as the count for the item(s) they have ordered. I tried to brute force it with a formula in Google Sheets but the formula messes up and stops giving the correct information so I figured using a scripts function would be better for what I am trying to do. I made a new sheet to test a script on but have little experience and can't seem to make any progress.
I'd like to get the title(top row) of each column and the count of the item(s) into the order column on sheet2 base on the matching names on both sheets. If anyone could help or provide some insight it would be greatly appreciated.
Here is the code I came up with:
function myFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var orders = ss.getSheetByName("Sheet2");
var data = ss.getSheetByName("Sheet1");
var names = orders.getRange("Sheet2!A2:A5");
var name = names.getValues();
var startRow = 1
var endRow = ss.getLastRow()
var getRange = ss.getDataRange();
var getRow = getRange.getRow();
var title = data.getRange("Sheet1!B1:J1");
var item = title.getValues();
var itemCell = data.getRange("Sheet1!B2").getValue();
var orderCell = orders.getRange(2,2).getValue()
Logger.log(itemCell)
if(itemCell>=0){
orders.getRange(2,2).setValue("item1 x " +itemCell)
}
currently this only has the desired effect on one cell and does not complete the row and repeat on next column.
Here is how I adjusted the code to try and fit a larger data set:
function myFunction() {
const srcSheetName = "Test Data";
const dstSheetName = "Order Changes";
const ss = SpreadsheetApp.getActiveSpreadsheet();
// 1. Retrieve source values.
const srcSheet = ss.getSheetByName(srcSheetName);
//I changed values 1,1 to change the values that were retreived
const [[, ...header], ...srcValues] = srcSheet.getRange(1, 1,
srcSheet.getLastRow(), srcSheet.getLastColumn()).getValues();
// 2. Create an object using the source values.
const srcObj = srcValues.reduce((o, [a, ...v]) => {
const temp = v.reduce((s, r, i) => {
if (r.toString() != "") s += `${header[i]} ${r}`;
return s;
}, "");
return Object.assign(o, {[a]: temp || ""});
}, {});
// 3. Retrieve the header column of destination values.
const dstSheet = ss.getSheetByName(dstSheetName);
//I changed values 2 or 1 to adjust the destination of values
const dstRange = dstSheet.getRange(2, 1, dstSheet.getLastRow() - 1);
const dstValues = dstRange.getValues();
// 4. Create the output values using the header column and the object.
const putValues = dstValues.map(([a]) => [srcObj[a] || ""]);
// 5. Put the values.
dstRange.offset(0, 1).setValues(putValues);
}
after making the changes and running the code the values would either not appear or appear in the wrong column with incorrect data.
Goal of function Update:
Match names in Sheet2!A with names in Sheet1!F
If a match is found combine header and value of cells in Sheet1!N1:BQ.
This should be from the same row of the matched name in Sheet1!F (example:
John Smith lm14 1, lm25 2, lm36 1)
Place combined data into Sheet2!C
Repeat for every name in Sheet2!A
header and cell value should not be combined if value < 0
I hope this helps to clarify any misunderstanding.
Here is are better example images:
I believe your goal as follows.
In your goal, the upper image in your question is the output you expect, and the cells "B2" should be item1 1 item3 1 item6 2 item9 3 when the lower input situation is used.
You want to achieve this using Google Apps Script.
In order to achieve above, I would like to propose the following flow.
Retrieve source values.
Create an object using the source values.
Retrieve the header column of destination values.
Create the output values using the header column and the object.
Put the values.
Sample script:
Please copy and paste the following script to the script editor of the Spreadsheet and set the source sheet name and destination sheet name, and run myFunction.
function myFunction() {
const srcSheetName = "Sheet1";
const dstSheetName = "Sheet2";
const ss = SpreadsheetApp.getActiveSpreadsheet();
// 1. Retrieve source values.
const srcSheet = ss.getSheetByName(srcSheetName);
const [[, ...header], ...srcValues] = srcSheet.getRange(1, 1, srcSheet.getLastRow(), srcSheet.getLastColumn()).getValues();
// 2. Create an object using the source values.
const srcObj = srcValues.reduce((o, [a, ...v]) => {
const temp = v.reduce((s, r, i) => {
if (r.toString() != "") s += `${header[i]} ${r}`;
return s;
}, "");
return Object.assign(o, {[a]: temp || ""});
}, {});
// 3. Retrieve the header column of destination values.
const dstSheet = ss.getSheetByName(dstSheetName);
const dstRange = dstSheet.getRange(2, 1, dstSheet.getLastRow() - 1);
const dstValues = dstRange.getValues();
// 4. Create the output values using the header column and the object.
const putValues = dstValues.map(([a]) => [srcObj[a] || ""]);
// 5. Put the values.
dstRange.offset(0, 1).setValues(putValues);
}
References:
getValues()
setValues(values)
reduce()
map()
Added 1:
About your current issue, the reason of your issue is that the ranges of source and destination are the different from the sample image in your question. My suggested answer is for the sample images in your initial question. When you changed the structure of the Spreadsheet, it is required to modify my suggested script. But from I changed values 1,1 to change the values that were retrieved and I changed values 2 or 1 to adjust the destination of values, I couldn't understand about your modified script. So as the additional script, I would like to modify my suggested answer for your updated question.
From your updated question and replyings, I understood that the source range and destination range are "N1:BQ" and "A52:C79", respectively. From this, please modify above sample script as follows.
From:
const [[, ...header], ...srcValues] = srcSheet.getRange(1, 1, srcSheet.getLastRow(), srcSheet.getLastColumn()).getValues();
To:
const [[, ...header], ...srcValues] = srcSheet.getRange("N1:BQ" + srcSheet.getLastRow()).getValues();
and
From:
dstRange.offset(0, 1).setValues(putValues);
To:
dstRange.offset(0, 2).setValues(putValues);
Added 2:
About your current issue, the reason of your issue is that the ranges of source and destination are the different from your 1st updated question in your question. My suggested answer is for the sample images in your 1st updated question. When you changed the structure of the Spreadsheet, it is required to modify my suggested script.
From your 2nd updated question, I understood that the source range and destination range are "F1:BQ" (the column "F" is the title and the columns "N1:BQ" are the values.) and "A2:C", respectively. From this, please modify above sample script as follows.
function myFunction() {
const srcSheetName = "Sheet1";
const dstSheetName = "Sheet2";
const ss = SpreadsheetApp.getActiveSpreadsheet();
// 1. Retrieve source values.
const srcSheet = ss.getSheetByName(srcSheetName);
const [[,,,,,,,, ...header], ...srcValues] = srcSheet.getRange("F1:BQ" + srcSheet.getLastRow()).getValues();
// 2. Create an object using the source values.
const srcObj = srcValues.reduce((o, [a,,,,,,,, ...v]) => {
const temp = v.reduce((s, r, i) => {
if (r.toString() != "") s += `${header[i]} ${r}`;
return s;
}, "");
return Object.assign(o, {[a]: temp || ""});
}, {});
// 3. Retrieve the header column of destination values.
const dstSheet = ss.getSheetByName(dstSheetName);
const dstRange = dstSheet.getRange(2, 1, dstSheet.getLastRow() - 1);
const dstValues = dstRange.getValues();
// 4. Create the output values using the header column and the object.
const putValues = dstValues.map(([a]) => [srcObj[a] || ""]);
console.log(srcObj)
// 5. Put the values.
dstRange.offset(0, 2).setValues(putValues);
}

Spreadsheet with conditional dropdown based on options from another sheet

I have a destination sheet where in Col "B" there are a list of "Companies", in Col "K" I would to choose, with a dropdown, the "Address" available (of that specific "Company") taken from another sheet, located in col B and J.
How to do this with a script?
https://docs.google.com/spreadsheets/d/15g_3TMmVufKZogCbO3SUWBUp3iwc21nQgPBw6_GWXQQ/edit
I believe your goal as follows.
You want to put the data validation rules to the column "K2:K" in the sheet destination.
You want to create the rules by retrieving the values from the columns "B" and "J" in the sheet source.
For this, how about this answer?
Flow:
Retrieve values from the both sheets.
Create an object for creating the rules.
Create the rules.
Put the rules to the column "K" in the sheet destination.
Sample script:
function myFunction() {
// 1. Retrieve values from the both sheets.
const ss = SpreadsheetApp.getActiveSpreadsheet();
const src = ss.getSheetByName("source");
const dst = ss.getSheetByName("destination");
const srcValues = src.getRange("A2:J" + src.getLastRow()).getValues();
const dstValues = dst.getRange("B2:B" + dst.getLastRow()).getValues();
// 2. Create an object for creating the rules.
const obj = srcValues.reduce((o, [a,b,,,,,,,,j]) => Object.assign(o, {[a]: [b, j]}), {});
// 3. Create the rules.
const rules = dstValues.map(([e]) => ([SpreadsheetApp.newDataValidation().requireValueInList(obj[e]).build()]));
// 4. Put the rules to the column "K" in the sheet `destination`.
dst.getRange("K2:K" + dst.getLastRow()).setDataValidations(rules);
}
References:
newDataValidation()
setDataValidations()
Added 1:
For your 3 additional questions in your replying, I answer as follows.
Q1: If in future I would to retrieve the values from further columns in the sheet source. How to edit the script?
A1: In this case, please modify [a,b,,,,,,,,j]. Now, a, b and j are Company, Address options 1 and Address options 2, respectively.
Q2: To have the sheets update with the script, you suggest me to activate a trigger?
A2. I cannot understand what you want to do.
Q3: If I put a value in the col "B" (destination) that is not present in col "A" (source), the script not works. I need this.
A3: Please modify above script as follows.
From
- const rules = dstValues.map(([e]) => ([SpreadsheetApp.newDataValidation().requireValueInList(obj[e]).build()]));
To
const rules = dstValues.map(([e]) => ([obj[e] ? SpreadsheetApp.newDataValidation().requireValueInList(obj[e]).build() : null]));
Added 2:
The question 2, I explain. Naturally when I run the script the dropdown are populated, but if I add a new company, I must to run again the script and so on... to avoid this, how can I do?
For above your additional 2nd question, the sample script is as follows. In this case, when the column "B" of the sheet of destination is edited, the script is run. In this case, the simple trigger can be used.
Sample script:
function onEdit(e) {
const range = e.range;
const sheet = range.getSheet();
if (range.getColumn() != 2 || sheet.getSheetName() != "destination") return;
// 1. Retrieve values from the both sheets.
const ss = e.source;
const src = ss.getSheetByName("source");
const dst = ss.getSheetByName("destination");
const srcValues = src.getRange("A2:J" + src.getLastRow()).getValues();
const dstValues = dst.getRange("B2:B" + dst.getLastRow()).getValues();
// 2. Create an object for creating the rules.
const obj = srcValues.reduce((o, [a,b,,,,,,,,j]) => Object.assign(o, {[a]: [b, j]}), {});
// 3. Create the rules.
const rules = dstValues.map(([e]) => ([obj[e] ? SpreadsheetApp.newDataValidation().requireValueInList(obj[e]).build() : null]));
// 4. Put the rules to the column "K" in the sheet `destination`.
dst.getRange("K2:K" + dst.getLastRow()).setDataValidations(rules);
}