Script to move email addresses from one sheet to another - google-apps-script

I have problems figuring out how to script the following:
1. I have a sheet tab_A:
Col_A, Col_B
URL_1, Email_1
URL_2, Email_2
URL_3, Email_3
1. I have a sheet tab_B:
Col_A, ..., Col_F
URL_3, empty
URL_1, empty
URL_2, empty
"..." means several columns with data.
I need a script that takes row for row in tab_A, copy the email address, find the correct row in tab_B (the same URL) and paste the email in Col F.
Any help is appreciated.

I believe your goal as follows.
You want to copy the values of the column "B" of "tab_A" to the column "F" of "tab_B" by checking the column "A" of "tab_A" and "tab_B" on the Spreadsheet.
For example, when the row 1 has URL_1, Email_1 on "tab_A" and the row 2 has URL_1 on "tab_B", you want to copy the value of Email_1 to the column "F" of "tab_B" with the same row.
In order to achieve your goal, I would like to propose the following flow.
Retrieve values from "tab_A" and create an object for searching the value of the column "A".
Create an array for putting values to "tab_B".
Put the values to "tab_B".
Sample script:
Please copy and paste the following script to the script editor of Spreadsheet and run myFunction. By this, the values are put to "tab_B". Before you use this, please set the sheet names for your actual situation.
function myFunction() {
// 1. Retrieve values from "tab_A" and create an object for searching the value of the column "A".
const ss = SpreadsheetApp.getActiveSpreadsheet();
const tabA = ss.getSheetByName("tab_A");
const sourceValues = tabA.getDataRange().getValues();
const obj = sourceValues.reduce((o, [a, b]) => Object.assign(o, {[a]: b}), {});
// 2. Create an array for putting values to "tab_B".
const tabB = ss.getSheetByName("tab_B");
const outputValues = tabB.getDataRange().getValues().map(r => [obj[r[0]] || ""]);
// 3. Put the values to "tab_B".
tabB.getRange(1, 6, outputValues.length, 1).setValues(outputValues);
}
Note:
When you want to use above script as the custom function, you can also use the following script. In this case, please put a custom formula of =SAMPLE(tab_A!A1:B, tab_B!A1:A) to the cell "F1" of "tab_B".
function SAMPLE(valuesOfTabA, valuesOfTabB) {
const obj = valuesOfTabA.reduce((o, [a, b]) => Object.assign(o, {[a]: b}), {});
return valuesOfTabB.map(r => [obj[r[0]] || ""]);
}
References:
getValues()
setValues(values)
Custom Functions in Google Sheets
reduce()
map()

Related

Concatenating merged cells and on a specific condition in apps script

I have this table where I want to concatenate each row in it on specific columns
The concatenation should happen on columns Option Name, Type 1, Type 2and Existing measure/New measure
For Existing measure and New measure wherever there is an entry that value should be picked up for concatenation.
The output I want is an array as below
[Name 1-C type 1-Yearly-GB, Name 1-C type 2-Monthly-MB, Name 2-C type 3-Quarterly-GB, Name 2-C type 2-Daily-Bytes,Name 2-C type 4-Monthly- MB,Name 3-C type 1-Yearly-KB]
I could use an array formula and concatenate and store in column L and read that column via apps script. but I want to use apps script to get the array I desire.
Please help!
I believe your goal is as follows.
You want to convert your sample input Spreadsheet image to the array of The output I want is an array as below.
You want to put the created array in the column "L".
You want to achieve this using Google Apps Script.
In your situation, how about the following sample script?
Sample script:
function myFunction() {
const sheetName = "Sheet1"; // Please set your sheet name.
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
const range = sheet.getRange("C7:K" + sheet.getLastRow());
let temp = "";
const ar = range.getDisplayValues().map(([c, , , , , ...v]) => {
if (c) temp = c;
return [[c, ...v].join("") && [temp, ...v.filter(String)].join("-")];
});
console.log(ar); // You can see the created array in the log.
// If you want to retrieve the flattened array like your question, please use console.log(ar.flat());
range.offset(0, 9, ar.length, 1).setValues(ar);
}
When this script is run, the values are retrieved from columns "C" and "H" - "K", and an array is created including the string values concatenated columns "H" - "K". And, the created array is put to column "L".
Reference:
map()

