forEach only posting last entry in 1d array javascript - javascript

I am trying to post all entries in a 1d array to a column in a google sheets. The array is the product of filtering two larger arrays and returning the names that do not appear on both lists.
below is an example of the generated array.
unPub = [fake name, test1, test2, test3]
Here is the code I have written so far:
function unPublished(){
const q3 = SpreadsheetApp.openById("1111111111");
const packAllergies = q3.getSheetByName("PACK_ALLERGIES");
const packSrch = packAllergies.getRange("D5:D" + packAllergies.getLastRow()).getValues().flat();
const allergyNames = allergy.getRange("A2:A" + allergy.getLastRow()).getValues().flat();
var unPub = (packSrch.filter(e => !allergyNames.includes(e)));
var sRow = allergy.getLastRow()+1
if (unPub.length > 0){
unPub.forEach(e => allergy.getRange(sRow,1).setValue(e));
}
}
I have tried a for loop to iterate over the list as well as forEach and still only get the last entry of the unPub array to post in the defined range.
How can I get each element in the array to post to the column starting at sRow?

Explanation:
You don't need a loop to set values to the sheet. In fact it is not recommended, see best practices.
You need the following two steps:
transform your row array into a column array:
unPub=unPub.map(v=>[v]);
because you want to set the data into a column.
remove the forEach loop and directly pass the values with a single line:
allergy.getRange(sRow,1,unPub.length,1).setValues(unPub);
Solution:
function unPublished(){
const q3 = SpreadsheetApp.openById("1111111111");
const packAllergies = q3.getSheetByName("PACK_ALLERGIES");
const packSrch = packAllergies.getRange("D5:D" + packAllergies.getLastRow()).getValues().flat();
const allergyNames = allergy.getRange("A2:A" + allergy.getLastRow()).getValues().flat();
var unPub = (packSrch.filter(e => !allergyNames.includes(e)));
var sRow = allergy.getLastRow()+1;
unPub=unPub.map(v=>[v]);
allergy.getRange(sRow,1,unPub.length,1).setValues(unPub);
}
Issue with your approach:
Besides performance issues which I described in the explanation section, your forEach loop does not work because you overwrite every value on the same cell. If you see, this part allergy.getRange(sRow,1) does not change in the for loop, given that sRow is constant.
If you want your approach to work, then you need to introduce an iterator i in the forEach loop and use that to iterate through the cells:
unPub.forEach((e,i) => allergy.getRange(sRow+i,1).setValue(e));
function unPublished(){
const q3 = SpreadsheetApp.openById("1111111111");
const packAllergies = q3.getSheetByName("PACK_ALLERGIES");
const packSrch = packAllergies.getRange("D5:D" + packAllergies.getLastRow()).getValues().flat();
const allergyNames = allergy.getRange("A2:A" + allergy.getLastRow()).getValues().flat();
var unPub = (packSrch.filter(e => !allergyNames.includes(e)));
var sRow = allergy.getLastRow()+1
if (unPub.length > 0){
unPub.forEach((e,i) => allergy.getRange(sRow+i,1).setValue(e));
}
}
but I really recommend you the first approach I mentioned.

Related

Looping through Json Arrays from API

I am currently trying to loop and add each element of the quantity of each bid and ask which appears as bids[0][1], bids[1][1], bids[1][2] and add each element in the Array sequence. Any help will be greatly appreciated.
I tried adding the array but I am unable to turn the Json data to code here. Below is the API reference
I tried the code:
const binanceTrade = JSON.parse(data)
const bidsQuantity = binanceTrade.bids[0][1]
const askQuantity = binanceTrade.asks[0][1]
for(var i = 0; i<bidsQuantity.length; i++){
var j = 1;
bidsQuantity = bidsQuantity.push(binanceTrade.bids[j][1])
console.log(bidsQuantity)
j++
//bids[0][1] + bids[1][2]
}
And the public Binance API route for reference: https://api.binance.com/api/v3/depth?symbol=BTCUSDT&limit=5
You can use reduce() to loop over the bids and asks arrays, totaling the second element of each item.
const binanceTrade = JSON.parse(data);
const bidsQuantity = binanceTrade.bids.reduce((acc, [_, quantity]) => acc + quantity, 0);
const asksQuantity = binanceTrade.asks.reduce((acc, [_, quantity]) => acc + quantity, 0);
One approach would be to use map
const bidsQuantity = [];
binanceTrade.bids.map((bids) => {
bidsQuantity.push(bids[1]);
});
You can do this again in a similar way for the asks

How to load Named Ranges into array?

