I have a simple loop I want to turn into an arrow function
The function deletes all rows from the mainArray when there is a match in columns B or C with data in searchArray
removeROW_LOOP() works but removeROW_ARROW() does not
I get the error when running removeROW_ARROW() Exception: The number of columns in the data does not match the number of columns in the range. The data has 14 but the range has 21.
Thanks for any assistance with this
My data looks like
mainArray
searchArray
results
Loop (works)
function removeROW_LOOP() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
const searchArray = ss.getSheetByName('Helper_(ignore)').getDataRange().getDisplayValues().flat();
const mainArray = ss.getSheetByName('Connections').getDataRange().getDisplayValues();
let result = [];
for (var i = 0; i <= searchArray.length-1; i++) {
result = removeROW(mainArray, searchArray[i]);
}
const sht = ss.getSheetByName('AAA');
sht.clear()
sht.getRange(1,1, result.length, result[0].length).setValues(result);
}
My attempt at arrow function (does not work)
function removeROW_ARROW() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
const searchArray = ss.getSheetByName('Helper_(ignore)').getDataRange().getDisplayValues().flat();
const mainArray = ss.getSheetByName('Connections').getDataRange().getDisplayValues();
const result = searchArray.map(s => removeROW(mainArray, s));
const sht = ss.getSheetByName('AAA');
sht.clear()
sht.getRange(1,1, result.length, result[0].length).setValues(result);
}
and
function removeROW(array, item) {
array = array.filter(a => a[1]!=item && a[2]!=item);
return array
}
I don't think the problem is function vs. arrow function. Your refactor actually changes the behavior significantly.
removeROW_LOOP() will reset result upon each iteration with the newly filtered array. However, in removeROW_ARROW(), result will be an array of arrays. Specifically, each item in the result will be a filtered version of mainArray with only one searchArray entry filtered out of it.
I think you want somthing along these lines:
function removeUPDATED() {
// ...
const result = mainArray.filter(x => !searchArray.includes(x[0]) && !searchArray.includes(x[1]));
// ...
}
I might have included a bug there, but I think it demonstrates the approach you want.
Related
I got this these values.
And I want to have this result.
So I made the following test code and tried it to the first cell.
function test2() {
const ss = SpreadsheetApp.getActive();
const sheet = ss.getSheetByName("richText3");
const range1 = sheet.getRange("A1");
const text1 = range1.getValue();
Logger.log(text1);
const re = new RegExp(/\([ a-zA-Z\/']*\)\?/dg);
const redBold = SpreadsheetApp.newTextStyle().setBold(true).setForegroundColor('red').build();
let array;
while ((array = re.exec(text1)) !== null) {
const [start, end] = array.indices[0];
const richTxtValBlder = SpreadsheetApp.newRichTextValue()
.setText(text1)
.setTextStyle(start, end, redBold)
.build();
range1.setRichTextValue(richTxtValBlder);
}
}
After first try, I got this result.
I checked the Reference Document again and I found this comment.
setText(text) : Sets the text for this value and clears any existing text style.
When creating a new Rich Text value, this should be called before setTextStyle()
I found that I should call .setText() once and call .setTextStyle() multiple times.
But the problem is .setTextStyle() should be called programmatically according to the number of patterns in each cell and I cannot find how to do it programmatically.
Each cell may have 0 to 10 patterns and I don't want to make 10 different richTExtValueBuilder which only differ in the number of .setTextStyle() calls.
Do you have any different ideas ?
Modification points:
In your script, only cell "A1" is used, and also the 1st match is used. I thought that this might be the reason for your issue.
In order to achieve your goal, I retrieve the values from column "A". And also, I use matchAll instead of exec.
When these points are reflected in your script, how about the following modification?
Modified script:
function test2() {
const ss = SpreadsheetApp.getActive();
const sheet = ss.getSheetByName("richText3");
const range1 = sheet.getRange("A1:A" + sheet.getLastRow());
const re = new RegExp(/\([ a-zA-Z\/']*\)\?/g);
const redBold = SpreadsheetApp.newTextStyle().setBold(true).setForegroundColor('red').build();
const richTextValues = range1.getRichTextValues();
const res = richTextValues.map(([a]) => {
const array = [...a.getText().matchAll(re)];
if (array) {
const temp = a.copy();
array.forEach(a => temp.setTextStyle(a.index, a.index + a[0].length, redBold));
return [temp.build()];
}
return [a];
});
range1.setRichTextValues(res);
}
Testing:
When this script is run, the following result is obtained.
From:
To:
References:
map()
setRichTextValues(values)
It looks like you need to call .setText() once, .setTextStyle() multiple times, and .build() once, e.g. change your while loop. Untested code:
let richTxtValBlder = SpreadsheetApp.newRichTextValue().setText(text1);
while ((array = re.exec(text1)) !== null) {
const [start, end] = array.indices[0];
richTxtValBlder = richTxtValBlder.setTextStyle(start, end, redBold);
}
richTxtValBlder = richTxtValBlder.build();
range1.setRichTextValue(richTxtValBlder);
I made files with table. On those table there is some datas that are changing according to each files. So I created a file where there is a table that sum all of the others tables that are in others files.
First i made my code that sum tables of two files :
function juneFunction() {
var sheet_origin1=SpreadsheetApp
.openById("ID1")
.getSheetByName("Calcul/Machine");
var sheet_origin2=SpreadsheetApp
.openById("ID2")
.getSheetByName("Calcul/Machine");
var sheet_destination=SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
//define range as a string in A1 notation
var range1="BU4:CF71"
var values1=sheet_origin1.getRange(range1).getValues();
var values2=sheet_origin2.getRange(range1).getValues();
var destinationrange=sheet_destination.getRange("C3:N70");
//Here you sum the values of equivalent cells from different sheets
for(var i=0; i<values1.length;i++)
{
for(var j=0; j<values1[0].length;j++)
{
sum=values1[i][j]+values2[i][j];
destinationrange.getCell(i+1,j+1).setValue(sum);
}
}
}
This code is working perfectly.
But my goal is to sum 26 tables of 26 files.
So I tried to do it with 3 files, here is the code :
function juneFunction() {
var sheet_origin1=SpreadsheetApp
.openById("ID1")
.getSheetByName("Calcul/Machine");
var sheet_origin2=SpreadsheetApp
.openById("ID2")
.getSheetByName("Calcul/Machine");
var sheet_origin3=SpreadsheetApp
.openById("ID3")
.getSheetByName("Calcul/Machine");
var sheet_destination=SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
//define range as a string in A1 notation
var range1="BU4:CF71"
var values1=sheet_origin1.getRange(range1).getValues();
var values2=sheet_origin2.getRange(range1).getValues();
var values3=sheet_origin3.getRange(range1).getValues();
var destinationrange=sheet_destination.getRange("C3:N70");
//Here you sum the values of equivalent cells from different sheets
for(var i=0; i<values1.length;i++)
{
for(var j=0; j<values1[0].length;j++)
{
for(var k=0; k<values1[0].length;k++){
sum=values1[i][j][k]+values2[i][j][k]+values3[i][j][k];
destinationrange.getCell[[i+1,j+1,k+1]].setValues(sum);
}
}
}
}
Here I have this error : TypeError : Cannot read properly 'setValues' of undefined. This error happen here :
destinationrange.getCell[[i+1,j+1,k+1]].setValues(sum);
I think that the error is coming from the .getCell but i dont know why ...
Range.getCell() takes two arguments: the row offset and the column offset. You are handing it just one argument: an array of three numbers.
You should not be using Range.getCell() in the first place. Instead, use Array.map() to get the sheets, Array.forEach() to iterate over them, and finally Range.setValues() to write all results in one go, like this:
function sumRangeAcrossManySpreadsheets() {
const spreadSheetIds = ['id1', 'id2', 'id3',];
const sheetName = 'Calcul/Machine';
const rangeA1 = 'BU4:CF71';
const targetRange = SpreadsheetApp.getActiveSheet().getRange('C3');
const sheets = spreadSheetIds.map(id => SpreadsheetApp.openById(id).getSheetByName(sheetName));
const result = [];
sheets.forEach((sheet, sheetIndex) => {
if (!sheet) {
throw new Error(`There is no sheet ${sheetName} in spreadsheet ID '${spreadSheetIds[sheetIndex]}'.`);
}
const values = sheet.getRange(rangeA1).getValues();
values.forEach((row, rowIndex) => row.forEach((value, columnIndex) => {
if (!result[rowIndex]) {
result[rowIndex] = new Array(row.length).fill(0);
}
result[rowIndex][columnIndex] += Number(value) || 0;
}));
});
targetRange.offset(0, 0, result.length, result[0].length).setValues(result);
}
I am working with different 2d arrays (rows and columns, google app script). For instance (r = row, c = column):
var array1 =
[[r1c1, r1c2, r1c3],
[r2c1, r2,c2, r2c2]]
var array2 =
[[r1c4, r1c5],
[r2c4, r2c5],
[r3c4, r3c5]]
and I want to have it like that:
var array1and2 =
[[r1c1, r1c2, r1c3, r1c4, r1c5],
[r2c1, r2c2, r2c3, r2c4, r2c5],
[empty, empty, empty, r3c4, r4c5]
]
It doesn't have to be empty but as already said I want to display it in google spreadsheets. The second array should be in the first empty row and column next to the first array.
I hope it is understandable and thank you very much for you help!
Somethink like this you can do:
var array2 = [['r1c4', 'r1c5'],
['r2c4', 'r2c5'],
['r3c4', 'r3c5']];
var array1 = [['r1c1', 'r1c2', 'r1c3'],
['r2c1', 'r2,c2', 'r2c2']];
var result = array2.map((elem, i)=>[...(array1[i] || new Array(array1[0].length).fill('empty')), ...elem]);
console.log(result);
I'm reading your question as being about not just how to create an amalgamated array but actually to manipulate the data in situ, so I'd approach it something like this (assuming that array 1 lives on sheet1 and array 2 lives on sheet2:
function translateArray(){
var sh = SpreadsheetApp.getActiveSpreadsheet();
var ss = sh.getSheetByName("Sheet1");
var os = sh.getSheetByName("Sheet2");
var range1 = ss.getRange("A1:C2");//2 rows 3 columns
var range1Width = range1.getValues()[0].length;//checks length of first row - the width
var range2 = os.getRange("A1:B3");//3 rows 2 columns
var data = range2.getValues();
ss.getRange(1,range1Width,data.length, data[0].length).setValues(data);
}
This code copies the data in sheet2 to sheet 1 alongside it.
use [1] instead of [0] in range1Width if you have header data wider than the real data.
A less orthodox approach:
function merge2Arrays() {
var array1 = [['r1c1', 'r1c2', 'r1c3'],['r2c1', 'r2,c2', 'r2c3']];
var array2 = [['r1c4', 'r1c5'],['r2c4', 'r2c5'],['r3c4', 'r3c5']];
const ss=SpreadsheetApp.getActive();
const sh=ss.insertSheet('xxxxx');
sh.getRange(1,1,array1.length,array1[0].length).setValues(array1);
sh.getRange(1,array1[0].length+1,array2.length,array2[0].length).setValues(array2);
const a=sh.getDataRange().getValues();
ss.deleteSheet(sh);
ret a;
}
function merge3Arrays() {
var array1 = [['r1c1', 'r1c2', 'r1c3'],['r2c1', 'r2,c2', 'r2c3']];
var array2 = [['r1c4', 'r1c5'],['r2c4', 'r2c5'],['r3c4', 'r3c5']];
var array3 = [["r1C6"],["r2c6"],["r3c6"],["r4c6"],["r5c6"],["r6c6"]];
const ss=SpreadsheetApp.getActive();
const sh=ss.insertSheet('xxxxx');
sh.getRange(1,1,array1.length,array1[0].length).setValues(array1);
sh.getRange(1,array1[0].length+1,array2.length,array2[0].length).setValues(array2);
sh.getRange(1,array1[0].length + array2[0].length + 1,array3.length,array3[0].length).setValues(array3);
const a=sh.getDataRange().getValues();
ss.deleteSheet(sh);
SpreadsheetApp.getUi().showModelessDialog(HtmlService.createHtmlOutput(JSON.stringify(a)), "Display Array");
}
This is my array from which I want to find max.
number = {"abc": [43,4,34,34,6,444], "dsfsd":[324,324,32,43,34,2] };
console.log((Math.max(...number[abc]));
Here the output is 444, and it's working fine. But now, I want to select max from selected indexs. I am storing those indexes in this array.
available = [0,2,3];
Now, index 0,2,3 of number[abc] are 43,34, 6
And I want 43 to be displayed, because it is the max from selected indexes.
How can I do it?
Map the indicies to the values, and then call Math.max on those values:
const number = {"abc": [43,4,34,34,6,444], "dsfsd":[324,324,32,43,34,2] };
const available = [0,2,3];
const availableValues = available.map(i => number.abc[i]);
console.log(Math.max(...availableValues));
You can create a reusable function that will have a custom logic to check the highest number instead of using Math.max(). Using reusable function will help you to scale the code without duplicating the logic.
var available = [0,2,3];
var number = {"abc": [43,4,34,34,6,444], "dsfsd":[324,324,32,43,34,2] };
function getHighest(available, number){
var index = available[0];
var highest = number[index];
for(var i=1; i<available.length; i++){
index = available[i];
if(highest < number[index]){
highest = number[index];
}
}
return highest;
}
var highest = getHighest(available, number['abc']);
console.log(highest);
You can also achview this by filtering the number.abc array.
const number = {"abc": [43,4,34,34,6,444], "dsfsd":[324,324,32,43,34,2] };
const available = [0,2,3];
const filtered = number.abc.filter((num, idx) => available.includes(idx));
console.log(Math.max(...filtered));
I'm running into trouble concatenating "two" arrays during recursion. I start with an empty array and during the function I concatenate it with the new array. The concatenated version becomes the array which is again concatenated with another array within the same function. Following is what I want:
var oldArray = [];
for (i=0; i<10; i++) {
var newArray = [i];
oldArray = oldArray.concat([newArray]);
}
document.write(oldArray);
produces: [0,1,2,3,4,5,6,7,8,9]
But if I use my actual production code I get a two steps forward, one step back sort of effect.
written in angular. go() is the wrapping function
var accounts = [];
$http.get('/api/account/', {params:{page:page}}).then(function (result) {
var count = result.data.count,
totalPages = Math.ceil(count/10),
results = result.data.results;
accounts = accounts.concat(results);
if (page < totalPages) {
page++;
go(page);
}
return accounts;
});
produces: [0,1,0,2,1,3,2,4,3,5,4,6,5,7,6,8,7,9]