Automate Costs in Google Sheets from Google Form Checkbox Input

I'm trying to use Google Forms to collect some information on my clients. The application here is a clinical setting, so the form asks what treatments they would like in the form of a checkbox option. I would then like to automate the sum total of the treatment costs using the forms output.
The issue I'm having is that Google Forms outputs a list of strings in a single cell for this response. I'll add more detail below, but I don't know how to split the string into individual values, lookup that value in a separate column, get the cost, and display only the sum in a separate cell.
I've made a minimal working example in the form of a GSheet, you can find it here.
In that master sheet, you'll find three other sheets; Costs, Form Responses, and Overview.
The Costs sheet is static and only contains a list of items and their costs. This sheet will change on occasion (price updates, removal/addition of items)
The Form Responses sheet will contain the raw output from a Google Form. The column of note here is the Choose Things from the List column, which contains a list of responses.
The Overview sheet will house some redundant info, but it's meant to be a cleaned-up sheet with information. You'll notice a Cost of Things ($) column. In this column, I would like the total sum of all items the response list from the Form Responses sheet.
I can do this in Python easy. I would do it something like this:
costs = {'a': 1, 'b': 2, 'c': 3}
input_items = ['a', 'b', 'c']
x = []
for item in input_items:
x.append(costs[item])
total_sum = sum(x)
How would I do this with Google Sheets? I want to
split a list embedded in a cell
check each list item for its cost in a separate sheet
sum the costs of each item
Please let me know if I need to clarify, I'm not quite sure how to pose the problem using Google Sheets language.
==============
EDIT: Sorry, I just updated the GSheet permissions. It should be viewable to everyone now.
In your situation, how about the following sample script?
Sample script:
function myFunction() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet1 = ss.getSheetByName("Costs");
const sheet2 = ss.getSheetByName("Forms Responses");
const sheet3 = ss.getSheetByName("Overview");
// 1. Retrieve values from "Costs" sheet and create an object.
const values1 = sheet1.getRange("A2:B" + sheet1.getLastRow()).getValues();
const obj1 = values1.reduce((o, [a, b]) => (o[a.trim()] = b, o), {});
// 2. Retrieve values from "Forms Responses" sheet and create an object.
const values2 = sheet2.getRange("A2:C" + sheet2.getLastRow()).getValues();
const obj2 = values2.reduce((o, [a, b, c]) => (o[a.trim() + b.trim()] = c.split(",").reduce((n, e) => (n += obj1[e.trim()] || 0, n), 0), o), {});
// 3. Retrieve values from "Overview" sheet and create an array.
const values3 = sheet3.getRange("A2:B" + sheet3.getLastRow()).getValues();
const res = values3.map(([a, b]) => [obj2[a.trim() + b.trim()] || null]);
// 4. Put array to "Overview" sheet.
sheet3.getRange(2, 3, res.length, 1).setValues(res);
}
In this sample script, the following flow is run.
Retrieve values from "Costs" sheet and create an object.
Retrieve values from "Forms Responses" sheet and create an object.
Retrieve values from "Overview" sheet and create an array.
Put array to "Overview" sheet.
In this case, the result values are put to the column "C".
Note:
This sample script is for your sample Spreadsheet. So, when you change the Spreadsheet, the script might not be able to be used. Please be careful about this.
References:
getValues()
setValues(values)
reduce()
map()

Delete Row if Match found from another spreadsheet - Google Sheets