I have defined a couple of named ranges in my sheet following the naming schema fieldName1, fieldName2, fieldName3 etc. They are located in different places in my sheet.
I am trying to load those fieldName(s) into an array now, but I encounter an issue, as
getRange("fieldName1").getValue() is expecting a "String" of fieldName rather than a getRange(fieldName[c]).getValue() code.
Here is what I have so far:
const noFields = wsS.getRange("noFields").getValue()
var fieldName = []
for (var c = 1; c <= noFields; ++c) {
fieldName[c] = wsS.getRange(fieldName[c]).getValue()
}
noFields is a variable of how many fieldNameX named ranges I have to load, as I didn't think of a better way to load them still.
Please help, thank you.
So you want all the named ranges that start with "fieldName", and want to get all their values as an array.
You can get all the named ranges from the spreadsheet using .getActive(). However, they will not be sorted as expected.
If you simply want an array with all the values from named ranges, do:
var ss = SpreadsheetApp.getActive()
var values = ss.getNamedRanges()
.map(namedRange => namedRange.getRange().getValue())
If you only want the values from those ranges following the naming schema, do:
const PREFIX = "fieldName" // case sensitive
var values = ss.getNamedRanges()
.filter(namedRange => namedRange.getName().startsWith(PREFIX))
.map(namedRange => namedRange.getRange().getValue())
You might also want to sort the named ranges by the number after "fieldName", just in case they were entered in different order, right? But that complicates things a little since the names are strings.
One way of sorting the array is by constructing an associative array with the range names:
const PREFIX = "fieldName" // case sensitive
var ss = SpreadsheetApp.getActive()
var namedRanges = ss.getNamedRanges()
.filter(namedRange => namedRange.getName().startsWith(PREFIX))
var associativeArray = []
for (var namedRange of namedRanges) {
associativeArray[namedRange.getName().substring(PREFIX.length)] =
namedRange.getRange().getValue()
}
var values = associativeArray.flat()
Or with a custom comparator:
const PREFIX = "fieldName" // case sensitive
var ss = SpreadsheetApp.getActive()
var values = ss.getNamedRanges()
.filter(namedRange => namedRange.getName().startsWith(PREFIX))
.map(namedRange => [
Number(namedRange.getName().substring(PREFIX.length)),
namedRange.getRange().getValue() ])
.sort((itemA,itemB) => itemA[0] - itemB[0])
.map(item => item[1])
In any case, you will end up with an array of values that you can use.
Get all named ranges in to an array
SpreadsheetApp.getActive().getNamedRanges();
Class namedRanges
function namedRangesInAnArray() {
const ss = SpreadsheetApp.getActive();
const names = ['r1','r2'.....];//array of names to get
return SpreadsheetApp.getActive().getNamedRanges().filter(r => ~names.indexOf(r.getName()));
}

Finding index of an element in a specified array

I'm in the making of a google sheets app script where I want to check if a value from one cell is in an array of values, then find what the index is so I can direct my function to that cell.
I'm at the point where I have my array in a variable called distArray, and I want to check if "id" is in that array.
Here's the code to better visualize:
function logs() {
let app = SpreadsheetApp
let dest = app.getActiveSpreadsheet().getSheetByName("Baza Danych");
let lastrow = dest.getLastRow();
let destArr = dest.getRange(2, 1, lastrow).getValues();
let id = app.getActiveSpreadsheet().getSheetByName("Zgloszenia").getRange(6, 2).getValue();
let position = destArr.indexOf(id);
Logger.log(id)
Logger.log(destArr)
Logger.log(position)
}
And here is the output I get.
My problem is that no matter what the value of "id" is, the index is either -1 or 0 meaning the value either is not in the array or is in the first cell.
Try to add .flat() at the end of the line:
let destArr = dest.getRange(2, 1, lastrow).getValues();
This way:
let destArr = dest.getRange(2, 1, lastrow).getValues().flat();
Explanation:
The method getValues() gives you a 2d array [[1],[2],[3],...].
The flat() method converts a 2d array into an ordinary flat array [1,2,3,...].
After that you will able to use array.indexOf(element) to get an index of the element in the array.
Description
Yuri's solution is a good example if you don't want to know which element of the array contains the value your looking for. But if you need to know which row of the array contains the value the following example shows how to search a 2D array.
Script
function find() {
try {
let a = [['a','b'],['c','d'],['e','f'],['g','h']];
let b = "f";
let c = a.findIndex( d => d.indexOf(b) >= 0 );
console.log("c = "+c);
}
catch(err) {
console.log(err);
}
}
7:51:23 AM Notice Execution started
7:51:24 AM Info c = 2
7:51:23 AM Notice Execution completed
Reference
Array.findIndex()
Array.indexOf()

