Break multiple value cell into different rows via Apps Scripts - javascript

I'm looking to split a cell that have multiple values separated by "\n" via App Script. Itried a few functions that were proposed but nothing seemed to work. Below you can see a sample format and desired outcome.
Thanks.

I believe your goal is as follows.
You want to split each cell value with \n using Google Apps Script.
You want to put the split values in the vertical direction.
In this case, how about the following sample script?
Sample script:
function myFunction() {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1"); // Please set your sheet name.
const range = sheet.getRange("A2:A" + sheet.getLastRow());
const values = range.getValues().flatMap(([a]) => a ? a.split("\n").map(e => [e.trim()]) : []);
range.offset(0, 1, values.length).setValues(values);
}
When this script is run, the source values are retrieved from the column "A". And, the converted values are put in column "B".
Reference:
flatMap()

Related

How to get a new table with keys and sum results in Google Spreadsheets?

I got a script to count sum for each column in my Google sheet. I'm getting correct result in a Dialog message.
Now I need to make a chart from this data. So I need to extract keys and values and insert to sheet in active spreadsheet.
function countNutrients(){
var sh=SpreadsheetApp.getActiveSheet();
var hA=sh.getRange(2,23,1,sh.getLastColumn()-22).getValues()[0];
var sum={};
hA.forEach(function(h,i){sum[h]=0;})
var vA=sh.getRange(3,23,sh.getLastRow()-1,sh.getLastColumn()-22).getValues();
vA.forEach(function(r,i){
r.forEach(function(c,j){
if(!isNaN(c)) {
sum[hA[j]]+=c;
Logger.log(sum);
}
});
});
var html="";
var keys=Object.keys(sum);
keys.forEach(function(k,i){
html+=Utilities.formatString('<br />sum[%s]=%s',k,sum[k]);
})
var ui=HtmlService.createHtmlOutput(html);
SpreadsheetApp.getUi().showModelessDialog(ui,'The dish contains');
}
Any ideas how to get it?
Thank you!
Understanding
You want to retrieve the keys and values from the object of sum as an array.
You want to put the retrieved values to a sheet in the active Spreadsheet.
You want to achieve this by modifying your script.
Modification point:
In this answer, the keys and values are retrieved from the object of sum using Object.entries().
Modified script:
When your script is modified, it becomes as follows. Before you run the script, please set the sheet name that you want to put the values.
From:
var html="";
var keys=Object.keys(sum);
keys.forEach(function(k,i){
html+=Utilities.formatString('<br />sum[%s]=%s',k,sum[k]);
})
var ui=HtmlService.createHtmlOutput(html);
SpreadsheetApp.getUi().showModelessDialog(ui,'The dish contains');
To:
var values = Object.entries(sum);
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
sheet.getRange(1, 1, values.length, values[0].length).setValues(values);
Note:
In this case, the keys and values are put to the column "A" and "B", respectively. If you want to put the keys and values to other columns, please tell me.
When you use this modified script, please enable V8 at the script editor.
Reference:
Object.entries()

Sorting raw data from one sheet into separate tabs on a new sheet in Google App Script