I would like to delete any rows of data from spreadsheet 2 if a Match is found in spreadsheet 1.
In the image below (spreadsheet 1) we have SKU A10114 & New Location J05A1.
In the below Image(Spreadsheet 2) here you can see SKU A10114 at Location J05A1 has 2 line entries.
So the code would delete both lines of A10114 at Location J05A1 ONLY
If A10114 had a different location it would not be deleted
I believe your goal as follows.
You want to delete rows in Sales sheet, when the values of columns "A" and "F" in Relocation sheet are included in the values of columns "A" and "B" in Sales sheet.
For this, I would like to propose the following flow.
Retrieve the values from Relocation sheet and create an object for searching values.
Retrieve the values from Sales sheet and create an array for deleting the rows.
Delete rows.
When above flow is reflected to the script, it becomes as follows.
Sample script:
function myFunction() {
const ss = SpreadsheetApp.getActive();
// 1. Retrieve the values from `Relocation` sheet and create an object for searching values.
const sheet1 = ss.getSheetByName('Relocation');
const valuesSheet1 = sheet1.getRange("A1:J" + sheet1.getLastRow()).getValues()
.reduce((o, [a,,,,,f]) => Object.assign(o, {[a + f]: true}), {});
// 2. Retrieve the values from `Sales` sheet and create an array for deleting the rows.
const sheet2 = ss.getSheetByName('Sales');
const valuesSheet2 = sheet2.getRange("A2:B" + sheet2.getLastRow()).getValues()
.reduce((ar, [a,b], i) => {
if (valuesSheet1[a + b]) ar.push(i + 2);
return ar;
}, []).reverse();
// 3. Delete rows.
valuesSheet2.forEach(r => sheet2.deleteRow(r));
}
Note:
When I saw your script in your shared Spreadsheet, from the sheet names in the script, I thought that your script might not be related to this question. So I proposed above sample script.
References:
reduce()
deleteRow(rowPosition)

Score calculation, considering negative marking

I'm trying to find if I can ask a script to calculate a grade where for each good answer it gives +1, and for each wrong answer it substracts -0,25. The calculation will be made in column C, as shown in the screenshot. In the example, the correct score is 6,25 instead of 7.
BTW, the score calculation refers to the key answers on the row 2.
I believe your goal as follows.
You want to calculate for the values of column "C" by checking the background colors of each cell using Google Apps Script.
When the background color of cell is #3ace9c (green), you want to add 1.
When the background color of cell is #ff9900 (orange), you want to reduce 0.25.
You want to calculate for the values of column "C" by comparing the columns "D" to "M" at the row 2 (the referring answer) and each row (after row 3) using Google Apps Script.
When the columns "D" to "M" at the row 2 are the same with each row, you want to add 1.
When the columns "D" to "M" at the row 2 are NOT the same with each row, you want to reduce 0.25.
For this, how about this answer?
Pattern 1:
In this pattern, the background colors are used. The flow of this sample script is as follows.
Prepare an object including the color codes for "good" and "wrong" answers.
Retrieve the background colors from the range of "D3:M" in the sheet.
Calculate the values using the object and the background colors.
Put the values to the column "C" in the sheet.
Sample script:
function myFunction() {
const sheetName = "Sheet1"; // Please set the sheet name.
// 1. Prepare an object including the color codes for "good" and "wrong" answers.
const obj = {"#3ace9c": 1, "#ff9900": -0.25}; // Please set the color codes, if you change the color.
// 2. Retrieve the background colors from the range of "D3:M" in the sheet.
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
const backgrounds = sheet.getRange("D3:M" + sheet.getLastRow()).getBackgrounds();
// 3. Calculate the values using the object and the background colors.
const result = backgrounds.map(r => [r.reduce((c, e) => c += obj[e], 0)]);
// 4. Put the values to the column "C" in the sheet.
sheet.getRange(3, 3, result.length, 1).setValues(result);
}
Pattern 2:
In this pattern, the referring answer of the row 2 is used. The flow of this sample script is as follows.
Retrieve values from the range of "A2:M" in the sheet.
Retrieve the referring answer.
Calculate the values of each values by comparing the referring answer, and create an array including the result values.
Put the values to the column "C" in the sheet.
Sample script:
function myFunction() {
const sheetName = "Sheet1"; // Please set the sheet name.
// 1. Retrieve values from the range of "A2:M" in the sheet.
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
const values = sheet.getRange("A2:M" + sheet.getLastRow()).getValues();
// 2. Retrieve the referring answer.
const answers = values.shift();
answers.splice(0, 3);
// 3. Calculate the values of each values by comparing the referring answer, and create an array including the result values.
const result = values.map(([,,c,...dm]) => [dm.reduce((c, e, i) => e == answers[i] ? c + 1 : c - 0.25, 0)]);
// 4. Put the values to the column "C" in the sheet.
sheet.getRange(3, 3, result.length, 1).setValues(result);
}
References:
map()
reduce()
getBackgrounds()
getValues()
setValues()

