Combine a list of objects with information from a map Dart - loops

I am trying to add information from a map received through an http call to a list of objects in Dart. For example, the list of objects are Tools that have the toollocation property:
Tool(
{this.make,
this.model,
this.description,
this.tooltype,
this.toollocation,
this.paymenttype,
this.userid});
I am also using the google distance matrix api to gather the distance from the user that the tool is.
Future<DistanceMatrix> fetchDistances() async {
await getlocation();
latlongdetails = position['latitude'].toString() +
',' +
position['longitude'].toString();
print(latlongdetails);
print('still running');
final apiresponsepc = await http
.get(
'https://maps.googleapis.com/maps/api/distancematrix/json?units=imperial&origins=$latlongdetails&destinations=$postcodes&key=xxx');
distanceMatrix =
new DistanceMatrix.fromJson(json.decode(apiresponsepc.body));
return distanceMatrix;
}
What I have been doing in the past is calling a future and just getting the distance once I have returned the original results for the tool. However I want to be able to sort the tool results by distance, so I need to iterate through each tool in the list and add the distance for each of them.
So far I have been trying a foreach loop on the tools list:
finalresults.forEach((tool){ tool.toollocation = distanceMatrix.elements[0].distance.text;});
but clearly this will only add the first distance measurement to every one of the tools.
Is there any way I can iterate through each tool and then add the distance from the distance matrix map? Each distance will be in sequence with each tool in the list.

I think this is what you wanted to do
finalResults.forEach((tool) {
distanceMatrix.elements.forEach((element){
tool.toolLocation = element.distance.text;
});
});
If elements is also a List then you can use the foreach syntax to iterate through it.

I have resolved this with the following code:
int number = 0;
finalresults.forEach((tool) {
tool.toollocation = distanceMatrix.elements[number].distance.text;
number = number + 1;
});

Related

Calling an API based on data within cells A2:A100 and outputting to cells G2:G100

I've been trying to figure out how to get a Google AppsScript to pull in an API for keyword rank tracking directly within Google Sheets.
The loop is required to dynamically pull in information from column A and output the keyword ranking position into column G.
The keywords are in cells A2-A100. The ranking position (which is the only thing we are pulling from the API) we are popping into the corresponding row in column G, starting from G2. For testing purposes, we've got the loop set from 1 to 3.
We're at a bit of a loss as to why this isn't working as expected, and would really appreciate a nudge in the right direction!
The issue is that the very first result always returns 'keyword = undefined' within the API, and returning a result of '-1', meaning that the first row is not read. We've tried updating the r to 0, to 2, and changing the r references to no avail.
This makes us think that there must be something wrong with the loop, rather than the rest of the code, but please do correct me if this is not the case.
The script we've gotten so far is;
function callAPI() {
//New loop
for (r = 1; r <= 3; r++) {
{
//Find keyword, encode query and url
var query = keyword;
var url =
'https://api.avesapi.com/search?apikey={{APIKEYREMOVEDFORPRIVACY}}&type=web&' +
'google_domain=google.co.uk&gl=gb&hl=en&device=mobile&output=json&num=100&tracked_domain={{CLIENTDOMAIN}}.com&position_only=true&uule2=London,%20United%20Kingdom' +
'&query=' +
encodeURIComponent(query);
//Call API and add to log
var response = UrlFetchApp.fetch(url, { muteHttpExceptions: true });
Logger.log(response);
//Get column value for keyword
var sheet = SpreadsheetApp.getActiveSheet();
var keyword = sheet.getRange(1 + r, 1).getValue();
}
//Set value of column
var results = sheet.getRange(1 + r, 7).setValue(response);
}
}
Additional edit:
So this is crystal clear, the desired input is;
keyword in A2 is read using the API and the output found (ranking position) is fed into G2.
the loop should then read A3, find the corresponding ranking position within the API, and adds that value to G3
rinse and repeat until the end of the loop.
Hopefully this is enough to go on, would really appreciate any advice, thank you!
Basically from TheMaster's comments you switch up your statements to this:
function callAPI() {
var sheet = SpreadsheetApp.getActiveSheet();
var keywords = sheet.getRange(2,1,3).getValues();
var responses = [];
//New loop
for (r = 0; r <= 2; r++) {
//Find keyword, encode query and url
var query = keywords[r][0];
var url =
'https://api.avesapi.com/search?apikey={{APIKEYREMOVEDFORPRIVACY}}&type=web&' +
'google_domain=google.co.uk&gl=gb&hl=en&device=mobile&output=json&num=100&tracked_domain={{CLIENTDOMAIN}}.com&position_only=true&uule2=London,%20United%20Kingdom' +
'&query=' +
encodeURIComponent(query);
//Call API and add to log
var resp = UrlFetchApp.fetch(url, { muteHttpExceptions: true });
Logger.log(resp);
responses.push([resp]);
}
//Set value of column
sheet.getRange(2,7,3).setValues(responses);
}
Note that I moved the sheet declaration outside the loop, it needs to be only called once.
EDIT: I updated the code to follow best practices in the tag info page. Note the usage of arrays as return values of getValues() and parameter of setValues().