I have a large data file that I want to be separated/filtered out into separate tabs on a new sheet. They need to be filtered by a certain column containing "BGT" within the string.
I am very new to using Google Apps Script so this is a work in progress. I am able to pull the data from one sheet into another, convert it to an array and assign the column I want to sort by. I just can't properly set up the filter.
function CreativeReport() {
  var ss = SpreadsheetApp.openByUrl(SPREADSHEET_URL);
  var sheet = ss.getSheetByName(SHEET_NAME);
var folder = getDriveFolderFromPath("Daily Creative Audit");
var fileId = folder.getFiles().next().getId();
var RawMM = SpreadsheetApp.openById(fileId);
var ArrayRawData = RawMM.getRange(2,1,RawMM.getLastRow()-1,5).getValues();
var ConceptName = RawMM.getRange('A:A').getValues();
var BGTFilter = ConceptName.includes("BGT");
var BGTFilteredData = ArrayRawData.filter(function(item){ return item [6] === BGTFilter;});
Logger.log(BGTFilteredData);
The column I want to sort buy contains cells formatted like this
2019_BGT_Bigsale_300x50
2019_SWT_Bigsale_300x50
2019_AI_Bigsale_300x50
2019_SWO_Bigsale_300x50
2019_DCO_Bigsale_300x50.
The goal is to have the script filter this raw data, and sort it into separate sheets depending on the cells containing:
BGT
SWO
SWT
AI
DCO
The code I how SHOULD log out just the concepts containing "BGT" in the string but I get
TypeError: Cannot find function includes in object ,concept_name,
You have values at "Sheet1" in the source Spreadsheet.
You want to separate the values by checking whether the column "A" has the values of BGT, SWO, SWT, AI, DCO.
You want to put the separated values to the sheet names of BGT, SWO, SWT, AI, DCO in the destination Spreadsheet.
I could understand like above. If my understanding is correct, how about this modification? Unfortunately, the modified script was largely changed from your original script. I apologize for this. I think that there are several answers for your situation. So please think of this as just one of them.
The flow of the modified script is as follows.
Retrieve values from the source sheet.
Create an object for retrieving values for each destination sheet.
The object is like {"BGT": [values], "SWO": [values],,,}.
Put values for the destination sheets.
Modified script:
Before you run the script, please set srcSpreadsheetId, dstSpreadsheetId and srcSheetName.
function CreativeReport() {
// Variables: Please set them for your situation.
var srcSpreadsheetId = "###"; // Please set the source Spreadsheet ID. In your sample, the Spreadsheet name is "Sample Raw Data".
var dstSpreadsheetId = "###"; // Please set the destination Spreadsheet ID. In your sample, the Spreadsheet name is "Sorted Data".
var srcSheetName = "Sheet1"; // Please modify this. In this sample, it's the same with your sample Spreadsheet.
var dstSheetNames = ["BGT", "SWO", "DCO", "AI", "SWT"];
// Open Spreadsheets
var srcSS = SpreadsheetApp.openById(srcSpreadsheetId);
var dstSS = SpreadsheetApp.openById(dstSpreadsheetId);
// Retrieve values from the source sheet.
var srcSheet = srcSS.getSheetByName(srcSheetName);
var srcValues = srcSheet.getRange(2, 1, srcSheet.getLastRow() - 1, 5).getValues();
// Create an object for retrieving values for each destination sheet.
var object = srcValues.reduce(function(obj, e) {
dstSheetNames.forEach(function(f) {
if (e[0].indexOf(f) > -1) obj[f] = obj[f] ? obj[f].concat([e]) : [e];
});
return obj;
}, {});
// Put values for the destination sheets.
dstSheetNames.forEach(function(e) {
var sheet = dstSS.getSheetByName(e);
sheet.getRange(sheet.getLastRow() + 1, 1, object[e].length, object[e][0].length).setValues(object[e]);
});
}
Note:
As mentioned at my comment, the values retrieved by getValues() is 2 dimensional array. So if Array.includes() can be used at Google Apps Script, in your script, ConceptName.includes("BGT") is always false. And, unfortunately, Array.includes() cannot be used at Google Apps Script. Ref
This modified script was prepared using the sample Spreadsheets of "Sample Raw Data" and "Sorted Data" you shared. So if your actual Spreadsheet is different from these samples, please modify above script.
When the values of source Spreadsheet and the number of destination sheets are much increased, I recommend to use Sheets API for putting the values for each destination sheet.
References:
getValues()
Basic JavaScript features
includes()
indexOf()
reduce()
for​Each()

How to Merge rows in Google Sheets

I tried digging this stuff and have no solution so I'm hoping someone can assist. I have a sheet with the following:
Data
123|456|789
111|222|333
etc...
Result Needed
|123 456 789|
|111 222 333|
etc...
I'm trying to avoid formulas (=Concat) and (=A2&" "&B2&" "&C2) etc...
I tried sheet.getRange(2,1,1,2).mergeAcross(); and it merged the cells and kept he left-most value. Google searches point to the formula solution.
You can try Array.join() for each row:
Snippet:
var jointRowsArray = sheet
.getRange(2, 1, 2, 3) //A2:C4
.getValues()
.map(function(row) {
return [row.join(' ')];//join Each row
});
sheet.getRange(2, 4, jointRowsArray.length, 1).setValues(jointRowsArray);
To Read:
Arrays
Array#join
Array#map
Range#setValues
Best Practices
var range = sheet.getRange(2,1,1,2)
var values = range.getValues()
range.clearContent()
sheet.getRange(2,1).setValue(values.join(' '));
You can pull the values into JS and then join and insert them into a single cell. You can also place this inside an iterator and do something akin to getRange(i,1,1,2). This can be triggered manually or by an edit hook.
However, this seems like the perfect fit for a single formula.
=TRANSPOSE(QUERY(TRANSPOSE(A:C),,COLUMNS(A:C)))
The formula would go on the first row in perhaps column D. JOIN functions cannot usually be arrayed in google sheets, and you would normally have to put a formula in for every row. However, here we are tricking the sheet into thinking our data is the header and displaying it accordingly.

How Can I Use setValue() Based Off Of 2 Arrays That Match Value?

I should start with letting you know that I'm an extreme novice in JS. My background is almost solely in SQL/VBA. So, any tips you could provide would be greatly appreciated, whether it's in coding or in Stack Overflow etiquette.
Problem Background:
I've got a Script that I use for sending outbound e-mails from Google Form responses, hosted within a Google Sheet and set to OnFormResponse(). Typically, I'm only asked to send back specific bits of information from the form responses within a HTML template e-mail. However, the business case I have now is that I need to look up the values from another sheet, where an adjacent column's value matches a form response value. With the value that's matched, I need to set the value of a specific column/row (F:F) within the Form Response sheet with it.
Example:
Here's a simplified version of what the Form Responses sheet looks like, along with the formula that I would typically use:
Here's what the other tab, 'Unique Databases!', looks like:
So, my understanding of JavaScript arrays is that on the Form Responses Sheet, I would load all columns (A:E in this example) into a variable, and get the values. Then, get columns A:B of 'Unique Databases!', which loads those values into another array. If that is accurate, how do you compare the index of 1 array against the index of another, and return an adjacent match?
Thanks!
You can try this function:
function dbmanager(dbname) {
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName('Unique Databases!');
var rg=sh.getDataRange();
var vA=rg.getValues();
var r='';
for(var i=0;i<vA.length;i++){
if(dbname==vA[i][0]){
r=vA[i][1];
break;
}
}
return r;
}
I'm not sure you need to use a apps script to make a comparison like this. Perhaps a much faster way would be to use a query in the sheet. Something like this maybe:
=QUERY(Sheet1!A1:B3,"SELECT B WHERE A ='"&E2&"'",0)
The first part of the query is looking up the unique databases data. The second part selects column B in the unique databases data where column A is equal to the data base name in the form responses data. Note this query goes in column F of the responses data.
Another alternative using code might be something like this. with the code below running.
function getFormData(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Sheet2');
var getRange = sheet.getRange('E2:E');
var data = getRange.getValues();
var lookup = getLookupData();
data.forEach(function(item,index){
sheet.getRange(index + 2 , 6).setValue(lookup[item])
})
}
function getLookupData() {
var obj = {};
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Sheet1');
var range = sheet.getRange('A2:B6');
var data = range.getValues();
data.forEach(function(item){
obj[item[0]] = item[1];
})
Logger.log(obj)
return obj;
}

Google apps script getRange() range not found error

I feel like I'm going about this in all the wrong way. I'm trying to automate some of my workload here. I'm cleaning up spreadsheets with 4 columns (A-E), 2000+ rows. Column B contains website URLs, column D contains the URL's business name, generated from another source.
Sometimes the tool doesn't grab the name correctly or the name is missing, so it populates the missing entries in column D with "------" (6 hyphens). I've been trying to make a function that takes an input cell, checks if the contents of the cell are "------", and if it is the function changes the contents of the input cell to the contents of the cell two columns to the left (which is generally a website url). This is what I've come up with.
function replaceMissing(input) {
var sheet = SpreadsheetApp.getActiveSheet();
//sets active range to the input cell
var cell = sheet.getRange('"' + input + '"');
//gets cell to fill input cell
var urlCell = sheet.getRange(cell.getRow(), cell.getColumn() - 2);
//gets contents of input cell as String
var data = cell.getValue();
//gets contents of urlCell as String
var data2 = cell.getValue();
//checks if input cell should be replaced
if (data === "------") {
//set current cell's value to the value of the cell 2 columns to the left
cell.setValue(data2);
}
}
When I attempt to use my function in my sheet, the cell is returning the error
Error Range not found (line 4).
I'm assuming, based on similar questions people have asked, that this is how you use the A1 notation of the function with an argument. However, that doesn't seem to be the case, so I'm stuck. I also don't think my solution is very good period.
1) It's somewhat ambiguous in GAS documentation, but custom functions have quite a few limitations. They are better suited for scenarios where you need to perform a simple calculation and return a string or a number type value to the cell. While custom functions can call some GAS services, this practice is strongly discouraged by Google.
If you check the docs for the list of supported services, you'll notice that they support only some 'get' methods for Spreadsheet service, but not 'set' methods https://developers.google.com/apps-script/guides/sheets/functions
That means you can't call cell.setValue() in the context of a custom function. It makes sense if you think about it - your spreadsheet can contain 1000s of rows, each with its own custom function making multiple calls to the server. In JavaScript, every function call creates its own execution context, so things could get ugly very quickly.
2) For better performance, use batch operations and don't alternate between read / write actions. Instead, read all the data you need for processing into variables and leave the spreadsheet alone. After processing your data, perform a single write action to update values in the target range. There's no need to go cell by cell when you can get the entire range using GAS.
Google Apps Script - best practices
https://developers.google.com/apps-script/guides/support/best-practices
Below is a quick code example that runs onOpen and onEdit. If you need more flexibility in terms of when to run the script, look into dynamically-created triggers https://developers.google.com/apps-script/reference/script/script-app
Because your spreadsheets have lots of rows, you may hit the execution quota anyway - by using triggers you can work around the limitation.
Finally, if a cell containing '----' is a rare occurrence, it might be better to create another array variable with new values and row numbers to update than updating the entire range.
Personally, I think the single range update action would still be quicker, but you could try both approaches and see which one works best.
function onOpen(){
test();
}
function onEdit() {
test();
}
function test() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('yourSheetName');
//range to replace values in
var range = sheet.getRange(2, 4, sheet.getLastRow() - 1, 1);
//range to get new values from
var lookupRange = range.offset(0, -2);
//2d array of values from the target range
var values = range.getValues();
//2d array of values from the source range
var lookupValues = lookupRange.getValues();
//looping through the values array and checking if array element meets our condition
for (var i=0; i < values.length; i++) {
values[i][0] = (values[i][0] == '------') ? lookupValues[i][0] : values[i][0];
}
// one method call to update the range
range.setValues(values);
}

Categories