How do you apply a JOIN and FILTER formula to an entire column in Google Sheets using Google Script?

I'm very new to Google Scripts so any assistance is greatly appreciated.
I'm stuck on how to apply my formula which uses both JOIN and FILTER to an entire column in Google Sheets.
My formula is: =JOIN(", ",FILTER(N:N,B:B=R2))
I need this formula to be added to each cell in Column S (except for the header cell) but with 'R2' changing per row, so in row 3 it's 'R3', row 4 it's 'R4' etc.
This formula works in Google sheets itself but as I have sheet that is auto replaced by a new updated version daily I need to set a google script to run at certain time which I can set up via triggers to add this formula to my designated column.
I've tried a few scripts I've found online but none have been successful.
If you want to solve this using only formulas:
Since your formula is always in the format:
=JOIN(", ",FILTER(N:N,B:B=R<ROW NUMBER>))
and you want to apply it to a very large number of rows, you can use INDIRECT and ROW to achieve a dynamic formula. This answer has a good example on how to use this.
Using formulas you don't risk running into time limits with Apps Script
In practical terms, if you have your data on column A, you can write =ARRAYFORMULA(CONCAT("R",ROW(A2:A))) to get something like this:
Your final formula should look like this:
=JOIN(", ",FILTER($N:$N,B:B=INDIRECT(CONCAT("R",ROW($R2)))))
Also, you can drag it down to other cells like any other formula!
Set the formulas through Apps Script:
You can use setFormulas(formulas) to set a group of formulas to all the cells in a range. formulas, in this case, refers to a 2-dimensional array, the outer array representing the different rows, and each inner array representing the different columns in each specific row. You should build this 2D array with the different formulas, while taking into account that the row index from R should be different for each single formula.
You could do something like this:
function settingFormulas() {
var sheet = SpreadsheetApp.getActive().getSheetByName("Sheet1");
var firstRow = 2;
var column = 19; // Column S index
var range = sheet.getRange(firstRow, column, sheet.getLastRow() - firstRow + 1);
var formulas = range.getValues().map((row, index) => {
let rowIndex = index + firstRow;
return ["=JOIN(\", \",FILTER(N:N,B:B=R" + rowIndex + "))"];
});
range.setFormulas(formulas);
}
In this function, the optional index parameter from the method map is used to keep track of the row index, and adding it to the formula.
In this function, the sheet name is used to identify which sheet the function has to set the formulas to (in this case, the name's Sheet1). Here I'm assuming that once the sheet is replaced by a newer one, the sheet name remains the same.
Execute this daily:
Once you have this function, you just have to install the time-driven trigger to execute this function daily, either manually, following these steps, or programmatically, by running this function once:
function creatingTrigger() {
ScriptApp.newTrigger("settingFormulas")
.timeBased()
.everyDays(1)
.create();
}
Reference:
setFormulas(formulas)
getRange(row, column, numRows)
Installable Triggers
Instead of the workaround hacks I implemented a simple joinMatching(matches, values, texts, [sep]) function in Google Apps Script.
In your case it would be just =joinMatching(R1:R, B1:B, N1:N, ", ").
Source:
// Google Apps Script to join texts in a range where values in second range equal to the provided match value
// Solves the need for `arrayformula(join(',', filter()))`, which does not work in Google Sheets
// Instead you can pass a range of match values and get a range of joined texts back
const identity = data => data
const onRange = (data, fn, args, combine = identity) =>
Array.isArray(data)
? combine(data.map(value => onRange(value, fn, args)))
: fn(data, ...(args || []))
const _joinMatching = (match, values, texts, sep = '\n') => {
const columns = texts[0]?.length
if (!columns) return ''
const row = i => Math.floor(i / columns)
const col = i => i % columns
const value = i => values[row(i)][col(i)]
return (
// JSON.stringify(match) +
texts
.flat()
// .map((t, i) => `[${row(i)}:${col(i)}] ${t} (${JSON.stringify(value(i))})`)
.filter((_, i) => value(i) === match)
.join(sep)
)
}
const joinMatching = (matches, values, texts, sep) =>
onRange(matches, _joinMatching, [values, texts, sep])