Non-blocking array reduce in NodeJS?

I have a function that takes in two very large arrays. Essentially, I am matching up orders with items that are in a warehouse available to fulfill that order. The order is an object that contains a sub array of objects of order items.
Currently I am using a reduce function to loop through the orders, then another reduce function to loop through the items in each order. Inside this nested reduce, I am doing a filter on items a customer returned so as not to give the customer a replacement with the item they just send back. I am then filtering the large array of available items to match them to the order. The large array of items is mutable since I need to mark an item used and not assign it to another item.
Here's some psudocode of what I am doing.
orders.reduce(accum, currentOrder)
{
currentOrder.items.reduce(internalAccum, currentItem)
{
const prevItems = prevOrders.filter(po => po.customerId === currentOrder.customerId;
const availItems = staticItems.filter(si => si.itemId === currentItem.itemId && !prevItems.includes(currentItem.labelId)
// Logic to assign the item to the order
}
}
All of this is running in a MESOS cluster on my server. The issue I am having is that my MESOS system is doing a health check every 10 seconds. During this working of the code, the server will stop responding for a short period of time (up to 45 seconds or so). The health check will kill the container after 3 failed attempts.
I am needing to find some way to do this complex looping without blocking the response of the health check. I have tried moving everything to a eachSerial using the async library but it still locks up. I have to do the work in order or I would have done something like async.each or async.eachLimit, but if not processed in order, then items might be assigned the same thing simultaneously.
You can do batch processing here with a promisified setImmediate so that incoming events can have a chance to execute between batches. This solution requires async/await support.
async function batchReduce(list, limit, reduceFn, initial) {
let result = initial;
let offset = 0;
while (offset < list.length) {
const batchSize = Math.min(limit, list.length - offset);
for (let i = 0; i < batchSize; i++) {
result = reduceFn(result, list[offset + i]);
}
offset += batchSize;
await new Promise(setImmediate);
}
return result;
}

create an array of the first n integers in google app script

Following this answer for an analogous question for vanilla JavaScript, I've tried to create an array of integers from 1 to 20 with the following code:
var k=Array.from(new Array(20), (x,i) => i + 1);
but I get Syntax error. Evidently Google App Script does not support ES6.
Is there any modern alternative to for ... loop?
You want to create an array including the number in each element using Google Apps Script.
It's like [1.0, 2.0, 3.0,,, 20]
If my understanding is correct, how about this workaround? Please think of this as just one of several workarounds. In this workaround, I used Array.apply().
Sample script 1:
var res = Array.apply(null, Array(20)).map(function(_, i) {return i + 1});
console.log(res); // or for Google Apps Script, Logger.log(res)
Sample script 2:
In the current stage (2022), the following sample script can be also used.
var res = [...Array(20)].map((_, i) => i + 1);
console.log(res);
Note:
For example, when Array.apply(null, Array(3)) and [...Array(3)] are run, an array of [undefined,undefined,undefined] is created. In this script, values are put in each element using map(). If you want to fill an array by a value, you can also use Array(3).fill("sample").
Above script can be worked with Google Apps Script.
If you want to use Logger.log(), please replace console.log(res) to Logger.log(res).
References:
Function​.prototype​.apply()
map()
Benchmark: Loop for Array Processing using Google Apps Script
Issue:
The main issue is that arrays created using new Array(length) is sparse and have elements that's never set and most array methods don't work on it.
Solution:
It's possible to create a dense array from sparse array using apply. Then, it's easy to get indexes of that array using Object.keys()
Snippets:
//using concat
function test11(){
Array.prototype.range = function(len){
return Object.keys(Array.prototype.concat.apply([],new Array(len)))//sparse to dense
}
Logger.log([].range(16))
}
//using push
function test12(){
Array.prototype.range = function(len){
var out = [];
Array.prototype.push.apply(out,new Array(len))
return Object.keys(out);
}
Logger.log([].range(15))
}
No. for/while loop is still best (performant) even on modern browsers. A small function can be used :
function range(n) { var a = []; while(n) a[n - 1] = n--; return a }
console.log( range(5) )

Node fast way to find in array

I have a Problem.
My script was working fine and fast, when there was only like up to 5000 Objects in my Array.
Now there over 20.000 Objects and it runs slower and slower...
This is how i called it
for(var h in ItemsCases) {
if(itmID == ItemsCases[h].sku) {
With "for" for every object and check where the sku is my itmID, cause i dont want every ItemsCases. Only few of it each time.
But what is the fastest and best way to get the items with the sku i need out of it?
I think mine, is not the fastest...
I get multiple items now with that code
var skus = res.response.cases[x].skus;
for(var j in skus) {
var itmID = skus[j];
for(var h in ItemsCases) {
if(itmID == ItemsCases[h].sku) {
the skus is also an array
ItemsCases.find(item => item.sku === itmID) (or a for loop like yours, depending on the implementation) is the fastest you can do with an array (if you can have multiple items returned, use filter instead of find).
Use a Map or an object lookup if you need to be faster than that. It does need preparation and memory, but if you are searching a lot it may well be worth it. For example, using a Map:
// preparation of the lookup
const ItemsCasesLookup = new Map();
ItemsCases.forEach(item => {
const list = ItemsCasesLookup.get(item.sku);
if (list) {
list.push(item)
} else {
ItemsCasesLookup.set(item.sku, [item]);
}
});
then later you can get all items for the same sku like this:
ItemsCasesLookup.get(itmID);
A compromise (not more memory, but some speedup) can be achieved by pre-sorting your array, then using a binary search on it, which is much faster than linear search you have to do on an unprepared array.

How to calculate the sum value of a set of arrays

Good day
I was wondering if someone can assist me in calculating the sum value of a array of data.
var products = [{'size':'400' },{'size':'500' }] ;
angular.forEach(products, function(item){
var total = 0;
total += item.size++;
return total;
});
How do I use $q.all() statement to store up this information as the file download section am using ignores my angular.forEach statment and just checks individual items instead.
Use reduce instead of angular.forEach
const productSum = products.reduce((sum, product) => sum + parseInt (product.size, 10), 0)
$q.all is for computing multiple promises. I think you are looking in the wrong direction.
if you want to get sum of all sizes then use javascript reduce method. It will return the sum of all sizes efficiently.
var products = [{'size':'400' },{'size':'500' }] ;
var sum = products.reduce((a,b) => parseInt(a.size) + parseInt(b.size))
console.log(sum)
About $q.all. $q.all typically use to send multiple http request one shot. If you want send the sum to your service then bind the sum as data property to one of or all your http requests (depend on your requirement).

Resources