How to retrieve a particular row from an array in google apps script?

I am using google sheets quite a lot, but now I am trying to use google apps script to get and update dynamic data retrieved from formulas into a static table.
So, I have a sheet called 'dynamique', with formulas retrieving, filtering and sorting data from other spreadsheets.
I want to be able to work on this data, so I am trying to create a button which would copy all the values from the 'dynamique' sheet into another sheet called 'statique'. That is, I want a formula which would check if the values from the column C of the 'dynamique' sheet are in the column C of the 'statique' sheet. And if the values aren't there, I want the script to copy them. (columns A and B are empty)
I've managed to get my script to work for one column, but now, I want to copy the whole line.
For example, if the value in dynamique!C10 can't be found in statique!C:C, my script writes the value of dynamique!C10 in the first empty cell of the column statique!C:C. But I want it to write dynamique!C10:J10 into my destination sheet (say it's going to be maybe statique!C8:J8).
Here is my code, working for only one cell.
function dynamicToStatic() {
var dynSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("dynamique");
var staSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("statique");
var dynLength = dynSheet.getRange("C1:C").getValues().filter(String).length;//.getLastRow();
var staLength = staSheet.getRange("C1:C").getValues().filter(String).length;
var staRange = staSheet.getRange(6,3,staLength-1);
var staValues = staRange.getValues();
var rangeToCheck = dynSheet.getRange(6,3,dynLength-1,8);
var valuesToCheck = rangeToCheck.getValues();
var numRows = rangeToCheck.getNumRows();
var staNumRows = staRange.getNumRows();
for (i = 0; i<= numRows; i++) {
var row = valuesToCheck[i];
var index = ArrayLib.indexOf(staValues , -1 , row);
if (index == -1) {
//if (staValues.indexOf(row) != -1) {
staSheet.getRange(i+6,3,1,8).setValues(row);
}
}
var timestamp = new Date();
staSheet.getRange(4,3).setValue('List updated on the: '+timestamp);
}
Now I can't manage to retrieve the whole line of the array, so as to be able to copy it using range.setValues(). I always get error messages.
Any help would be more than appreciated...
function gettingFullRows() {
const ss=SpreadsheetApp.getActive();
const sh=ss.getSheetByName('Sheet1');
const shsr=2;//data startrow
const vA=sh.getRange(shsr,1,sh.getLastRow()-shsr+1,sh.getLastColumn()).getValues();
let html='';
vA.forEach((r,i)=>{
html+=Utilities.formatString('<br />Row:%s is %s',i+shsr,r.join(','));
});
SpreadsheetApp.getUi().showModelessDialog(HtmlService.createHtmlOutput(html), "Row");
}
So i did some re-writing to your code and made some comments in there. I hope this will make some things clear.
Array's are 0 indexed. So if the value is NOT found in the .indexOf then it would return -1. Also (for speed) i first push all the result to a array and then set the array in one "action" this saves a lot of time. The calls to and from a sheet takes the most time.
For the conversion to a 1d array i used spread operator
See this link for difference in const / var / let
The timestamp string i updated with the use of Template literals
If you have some questions, shoot! (also i did not test this ofcourse)
function dynamicToStatic() {
const dynSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("dynamique");
const staSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("statique");
const dynValues = dynSheet.getRange(1,3,dynSheet.getLastRow(),8).getValues();
//This is a 2d array
const staRaw = staSheet.getRange(6, 3, staSheet.getLastRow()).getValues();
//Convert to 1d array, for the indexoff later on, this way it is easier.
const staValues = [].concat(...staRaw);
//to store the output, as a 2d array, inside the if you see i push it in as array so you have the 2d array for the setValues.
const output = [];
for (let i = 0; i < dynValues.length; i++){
//i = the index of the array (row) inside the array of rows, the 0 would be the values of column C.
if (staValues.indexOf(dynValues[i][0]) >= 0){
output.push([dynValues[i]]);
}
}
//Start by the lastrow + 1, column C(3), ouput is a array of arrays(rows), then get the [0].lengt for the columns inside the row array.
staSheet.getRange(staSheet.getLastRow()+1, 3, output.length, output[0].lenght).setValues(output);
const timestamp = new Date();
staSheet.getRange(4,3).setValue(`List updated on the: ${timestamp}`);
}

How to find max number from an array, but from selected elements only in javascript

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

Categories