Google Sheets: Fill Down Data - google-apps-script

I want to fill down Col D starting at Item Number Row and fill down that number to that row and the rows of data under it. And repeat for next items until done. Pictures below of desired results.
How would I do this in appscript?
Before:
Desired Result:

I believe your script as follows.
You want to achieve the situation from the top image to the bottom image in your question using Google Apps Script.
Flow:
In this case, I would like to propose the following flow.
Retrieve values from the sheet.
Create an array for putting values to the column "D".
In this case, I thought that the values for putting to the column "D" can be created using the retrieve values.
Put the values to the column "D".
Sample script:
When you use this script, please copy and paste the following script to the container-bound script of the Spreadsheet. And please set sheetName.
function myFunction() {
const sheetName = "Sheet1"; // Please set this.
// 1. Retrieve values from the sheet.
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
const values = sheet.getDataRange().getValues();
// 2. Create an array for putting values to the column "D".
let temp = 0;
const outputValues = values.map(([a, b]) => {
if (a.toUpperCase().includes("ITEM NUMBER:")) temp = b;
return [temp];
});
// 3. Put the values to the column "D".
sheet.getRange("D1:D" + outputValues.length).setValues(outputValues);
}
Note:
Please use this script with enabling V8.
If you want to use the script as the custom function, you can also use the following script. In this case, please put a custom function of =myFunction2(A1:B12) to the cell "D1" for your sample Spreadsheet.
function myFunction2(values) {
let temp = 0;
return values.map(([a, b]) => {
if (a.toUpperCase().includes("ITEM NUMBER:")) temp = b;
return [temp];
});
}
References:
getValues()
map()
setValues(values)

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()

How can I copy values from specific columns by google app script

I need to get & combine values of some specific columns (Name, Age, Phone) from a raw sheet to another sheet but I don't know how to do it with the google app script.
the workflow looks like as below:
First, I need to get values of only Name, Age, Phone columns from the raw sheet, I don't want to collect the region column. The raw sheet looks like as an image here:
Then I think I have to push it to an array and paste it to the result sheet which contains 3 columns: Name, Age, Phone
In query function I can do it with this statement:
=query("raw sheet","select Col1, Col3, Col4")
but I don't know how to do it with google app script.
If you use to try it with google app script, please share with me the reference code to do it, thank you so much!
I believe your goal is as follows.
You want to retrieve the specific columns from a source sheet, and want to put the retrieved columns to the destination sheet using Google Apps Script.
In this case, how about the following sample script?
Sample script:
Please copy and paste the following script and set the variables of sourceAndDestinationSheet and dstHeader and run the function with the script editor.
function myFunction() {
const sourceAndDestinationSheet = ["source sheet name", "destination sheet name"]; // Please set the source sheet name and destination sheet name to the 1st and 2nd element.
const dstHeader = ["Name", "age", "phone"]; // This is the header of the destination values.
// 1. Retrieve values from the source sheet.
const ss = SpreadsheetApp.getActiveSpreadsheet();
const [srcSheet, dstSheet] = sourceAndDestinationSheet.map(s => ss.getSheetByName(s));
const [header, ...values] = srcSheet.getDataRange().getValues();
// 2. Create an array of destination values.
const colIndexes = dstHeader.map(h => header.indexOf(h));
const dstValues = [dstHeader, ...values.map(r => colIndexes.map(i => r[i]))];
// 3. Put the destination values to the destination sheet.
dstSheet.getRange(1, 1, dstValues.length, dstValues[0].length).setValues(dstValues);
}
Note:
If you want to use this script as a custom function, how about the following script? In this case, please put a custom formula of =SAMPLE(Sheet1!A1:D, "Name,age,phone") (It supposes that the source sheet and range are "Sheet1" and "A1:D") to the destination sheet.
function SAMPLE(v, h) {
const dstHeader = h.split(",").map(e => e.trim());
const [header, ...values] = v;
const colIndexes = dstHeader.map(h => header.indexOf(h));
return [dstHeader, ...values.map(r => colIndexes.map(i => r[i]))];
}
In this sample script, it is required to be the same between dstHeader and the actual header name of the source sheet. Please be careful this.
References:
map()
Custom Functions in Google Sheets
Try
=query('raw_2'!A1:D,"select A,B,D where A is not null",1)

