Dobbelt foreach function - google-apps-script

I'm trying to import data from a JSON array to Google sheet. However, its a nested array which complexity exceeds my abilities.
This is My JSON (I have deleted irrelevant objects):
{"result":[
{"MyEnergyData_MarketDocument":{
"TimeSeries"[{
"Period":[{
"Point":[
{"position":"1","quantity":"0.489"},
{"position":"2","quantity":"7.57"},
{"position":"3","quantity":"0.131"}…] ETC
"Point":[
{"position":"1","quantity":"0.136"},
{"position":"2","quantity":"0.131"},
{"position":"3","quantity":"0.134"}…] ETC
“Point” … ETC
This is my GAS code
var meterdatajson = Utilities.jsonParse(meterdata);
var meterdataArray = meterdatajson['result'];
var arrayProperties = [];
meterdataArray.forEach(function(el) {
arrayProperties.push(
el.MyEnergyData_MarketDocument.TimeSeries[0].Period[0].Point.map(p => [
p.position,
p.quantity
]));});
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Forbrug");
sheet.getRange(1, 1, arrayProperties.length, arrayProperties[0].length).setValues(arrayProperties);
Secondly, when the “quantity” gets imported to sheets, it converts to a date. Makes no sense.
I have tried with a double "foreach" function. But regardless, I only get the first interval of "Point" and not the rest.
All the “position” and “quantity” need to be pushed to the same array in the same lever.

Try this
function dumpData() {
let testData = {"result":[
{"MyEnergyData_MarketDocument":
{ "TimeSeries":[
{ "Period":[
{ "Point":[
{"position":"1","quantity":"0.489"},
{"position":"2","quantity":"7.57"},
{"position":"3","quantity":"0.131"}]
},
{ "Point":[
{"position":"1","quantity":"0.136"},
{"position":"2","quantity":"0.131"},
{"position":"3","quantity":"0.134"}]
}
]
}
]
}
}
]
};
let arrayProperties = [];
let period = testData.result[0].MyEnergyData_MarketDocument.TimeSeries[0].Period.map( point => {
point.Point.forEach( position => arrayProperties.push([position.position,position.quantity]));
}
);
console.log(arrayProperties);
}
8:03:34 AM Notice Execution started
8:03:35 AM Info [ [ '1', '0.489' ],
[ '2', '7.57' ],
[ '3', '0.131' ],
[ '1', '0.136' ],
[ '2', '0.131' ],
[ '3', '0.134' ] ]
8:03:35 AM Notice Execution completed

Related

Filter the output of Logger.log to only show specific parts

I have a simple function (shown below) that lists all the existing schema values for a Google Workspace domain. I would like to only retrieve specific items.
This is my function:
function listSchema() {
const ss = SpreadsheetApp.getActive();
const sheet = ss.getSheetByName("Domain Schema");
const schemaSafeName = sheet.getRange(4,2).getValue();
try{
Logger.log(AdminDirectory.Schemas.get('my_customer',schemaSafeName));
}
catch(error){
const {code, message} = error.details;
if(code === 400 || code === 404 || code === 409 || code === 412){
console.log("Error 400 or 404 or 409 or 412");
}
else {
console.log(`${code} - ${message}`);
}
}
}
The Logger.log (prettified and commented) output is as follows:
{
etag="eObIY8zBQ9kCV0dcsWouNipdJvA0", // DON'T WANT THIS
displayName=Test Schema Group,
fields=
[
{
fieldId=87mf1ADjQwuyvPaKPIa2uw==, // DON'T WANT THIS
readAccessType=ADMINS_AND_SELF,
displayName=Test Field1,
fieldType=BOOL,
fieldName=Test_Field1,
etag="eObIY8zBQ9kCV03CoXVqpKE7PI", // DON'T WANT THIS
multiValued=false,
kind=admin#directory#schema#fieldspec // DON'T WANT THIS
},
{
multiValued=false,
fieldName=Test_Field2,
displayName=Test Field2,
etag="eObIY8zBQ9kCSggLJvV_R8EVdhq8S0O3A", // DON'T WANT THIS
fieldType=BOOL,
fieldId=7pRb-wPQQE2cyVvniaJA==, // DON'T WANT THIS
readAccessType=ADMINS_AND_SELF,
kind=admin#directory#schema#fieldspec // DON'T WANT THIS
}
],
schemaId=tAq6fq92Qn-6egbHjFFkug==, // DON'T WANT THIS
kind=admin#directory#schema, // DON'T WANT THIS
schemaName=Test_Schema_Group133
}
`
Note: every time I run it the order is different, so I guess using something to retrieve parts of the output using some sort of index would not work. But I may be wrong.
I'd like to get a "filtered" Logger.log output as follows (edited to show nested schema):
`
{
[
{
displayName=Test Schema Group 1,
fields=
[
{
readAccessType=ADMINS_AND_SELF,
displayName=Test Field1,
fieldType=BOOL,
fieldName=Test_Field1,
multiValued=false,
},
{
multiValued=false,
fieldName=Test_Field2,
displayName=Test Field2,
fieldType=BOOL,
readAccessType=ADMINS_AND_SELF,
}
],
schemaName=Test_Schema_Group1
},
{
displayName=Test Schema Group 2,
fields=
[
{
readAccessType=ADMINS_AND_SELF,
displayName=Test Field1,
fieldType=BOOL,
fieldName=Test_Field1,
multiValued=false,
},
{
multiValued=false,
fieldName=Test_Field2,
displayName=Test Field2,
fieldType=BOOL,
readAccessType=ADMINS_AND_SELF,
}
],
schemaName=Test_Schema_Group2
}
]
}
`
So, basically, I'd like the output to ignore the values of "fieldId", "kind", and "etag".
Additionally - and ideally -, I'd also like to have the option to only the value, without the name, such as: "Test Schema Group" instead of "displayName=Test Schema Group". This way I can then push those values to a Google Sheet, with a pre-defined title for each row, without the need to filter it (using the Index function).
If I understand how to do this, I can then adapt it to get only any given value. I hope...
Thanks in advance for any help.
Since you need to handpick what you want anyway, you might as well do it like this:
var data = {
etag: "eObIY8zBQ9kCV0dcsWouNipdJvA0",
displayName: "Test Schema Group",
fields: [{
fieldId: "87mf1ADjQwuyvPaKPIa2uw==",
readAccessType: "ADMINS_AND_SELF",
displayName: "Test Field1",
fieldType: "BOOL",
fieldName: "Test_Field1",
etag: "eObIY8zBQ9kCV03CoXVqpKE7PI",
multiValued: false,
kind: "admin#directory#schema#fieldspec"
},
{
multiValued: false,
fieldName: "Test_Field2",
displayName: "Test Field2",
etag: "eObIY8zBQ9kCSggLJvV_R8EVdhq8S0O3A",
fieldType: "BOOL",
fieldId: "7pRb-wPQQE2cyVvniaJA==",
readAccessType: "ADMINS_AND_SELF",
kind: "admin#directory#schema#fieldspec"
}
],
schemaId: "tAq6fq92Qn-6egbHjFFkug==",
kind: "admin#directory#schema",
schemaName: "Test_Schema_Group133"
}
var ret = {};
if(data.schemaName == "Test_Schema_Group133"){
ret.displayName = data.displayName;
ret.schemaName = data.schemaName;
var fields = [];
for (var f of data.fields) {
var obj = {};
obj.readAccessType = f.readAccessType;
obj.displayName = f.displayName;
obj.fieldType = f.fieldType;
obj.fieldName = f.fieldName;
obj.multiValued = f.multiValued;
fields.push(obj);
}
ret.fields = fields;
}
console.log(ret);
There are alternatives with Object.keys() or Object.getOwnPropertyNames(). But they will do essentially the same...

Is it better to save data as Object of Objects or Array of Arrays of Objects into NoSQL(MongoDB) database?

I have a code like below:
export type Params = NonNullable<ConstructorParameters<typeof URLSearchParams>[0]>;
class AlphaVantage { constructor (protected readonly api: AlaphaVantage) {} }
class Forex extends AlphaVantage {
rate (from_currency: string, to_currency: string) {
return this.api.query({
function: 'CURRENCY_EXCHANGE_RATE',
from_currency,
to_currency,
});
}
}
class Stocks extends AlphaVantage {
intraday (symbol: string , outputsize: string) {
return this.api.query({
function: 'TIME_SERIES_DAILY',
symbol,
outputsize,
});
}
}
export class AlaphaVantage {
#token: string;
constructor (token: string) {
this.#token = token;
}
async query <Result = any>(params: Params): Promise<Result> {
const url = new URL('https://www.alphavantage.co/query');
const usp = new URLSearchParams(params);
usp.set('apikey', this.#token);
url.search = usp.toString();
const request = new Request(url.href);
const response = await fetch(request );
if (!response.ok) throw new Error('Response not OK');
return response.json();
}
forex = new Forex(this);
stocks = new Stocks(this);
}
export const stockServerFetch = async () => {
const YOUR_API_KEY = 'ASLDVIWXGEWFWNZG';
const alpha = new AlaphaVantage('ASLDVIWXGEWFWNZG');
const fetchedStock = await alpha.stocks.intraday('MSFT', 'compact' ).then((data: any) => {return data["Time Series (Daily)"]} );
return fetchedStock;
}
const rawSummaryKeys = ['1. open', '2. high', '3. low', '4. close', '5. volume'] as const;
type RawSummaryKey = typeof rawSummaryKeys[number];
const parsedSummaryKeys = ['open', 'high', 'low', 'close', 'volume'] as const;
type ParsedSummaryKey = typeof parsedSummaryKeys[number];
const rawToParsedSummaryKeyMapping: Record<RawSummaryKey, ParsedSummaryKey> = {
'1. open': 'open',
'2. high': 'high',
'3. low': 'low',
'4. close': 'close',
'5. volume': 'volume',
};
/** Values are parsable as numbers */
type RawSummary = Record<RawSummaryKey, string>;
/** Keys are dates in format: YYYY-MM-DD */
type DailyRawSummaries = Record<string, RawSummary>;
type ParsedSummary = Record<ParsedSummaryKey, string>;
function parseRawSummary (summary: RawSummary): ParsedSummary {
const parsed = {} as ParsedSummary;
for (const key of rawSummaryKeys) {
// If the "volume" number ever exceeds Number.MAX_SAFE_INTEGER,
// then you can switch to using BigInts
parsed[rawToParsedSummaryKeyMapping[key]] = String(summary[key]);
}
return parsed;
}
type DailySummaryEntrry = [date: string, summary: ParsedSummary];
function parseDailySummaries (summaries: DailyRawSummaries): DailySummaryEntrry[] {
const entries: DailySummaryEntrry[] = [];
for (const date in summaries) {
const rawSummary = summaries[date];
if (!rawSummary) continue;
entries.push([date, parseRawSummary(rawSummary)]);
}
console.log(entries);
return entries.sort().reverse(); // sort by newest date first
}
type stocksData = [ name : string, stock : DailySummaryEntrry[] ];
export const saveStockData = async()=> {
const json = await stockServerFetch();
const parsed = parseDailySummaries(json);
const newStockData : DailySummaryEntrry[] = [];
const newStockDataWithName : stocksData[] = [];
for (const [date, summary] of parsed) {
newStockData.push([date, summary ]);
}
newStockDataWithName.push(['MSFT' , newStockData]);
}
stockServerFetch();
saveStockData();
This code downloads stock data from AlphaVantage server. Then after some modification the result of newStockDataWithName would be like the following:
[
[
"MSFT",
[
[ "2022-03-11", [Object] ],
[ "2022-03-10", [Object] ]
]
]
]
And if I want to add more stocks together with the above code, it will be like this:
[
[
[
"AAPL",
[
[ "2022-03-11", [Object] ],
[ "2022-03-10", [Object] ]
]
]
],
[
[
"MSFT",
[
[ "2022-03-11", [Object] ],
[ "2022-03-10", [Object] ]
]
]
]
]
[Object]s are something like this:
{
open: "174.1400",
high: "175.4800",
low: "171.5500",
close: "172.1200",
volume: "90865899"
}
But I don't know if this is a good idea to save such a array into the MongoDB or it's better to convert it to an Object format like the following?
{ 'AAPL' :
{ '2022-03-11' : {
'open' : "287.9600",
'high': "289.5100",
'low' : "279.4300",
'close' : "280.0700",
'volume' : "27209330"
},
'2022-03-10' : {
'open' : "283.0200",
'high' : "286.6000",
'low' : "280.5800",
'close' : "285.5900",
'volume' : "30628012"
}
},
'MSFT' :
{ '2022-03-11' : {
'open' : "287.9600",
'high': "289.5100",
'low' : "279.4300",
'close' : "280.0700",
'volume' : "27209330"
},
'2022-03-10' : {
'open' : "283.0200",
'high' : "286.6000",
'low' : "280.5800",
'close' : "285.5900",
'volume' : "30628012"
}
}
}

Why is my Script taking so long to execute and how can I fix or get around it?

My code is hitting the maximum exceeded time for google app scripts and I'm not sure why. Previously this code would run in maybe a minute or two, and now with no changes it only just gets over half way through the data I want. Is there a way to optimise this so it takes a shorter amount of time or a way I can save the team and I variables to run the script a second time from where it finished?
{
for(var team in response.returnData.equipos)
{
if(response.returnData.equipos[team].members.length > 0)
{
var i = 0;
while(i < response.returnData.equipos[team].members.length)
{
sheetName.appendRow([response.returnData.equipos[team].name, response.returnData.equipos[team].members[i].userId]);
i++;
}
}
}
}
if(team.length > 0)
{
sheetName.getRange('D2').setValue('=NOW()');
sheetName.getRange('D1').setValue(sheetName.getRange('D2').getValue());
sheetName.getRange('D2').clear();
}```
The reason your script is slow is that on every loop you are doing an appendRow(), which is an API call, which is slow. You need to refer to the best practices document for guidance.
Now to your problem.
Judging from your code, your source data looks like something like this:
const response = {
returnData: {
equipos: [
{ name: 'team 1', members: [{ userId: 1 }, { userId: 2 }] },
{ name: 'team 2', members: [{ userId: 3 }, { userId: 4 }] },
{ name: 'team 3', members: [{ userId: 5 }, { userId: 6 }] },
],
},
};
And the outcome you are looking for to append to our sheet looks like this:
[ [ 'team 1', 1 ], 
[ 'team 1', 2 ], 
[ 'team 2', 3 ], 
[ 'team 2', 4 ], 
[ 'team 3', 5 ], 
[ 'team 3', 6 ] ] 
In order to avoid doing an appendRow() on every loop, you need to build up a 2D array that looks like the output above and then add it all with one API call.
To build the output in question, you need to run this code:
const output = response.returnData.equipos.reduce(
(acc, team) => [
...acc,
...team.members.map(member => [team.name, member.userId]),
],
[]
);
Now that you have that data in the correct format, append this to your sheet like so (this is supposing that sheetName from your code is an actual spreadsheet object and not just its name as the variable name suggests):
const lRow = sheetName.getLastRow();
sheetName
.getRange(lRow + 1, 1, output.length, output[0].length)
.setValues(output);
Let me know if this helps.

Find Text in a Named Range

I'm trying to get the row or index number of a string That is put in by the user. I retrieve the data in an array using spreadsheet.getRangeByName("Symbols").getValues(). The problem is it doesn't retrieve the data as strings it retrieves it as an array. So the result is an array of arrays. I wanted to use the function indexOf(result.getResponseText().toUpperCase()), but that doesn't work. Here is my code:
var aSymbols = spreadsheet.getRangeByName("Symbols").getValues();
Logger.log(aSymbols);
var row = aSymbols.indexOf(result.getResponseText().toUpperCase());
Logger.log(row);
And the results are:
[[ ], [XOM], [PLTR], [IBM], [VWAGY], [LIT], [ ], []]
I tried adding "[" and "]" to the search string, but that didn't work. I also tried using spreadsheet.getRangeByName("Symbols").getValues().toString(), but it returned 1 big long string. What am I missing?
Solution:
You can use findIndex() to get the index in 2D array:
var aSymbols = spreadsheet.getRangeByName("Symbols").getValues();
Logger.log(aSymbols);
var row = aSymbols.findIndex(function check(e) {return e[0] === result.getResponseText().toUpperCase()});
Logger.log(row);
Reference:
findIndex()
You could try this:
let aSymbolsArray = aSymbols.flat();
This will convert the double Array into a Array of strings.
function test(){
let aSymbols = [['' ], ['XOM'], ['PLTR'], ['IBM'], ['VWAGY'], ['LIT'], ['' ], ['']];
console.log(aSymbols);
//
// [ 'XOM' ],
// [ 'PLTR' ],
// [ 'IBM' ],
// [ 'VWAGY' ],
// [ 'LIT' ],
// [ '' ],
// [ '' ] ]
//
let aSymbolsArray = aSymbols.flat();
console.log(aSymbolsArray);
//
// [ '', 'XOM', 'PLTR', 'IBM', 'VWAGY', 'LIT', '', '' ]
//
}

Sequelize: Write multidimensional array to database

I have a JSON file with nested arrays of varying length. That is, each object has an ARR with a different number of objects.
{ "count": 200,
"objects": [
{
"id": "FIRST",
"b": "two",
"c": "three",
"ARR": [{
"aa": "onion ",
"bb": 2,
"cc": "peanuts"},
},
{
"aa": "Jam ",
"bb": 4,
"cc": "Bread"},
}],
"d":"four"
]
}, . . . on and on
I have imported the JSON data to my JavaScript file:
const data = JSON.parse(require('fs').readFileSync('./jsonfiles/objects.JSON', 'utf8'))
trim data down to the objects of interest
const objs=data.objects;
I'm using Sequelize to write this to a mysql database. I have two models: Model 1: hasMany Arr sets: Model 2: belongsTo Model1.
Writing the to table1 from Model1 works well like this:
for (var key in Objs) {
var item = Objs[key]
db.Model1.create({
modelID: item.id,
modelB: item.b,
modelC:item.c
})
}
Now, I'm trying to write ARR to the associated model and am stumped on how to do this.
I do not know how many objects will be in each ARR
Storing ARR as a JSON obj in table1 won't serve well later.
This is the function I created for our companies API. Took me a week to put together but hopefully, this helps.
exports.functionName = async (req, res) => {
const params = req.params.something;
if (!params) {
res.status(httpStatusCodes.BAD_REQUEST).send({message:'Please provide params'});
return;
}
const function = inputs.map(prop => ({
propOne: uniqueID,
propTwo: prop.value,
}));
const value = await productInput.bulkCreate(function);
if (!value || value.length < 1) {
res.status(httpStatusCodes.INTERNAL_SERVER_ERROR).send({ message:'No inputs were updated for this product.' });
return;
}
res.send(value);
return;
};