I'm using Google Forms to collect a data. So, there was one question in the Google Form that I considered as a primary key when using the VLOOKUP function. However, there is a possibility that some data will collect zero (0) for the answer since they do not have an official ID number yet. I'm wondering if there is a way for me to automatically add or update a value when the value is zero (0). I'm thinking of using an temporary ID which letters and numbers. Ex: ID000001
I believe your goal is as follows.
You want to convert 0 to ID000001. And, when the multiple 0 values are existing, you want to convert to ID000001, ID000002,,,.
In this case, how about the following sample script?
Sample script:
function myFunction() {
const sheetName = "Sheet1"; // Please set the sheet name.
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
const range = sheet.getRange("A2:A" + sheet.getLastRow());
let c = 1;
const values = range.getValues().map(([a]) => [a === 0 ? `ID${(c++).toString().padStart(6, '0')}` : a]);
range.setValues(values);
}
In this script, it supposes that the column of "STUDENT ID" is the column "A" because I cannot know the column information from your sample image.
References:
map()
padStart()
Added:
From your following additional question,
Wait. If I run again the script, the output is starting again with ID000001. Is there a way for me to just continue the number?
In this case, how about using PropertiesServices as follows?
Sample script:
function myFunction() {
const sheetName = "Sheet1"; // Please set the sheet name.
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
const range = sheet.getRange("A2:A" + sheet.getLastRow());
const p = PropertiesService.getScriptProperties();
const count = p.getProperty("count");
let c = count ? Number(count) : 1; // If you want to set the initial count, please modify 1 to other.
const values = range.getValues().map(([a]) => [a === 0 ? `ID${(c++).toString().padStart(6, '0')}` : a]);
range.setValues(values);
p.setProperty("count", c);
}
// When you want to reset the count, please run this function.
function reset() {
PropertiesService.getScriptProperties().deleteProperty("count");
}
For example, when all ID###### values are updated every run, you can also use the following sample script.
function myFunction() {
const sheetName = "Sheet1"; // Please set the sheet name.
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
const range = sheet.getRange("A2:A" + sheet.getLastRow());
range.createTextFinder("^ID[\\d]+$").matchEntireCell(true).useRegularExpression(true).replaceAllWith(0);
let c = 1;
const values = range.getValues().map(([a]) => [a === 0 ? `ID${(c++).toString().padStart(6, '0')}` : a]);
range.setValues(values);
}
Related
I am trying to create a custom function with Google apps script which achieves the following goal:
example
To do this I have created the following Apps Script function:
function changeFontSize(cell,size) {
var spreadsheet = SpreadsheetApp.getActive().getSheetByName("Sheet1");
var range = spreadsheet.getRange(cell)
range.setFontSize(size);
}
And combined it with the following formula:
=IF(B2="Heading",changeFontSize(B3,18),changeFontSize(B3,12))
This returns the following error:
Exception: Range not found (line 3).
Why is this occuring and how can I adapt my function to correctly achieve my desired result?
Modification points:
I think that the reason of your issue of Exception: Range not found is due to B3 of =IF(B2="Heading",changeFontSize(B3,18),changeFontSize(B3,12)). In this case, the value of cell "B3" is given. If the value of cell "B3" is the A1Notation, no error might occur. But, from your current error message, I thought that the value might not be the A1Notation.
And also, even when B3 of =IF(B2="Heading",changeFontSize(B3,18),changeFontSize(B3,12)) is the A1Notation, I think that an error occurs. Because in the current stage, setFontSize cannot be used with the custom function. Unfortunately, it seems that this is the current specification.
From the above situation, in order to achieve your goal, how about using a simple trigger of OnEdit? The sample script is as follows.
Sample script:
function onEdit(e) {
const sheetName = "Sheet1"; // Please set the sheet name.
const fontSize1 = 18; // Please set the font size.
const fontSize2 = 12; // Please set the font size.
const { range } = e;
const sheet = range.getSheet();
if (sheet.getSheetName() != sheetName || range.getA1Notation() != "B2") return;
range.offset(1, 0).setFontSize(range.getValue() == "Heading" ? fontSize1 : fontSize2);
}
When you use this script, please set the value of Heading to the cell "B2". By this, the font size of the cell "B3" is changed to 18. And, when the cell "B2" is not Heading, the font size is changed to 12.
Note:
This is a simple sample script. So, please modify this for your actual situation.
Reference:
Simple Triggers
Added 1:
From your following 2nd question,
I was also hoping to expand this functionality for an entire column. So any cells within a column that contain "Heading" the cell below will be size 18 font. Is there a way to expand your solution to work for all instances within an entire column?
In this case, how about the following sample script?
Sample script:
Please set the header row.
function myFunction() {
const sheetName = "Sheet1"; // Please set the sheet name.
const fontSize1 = 18; // Please set the font size.
const fontSize2 = 12; // Please set the font size.
const headerRow = 2; // Please set the header row. From your showing sample, 2 is used.
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
const values = sheet.getDataRange().getValues();
const lastRow = values.length;
const { font1, font2 } = values[headerRow - 1].reduce((o, h, c) => {
o[h == "Heading" ? "font1" : "font2"].push(sheet.getRange(headerRow + 1, c + 1, lastRow - headerRow, 1).getA1Notation());
return o;
}, { font1: [], font2: [] });
if (font1.length > 0) {
sheet.getRangeList(font1).setFontSize(fontSize1);
}
if (font2.length > 0) {
sheet.getRangeList(font2).setFontSize(fontSize2);
}
}
In this case, when you run this script with the script editor, the values of header row is checked and the font size is changed.
Added 2:
About your 2nd question, how about the following script?
function myFunction() {
const sheetName = "Sheet1"; // Please set the sheet name.
const fontSize1 = 18; // Please set the font size.
const fontSize2 = 12; // Please set the font size.
const column = 2; // Please set the column number.
const headerTitle = "Heading";
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
const range = sheet.getRange(1, column, sheet.getLastRow());
const { s1, s2 } = range.createTextFinder(headerTitle).matchEntireCell(true).findAll().reduce((o, r) => {
o.s1.push(r.offset(1, 0).getA1Notation());
o.s2.push(r.getA1Notation());
return o;
}, { s1: [], s2: [] });
if (s2.length == 0) return;
[[s1, fontSize1], [s2, fontSize2]].forEach(([r, s]) => sheet.getRangeList(r).setFontSize(s));
}
this would work for what you are asking, in your follow-up question to Tanaike, but you should probably add in some other conditions.
Also, The first response is correct about your script not being possible and their solution is a good one, but your specific error was actually because the B3 needed to be passed to the function as a string: "B3"
function onEdit(e){
changeColumnFontSizeOnHeaderName(e);
}
function changeColumnFontSizeOnHeaderName(e) {
const getColIndexBy = (table,header_name) => table[0].indexOf(header_name);
const getTableValuesBy = (sheet) => sheet.getRange(1,1,1,1).isBlank() ? [] : Array.from(sheet.getRange(1,1,sheet.getLastRow(),sheet.getLastColumn()).getValues());
const header_name = 'Heading';
const fontSize1 = 18; // Please set the font size.
const fontSize2 = 12; // Please set the font size.
const sheet = SpreadsheetApp.getActiveSheet();
const table = getTableValuesBy(sheet); //retrieves the shreadsheet as a table.
const header_index = getColIndexBy(table,header_name); //Identifies the column index of your target header name.
const last_row_with_data = sheet.getLastRow();
const target_column = sheet.getRange(1,(header_index+1),last_row_with_data,1); //(starting row, starting column, last row, last column);
console.log(header_index+1)
const is_heading_match = sheet.getRange(1,(header_index+1)).getValue() == header_name;
const font_sizes_to_set = Array(last_row_with_data).fill().map(()=> is_heading_match ? [fontSize1] : [fontSize2]);
target_column.setFontSizes(font_sizes_to_set);
}
I have been trying to create a simple filter using Google Sheets App Script. That if cell C2 values contains `RAM' then Col"2" should hide all rows except 'RAM'. But its not working.
I have created a data validation in cell C2 which changes the value. Any help will be appreciated.
function create_filter(){
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet1 = ss.getSheetByName("Filter_Sheet");
const range = sheet1.getRange("A5:T");
const filter = range.createFilter();
const Filter_Criteria1 = Sheet1.range('C2').getActiveCell;
const coll1 = 2
const add_filter = filter.setColumnFilterCriteria(coll1,Filter_Criteria1);
}
another try:
function create_filter(){
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet1 = ss.getSheetByName("Filter_Sheet");
const range = sheet1.getRange("A4:T");
const filter = range.createFilter();
var range2 = SpreadsheetApp.getActiveSheet().getRange('D2');
var range3 sheet1.getRange('C2').activate();
var criteria = SpreadsheetApp.newFilterCriteria()
.setHiddenValues([cell != range2 ])
.build();
spreadsheet.getActiveSheet().getFilter().setColumnFilterCriteria(3, criteria);
};
I believe your goal is as follows.
You want to create the basic filter that when a text of RAM is contained to the cell value, you want to show the rows.
In this case, how about the following modification?
Modified script:
function create_filter() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet1 = ss.getSheetByName("Filter_Sheet");
const range = sheet1.getRange("A5:T");
const filter = range.createFilter();
filter.setColumnFilterCriteria(2, SpreadsheetApp.newFilterCriteria().whenTextContains("RAM").build());
}
In your 2 scripts, 2 ranges of const range = sheet1.getRange("A5:T") and const range = sheet1.getRange("A4:T") are used. In this modification, const range = sheet1.getRange("A5:T") is used. About this, please modify it for your actual situation.
When this script is run, the rows that RAM is contained at the value of column "B" are shown.
References:
setColumnFilterCriteria(columnPosition, filterCriteria)
Class FilterCriteriaBuilder
Added 1:
From the following replying in the comment and your script,
I have created a data validation in cell C2 there are 10 to 11 different string in that validation including RAM. So i want to create a on edit function that whenever Cell C2 string changes script should RUN and Column B will show exact value that is available in cell C2
I thought that the sheet name is "Filter_Sheet" and the data validation is put to the cell "C2". But, when I saw your Spreadsheet, the sheet name is "Sheet1" and the data validation is put to the cell "C1". So the following sample script uses your sample Spreadsheet. When you change the sheet name, please modify Sheet1 to others.
function onEdit(e) {
const offset = 0; // When the data validation is cell "C1" and "C2", please use 0 and 1, respectively.
const {range, value} = e;
const sheet = range.getSheet();
if (sheet.getSheetName() != "Sheet1" || range.getA1Notation() != "C" + (1 + offset)) return;
const r = sheet.getRange(`A${4 + offset}:T`);
const filter = sheet.getFilter();
if (filter) {
filter.remove();
}
r.createFilter().setColumnFilterCriteria(2, SpreadsheetApp.newFilterCriteria().whenTextContains(value).build());
}
From your following replying,
exactly the details are different because it is just sample sheet i can modify the code later when apply to real sheet
Please modify above script for your actual situation.
Added 2:
From your following replying,
but i am unable to edit this code. It is difficult for me to understand it, const value = sheet1.getRange('C2').getValue(); if this can be used then i can change the cell reference.
You want to run the script when the cell "C1" is edited. From your additional question, I understood like this. So, I proposed the modified script for using the event object of OnEdit simple trigger. But, when you don't want to use the event object, how about the following script?
function onEdit() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet1 = ss.getSheetByName("Sheet1");
const value = sheet1.getRange('C1').getValue();
const range = sheet1.getRange("A4:T");
const filter = sheet1.getFilter();
if (filter) {
filter.remove();
}
range.createFilter().setColumnFilterCriteria(2, SpreadsheetApp.newFilterCriteria().whenTextContains(value).build());
}
I have a sheet where one column contains an ID to a Jira ticket.
I would like to automatically convert this to a link to the ticket, based on the value I enter.
E.g. I'll enter SD-1234 into the column, and I would like it to then make it into a clickable link to https://demo.atlassian.net/browse/SD-1234/, but not show the URL int he cell, but the original value I entered (SD-1234).
The column is always E if that helps. Can someone give me a head start with how to script this in Script Editor?
For example, when the cell "E1" has SD-1234, =HYPERLINK("https://demo.atlassian.net/browse/"&E1, E1) is used, the value is SD-1234 with the hyperlink of https://demo.atlassian.net/browse/SD-1234. But in this case, the result cannot be directly shown in the cell "E1". When you want to directly convert SD-1234 in the cell "E1" to SD-1234 with the hyperlink of https://demo.atlassian.net/browse/SD-1234, how about the following sample script?
Sample script:
Please copy and paste the following script and run the function at the script editor. Before you use this, please set the sheet name.
This sample script converts from SD-1234 in the column "E" to =HYPERLINK("https://demo.atlassian.net/browse/SD-1234", "SD-1234").
function sample1() {
const sheetName = "Sheet1"; // Please set the sheet name.
const baseUrl = "https://demo.atlassian.net/browse/";
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
const range = sheet.getRange("E1:E" + sheet.getLastRow());
const values = range.getValues().map(([e]) => [`=HYPERLINK("${baseUrl}${e}", "${e}")`]);
range.setFormulas(values);
}
This sample script converts from SD-1234 in the column "E" to SD-1234 with the hyperlink of https://demo.atlassian.net/browse/SD-1234. In this case, the formula is not used.
function sample2() {
const sheetName = "Sheet1"; // Please set the sheet name.
const baseUrl = "https://demo.atlassian.net/browse/";
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
const range = sheet.getRange("E1:E" + sheet.getLastRow());
const values = range.getValues().map(([e]) => [SpreadsheetApp.newRichTextValue().setText(e).setLinkUrl(baseUrl + e).build()]);
range.setRichTextValues(values);
}
Note:
If your spreadsheet has the 1st header row, please modify "E1:E" + sheet.getLastRow() to "E2:E" + sheet.getLastRow().
References:
map()
Class RichTextValueBuilder
setRichTextValues(values)
Added 1:
From your following comments,
Do I have to set the sheet name? I have several sheets that I always want this function to be applied to (I have currently over 20 sheets in the file).
Also, I've entered this into Script Editor to test, and what do I do from here? There's no save that I can see... and it's not running on my sheet. How can I apply this to my sheet to automatically run whenever a value is entered in the cell?
When the above situations are reflected in the script, it becomes as follows.
Sample script:
Please copy and paste the following script to the script editor and save it. And please edit the column "E" of the sheet. By this, the cell has the hyperlink with the inputted text.
function onEdit(e) {
const sheetNames = ["Sheet1", "Sheet2",,,]; // Please set the sheet names you want to run the script.
const column = 5; // Column E. From your question, this script run for the column "E".
const {range} = e;
const sheet = range.getSheet();
const baseUrl = "https://demo.atlassian.net/browse/";
if (!sheetNames.includes(sheet.getSheetName()) || range.columnStart != column) return;
const values = range.getValues().map(([e]) => [SpreadsheetApp.newRichTextValue().setText(e).setLinkUrl(baseUrl + e).build()]);
range.setRichTextValues(values);
}
If you want to run the script for all sheets, you can use the following script.
function onEdit(e) {
const column = 5; // Column E. From your question, this script run for the column "E".
const {range} = e;
const sheet = range.getSheet();
const baseUrl = "https://demo.atlassian.net/browse/";
if (range.columnStart != column) return;
const values = range.getValues().map(([e]) => [SpreadsheetApp.newRichTextValue().setText(e).setLinkUrl(baseUrl + e).build()]);
range.setRichTextValues(values);
}
Added 2:
If you want to run the script to the column "E" of all sheets by one script running, you can also use the following script.
Sample script:
This sample script runs for the specific sheets.
function myFunction() {
const sheetNames = ["Sheet1", "Sheet2",,,]; // Please set sheet names you want to run the script.
const baseUrl = "https://demo.atlassian.net/browse/";
SpreadsheetApp.getActiveSpreadsheet().getSheets().forEach(sheet => {
if (!sheetNames.includes(sheet.getSheetName())) return;
const range = sheet.getRange("E1:E" + sheet.getLastRow());
const values = range.getValues().map(([e]) => [SpreadsheetApp.newRichTextValue().setText(e).setLinkUrl(baseUrl + e).build()]);
range.setRichTextValues(values);
});
}
This sample script runs for all sheets.
function myFunction() {
const baseUrl = "https://demo.atlassian.net/browse/";
SpreadsheetApp.getActiveSpreadsheet().getSheets().forEach(sheet => {
const range = sheet.getRange("E1:E" + sheet.getLastRow());
const values = range.getValues().map(([e]) => [SpreadsheetApp.newRichTextValue().setText(e).setLinkUrl(baseUrl + e).build()]);
range.setRichTextValues(values);
});
}
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.
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);
}