Looking for better way to pass range for my function sumBlack()

I would love to be able use my script in Google sheet with just =sumBlack(C4:14)
currently my script ** see below ** works in Google sheet with =sumBlack(3,4,14)
3 for column, 4 and 14 are the row start and end
here's my code... pleased that it works though
it sums only cells that have the fontColor of black
function sumBlack(column, rowst, rowend) {
result = 0;
for(row = rowst;row <= rowend;row++){
var txtColor = SpreadsheetApp.getActive().getDataRange().getCell(row, column).getFontColor();
if(txtColor == "#000000"){
result = result + SpreadsheetApp.getActive().getDataRange().getCell(row, column).getValue();
}
}
return result;
}
I believe your goal as follows.
You want to sum the values of cells, when the font color is #000000 as hex.
You want to achieve this using the custom function.
Modification points:
In this case, in order to give the a1Notation to the custom function, how about using =sumBlack("C4:14") instead of =sumBlack(C4:14)? Because when =sumBlack(C4:14) is used, the values of cells "C4:14" is given as 2 dimensional array. By this, the range cannot be known.
In this modification, getFontColors() and getValues() are used instead of getFontColor()andgetValue(), respectively. By this, I think that the process cost will be able to be reduced.
When you can permit this suggestion, how about the following modified script?
Modified script:
When you use this script, please put =sumBlack("C4:14") to a cell. In this case, please don't forget to enclose the a1Notation with ".
function sumBlack(a1Notation) {
const range = SpreadsheetApp.getActiveSheet().getRange(a1Notation);
const fontColors = range.getFontColors();
const values = range.getValues();
const result = fontColors.reduce((n, r, i) => {
r.forEach((c, j) => {
if (c == "#000000") n += Number(values[i][j]);
});
return n;
}, 0);
return result;
}
If you want to give the sheet name like =sumBlack("Sheet1!C4:14"), please modify above script as follows.
From
const range = SpreadsheetApp.getActiveSheet().getRange(a1Notation);
To
const range = SpreadsheetApp.getActiveSpreadsheet().getRange(a1Notation);
In above modified script, when =sumBlack("C4:14") is put and the cell value of "C4:14" is changed, no recalculation occurs. If you want to recalculate for this situation, please add the following script. The following script is automatically run when the cells in the active sheet are edited, and the formula of =sumBlack() is recalculated.
function onEdit(e) {
const sheet = e.source.getActiveSheet();
sheet.createTextFinder("=sumBlack").matchFormulaText(true).replaceAllWith("###sumBlack");
sheet.createTextFinder("###sumBlack").matchFormulaText(true).replaceAllWith("=sumBlack");
}
References:
getFontColors()
getValues()
reduce()
Class TextFinder

Google Sheet Formatting

I have bill of materials report where the data is double stacked when exported from database.
Original data:
Sheet is very large, but I want to format the data like this
Still a noob with appscript, but how would i script this to do that?
Thanks in advance.
I believe your goal as follows.
You want to achieve the following conversion on Google Spreadsheet using Google Apps Script.
From: Source sheet
To: Destination sheet
Flow:
In this case, I would like to propose the following flow of the sample script.
Retrieve values from the source sheet.
Create values for putting to the destination sheet an 2 dimensional array.
In this case, in the following sample script, the value of column "A" is checked. When the value of column "A" is not empty, the values of columns "A" to "D" is set to temp array. And when the next row of it has no value of column "A", the value of row is added to temp. By this, 0,123-00000-00,Test Assembly,,ABC Inc,abc123 is created. When the next row has no value of column "A", the value is added to ar by adding 4 empty values. By this, ,,,,XYZ Inc,abc234 is created. By this flow, the destination values are created.
Put the created value to the destination sheet.
Sample script:
When you use this script, please copy and paste the following script to the container-bound script of the Spreadsheet. And please set the source sheet name and destination sheet name.
function myFunction() {
const srcSheetName = "Sheet1"; // Please set the source sheet name.
const dstSheetName = "Sheet2"; // Please set the destination sheet name.
// 1. Retrieve values from the source sheet.
const ss = SpreadsheetApp.getActiveSpreadsheet();
const [header1, [,subHead1, subHead2], ...srcValues] = ss.getSheetByName(srcSheetName).getDataRange().getValues();
// 2. Create values for putting to the destination sheet an 2 dimensional array.
let temp = [];
const dstValues = srcValues.reduce((ar, [a,b,c,d]) => {
if (a.toString() != "") {
if (temp.length == 4) ar.push(temp.concat("",""));
temp = [].concat(a,b,c,d);
} else {
ar.push(temp.length == 4 ? temp = temp.concat(b,c) : Array(4).fill("").concat(b,c));
}
return ar;
}, [header1.concat(subHead1, subHead2)]);
// 3. Put the created value to the destination sheet.
ss.getSheetByName(dstSheetName).getRange(1, 1, dstValues.length, dstValues[0].length).setValues(dstValues);
}
When you run the function of myFunction(), the values are put to the destination sheet.
Note:
Please use this script with enabling V8.
If you want to use the script as the custom function, you can also use the following script. When you use this script, for example, when your sample input sheet is used, please put a custom function of =CUSTOMSAMPLE(A1:D12).
function CUSTOMSAMPLE(values) {
// 1. Retrieve values from the source sheet.
const [header1, [,subHead1, subHead2], ...srcValues] = values;
// 2. Create values for putting to the destination sheet an 2 dimensional array.
let temp = [];
const dstValues = srcValues.reduce((ar, [a,b,c,d]) => {
if (a.toString() != "") {
if (temp.length == 4) ar.push(temp.concat("",""));
temp = [].concat(a,b,c,d);
} else {
ar.push(temp.length == 4 ? temp = temp.concat(b,c) : Array(4).fill("").concat(b,c));
}
return ar;
}, [header1.concat(subHead1, subHead2)]);
// 3. Put the created value to the destination sheet.
return dstValues;
}
This sample script is for your sample input and output values in your question. When the cell structure in your actual sheet is different from them, the script might not be able to be used. Please be careful this.
References:
getValues()
reduce()
setValues(values)

function to list row number if the cell in a range matches with a key

I have a table like below.
Is there any function that returns the list of row numbers that matches with a key passed into the function? An example like below would returns 2 and 6 onto two cells.
function_to_listup("bar", "A1:A6")
If there isn't any built-in function, I would like to achieve it with a local function using google-app-script if possible.
It is possible to use a simple FILTER passing row numbers as the main argument and the condition as the second argument:
=FILTER(ROW(A1:A6),A1:A6="bar")
If you don't want the filtered numbers in a separate array/cells, you could use a simple IF enforcing array context with ARRAYFORMULA.
=ARRAYFORMULA(JOIN(",", IF(A1:A6="bar",ROW(A1:A6),)))
Pattern 1:
In this pattern, the built-in function is used.
Sample formula:
=QUERY({A1:A6,ARRAYFORMULA(ROW(A1:A6))},"select Col2 where Col1='bar'")
Input the range of A1:A6 and row numbers of ARRAYFORMULA(ROW(A1:A6)).
When the value of A1:A6 is bar, the row number of the same row is returned.
Result:
Pattern 2:
In this pattern, the custom function created by Google Apps Script is used. In this case, please put =function_to_listup("bar", "A1:A6") to a cell. From your question, it supposes that the range is a1Notation.
Sample script:
function function_to_listup(searchValue, a1Notation) {
const sheet = SpreadsheetApp.getActiveSheet();
const range = sheet.getRange(a1Notation);
const offset = range.getRow();
const values = range.getValues();
return values.reduce((ar, [v], i) => {
if (v == searchValue) ar.push(i + offset);
return ar;
}, []);
}
In this case, the result is the same with above sample formula.
Pattern 3:
In this pattern, the custom function created by Google Apps Script is used. TextFinder is used for this situation. In this case, please put =function_to_listup("bar", "A1:A6") to a cell. From your question, it supposes that the range is a1Notation.
Sample script:
function function_to_listup(searchValue, a1Notation) {
return SpreadsheetApp
.getActiveSheet()
.getRange(a1Notation)
.createTextFinder(searchValue)
.matchEntireCell(true)
.findAll()
.reduce((ar, r) => ar.concat(r.getRow()), []);
}
In this case, the result is the same with above sample formula.
References:
QUERY
Custom Functions in Google Sheets
Class TextFinder