Concurrently iterate over two iterables of same length - node.js

I have two iterables of the same length that I need to loop over at the same time. One iterable is a Map of custom objects, and the other is an array of objects. I need to add the contents of the array into the Map (via some helper prototype functions), preferably asynchronously and concurrently. Also, the two containers are associated to each other based on their order. So the first element in the array needs to be added to the first element in the Map.
If I was to do this synchronously it would look something like this:
var map;
var arr;
for (var i = 0; i < arr.length; i++) {
// get our custom object, call its prototype helper function with the values
// in the array.
let customObj = map[i];
customObj.setValues(arr[i])
}
Typically to loop over arrays async and concurrently I use bluebirds Promise.map. It would look something like this:
var arr
Promise.map(arr, (elem) => {
// do whatever I need to do with that element of the array
callAFunction(elem)
})
It would be awesome if I could do something like this:
var map;
var arr;
Promise.map(map, arr, (mapElem, arrElem) {
let customObj = mapElem[1];
customObj.setValue(arrElem);
})
Does anyone know of a library or a clever way to help me accomplish this?
Thanks.
EDIT: Just want to add some clarification on the objects stored in the map. The map is keyed on a unique value, and values are associated with that unique values are what make up this object. It is defined in a similar manner to this:
module.exports = CustomObject;
function CustomObject(options) {
// Initialize CustomObjects variables...
}
CustomObject.prototype.setValue(obj) {
// Logic for adding values to object...
}

if you already know, that the Map (I assume you really mean the JavaScript Map here, which is ordered) and the array have the same length, you do not need a mapping function, that takes both the array AND the map. One of both is enough, because the map function also gives you an index value:
var map;
var arr;
Promise.map(map, (mapElem, index) => {
let customObj = mapElem[1];
customObj.setValue(arr[index]);
});

You can use the function Promise.all that execute all the given asynchronous functions.
You should know that actually node.js support fully Promises, you do not need bluebirds anymore.
Promise.all(arr.map(x => anyAsynchronousFunc(x)))
.then((rets) => {
// Have here all return of the asynchronous functions you did called
// You can construct your array using the result in rets
})
.catch((err) => {
// Handle the error
});

Related

How can I get a call back to modify a string?

write a function named speaker that takes in an array of strings and a callback function.
Use forEach to build a new array of strings, each string modified by the callback. Return the new array.
const speaker = (words, callback) => {
};
Honestly have zero clue where to start
Most likely you want to write a function in JavaScript. It can be implemented like this:
const speaker = (words, callback) => {
let modifiedWords = [];
words.forEach((word) => {
// Add the modified string to the array
modifiedWords.push(callback(word));
});
return modifiedWords;
};
To begin with, we define an array modifiedWords, where after each iteration we will add the modified word with the callback function. Next, we use the forEach method for the words array. This method iterates over each element of the array and calls the callback function that we passed (we passed an anonymous arrow function that takes an array element as an argument and passes this element as an argument to our callback function and returns the result). The result is stored in the array modifiedWords with the push method. We then return an array of modified words modifiedWords.

Nodejs Google Drive API - Accessing array elements in callback function

I want file names produced using drive.files.list to use as strings for downloading all files in a folder.
I am having trouble accessing an array of filenames. Basically through lack of knowledge, So I am lost by the logic of the mouse over reference of map in files.map() See below code.
My code:
if (files.length) {
// map method calls the callbackfn one time for each element in the array
files.map((file) => {
// there are x elements (a filename/id) called x times
// I want to access one filename at a time. Return a plain string filename for a downloadFile() function
var names = [];
// rough test to produce desired output. Produces 'undefined' for array indexes greater than 0 e.g names[1]
// files.length = 1;
// Without the files.length = 1; filename is outputted x times
var names = [];
files.forEach(function (file, i) {
names[i]= file.name;
})
// first array index (with files.length =1;) first filename and only this filename. Correct result!!!
console.log(names[0]);
I don't have much experience with OOP or Nodejs. But using various tests (code changes) the largest output looked like an array of arrays. I want to narrow it down to an array of filenames that I can JSON.stringify before using for downloading.
Mouse over of 'map'
(method) Array<drive_v3.Schema$File>.map<void>(callbackfn: (value: drive_v3.Schema$File, index: number, array: drive_v3.Schema$File[]) => void, thisArg?: any): void[]
Calls a defined callback function on each element of an array, and returns an array that contains the results.
#param callbackfn — A function that accepts up to three arguments. The map method calls the callbackfn function one time for each element in the array.
#param thisArg — An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.
Any suggestions apprecieated.
Try replacing the code with this one:
if (files.length) {
var names = [];
files.forEach(function(file, i) {
names.push(file.name);
});
console.log('filenames: ', names);
}
and then you can read the filenames by looping through names array, and pass it to other functions for processing:
names.forEach((fileName, i)=>{
console.log('filename: ', fileName, ' index', i);
// pass the name to other functions for processing
download(fileName);
});

how to push data in array in typescript

I want to create a dynamic menu bar by fetching data from two collections (supcat and cat) then combining the two to create a new array which i will access on page load for menu but the push() is not working.
ngOnInit() {
this.cattest();}
cattest(){
var x;
this.supcatobj.fetchsupcat().subscribe(
(res)=>
{
if(res.length!=0)
{this.supcat=res;
for(let x=0;x<this.supcat.length; x++)
{
this.catobj.fetchallcat(this.supcat[x]["_id"]).subscribe(
(resp)=>
{
this.allcat=resp;
for(let y=0;y<this.allcat.length;y++)
{
}
this.testarr[x].push(this.supcat[x]["supcatname"],this.allcat);
}
);
}
}
}
);}
Instead of nesting subscribe() calls, I would try to compose separate observables for your two different collections and then use the combineLatest() operator to combine those into your desired array. It is hard to see exactly what you are working for, but conceptually it would be something like this:
const supcat$ = this.supcatobj.fetchsupcat().pipe(filter(cat => cat.length > 0));
const allCat$ = this.catobj.fetchallcat();
const combinedCats$ = combineLatest(supcat$, allCat$);
const res$ = combinedCats$.pipe(map(res => {
// do operation that involves both data sets
});
Remember that map() will return a new array. This way you will only need to subscribe to the one variable, and if you put it at the class level you could use the async pipe (|) in your template so it will unsubscribe automatically.

How do I filter keys from JSON in Node.js?

I'm trying to select certain keys from an JSON array, and filter the rest.
var json = JSON.stringify(body);
which is:
{
"FirstName":"foo",
"typeform_form_submits":{
"foo":true,
"bar":true,
"baz":true
},
"more keys": "foo",
"unwanted key": "foo"
}
Want I want:
{
"FirstName":"foo",
"typeform_form_submits":{
"foo":true,
"bar":true,
"baz":true
}
}
I've checked out How to filter JSON data in node.js?, but I'm looking to do this without any packages.
Now you can use Object.fromEntries like so:
Object.fromEntries(Object.entries(raw).filter(([key]) => wantedKeys.includes(key)))
You need to filter your obj before passing it to json stringify:
const rawJson = {
"FirstName":"foo",
"typeform_form_submits":{
"foo":true,
"bar":true,
"baz":true
},
"more keys": "foo",
"unwanted key": "foo"
};
// This array will serve as a whitelist to select keys you want to keep in rawJson
const filterArray = [
"FirstName",
"typeform_form_submits",
];
// this function filters source keys (one level deep) according to whitelist
function filterObj(source, whiteList) {
const res = {};
// iterate over each keys of source
Object.keys(source).forEach((key) => {
// if whiteList contains the current key, add this key to res
if (whiteList.indexOf(key) !== -1) {
res[key] = source[key];
}
});
return res;
}
// outputs the desired result
console.log(JSON.stringify(filterObj(rawJson, filterArray)));
var raw = {
"FirstName":"foo",
"typeform_form_submits":{
"foo":true,
"bar":true,
"baz":true
},
"more keys": "foo",
"unwanted key": "foo"
}
var wantedKeys =["FirstName","typeform_form_submits" ]
var opObj = {}
Object.keys(raw).forEach( key => {
if(wantedKeys.includes(key)){
opObj[key] = raw[key]
}
})
console.log(JSON.stringify(opObj))
I know this question was asked aways back, but I wanted to just toss out there, since nobody else did:
If you're bound and determined to do this with stringify, one of its less-well-known capabilities involves replacer, it's second parameter. For example:
// Creating a demo data set
let dataToReduce = {a:1, b:2, c:3, d:4, e:5};
console.log('Demo data:', dataToReduce);
// Providing an array to reduce the results down to only those specified.
let reducedData = JSON.stringify(dataToReduce, ['a','c','e']);
console.log('Using [reducer] as an array of IDs:', reducedData);
// Running a function against the key/value pairs to reduce the results down to those desired.
let processedData = JSON.stringify(dataToReduce, (key, value) => (value%2 === 0) ? undefined: value);
console.log('Using [reducer] as an operation on the values:', processedData);
// And, of course, restoring them back to their original object format:
console.log('Restoration of the results:', '\nreducedData:', JSON.parse(reducedData), '\nprocessedData:', JSON.parse(processedData));
In the above code snippet, the key value pairs are filtered using stringify exclusively:
In the first case, by providing an array of strings, representing the keys you wish to preserve (as you were requesting)
In the second, by running a function against the values, and dynamically determining those to keep (which you didn't request, but is part of the same property, and may help someone else)
In the third, their respective conversions back to JSON (using .parse()).
Now, I want to stress that I'm not advocating this as the appropriate method to reduce an object (though it will make a clean SHALLOW copy of said object, and is actually surprisingly performant), if only from an obscurity/readability standpoint, but it IS a totally-effective (and mainstream; that is: it's built into the language, not a hack) option/tool to add to the arsenal.

Protractor compare string numbers

Today I've faced interesting problem of create test for pretty simple behavior: 'Most recent' sorting. All what test need to know:
Every item have ID
Previous ID is less then next in this case of sorting
Approach: writing ID in to attribute of item, getting that id from first item with getAttribute() and either way for second.
Problem: getAttribute() promise resulting with string value and Jasmine is not able to compare (from the box) string numbers.
I would like to find elegant way to compare them with toBeLessThan() instead of using chains of few .then() that will be finished with comparing that things.
Root of no-type-definition evil
Thanks guys <3
You can create a helper function to convert string number to actual number, which will make use of Promises:
function toNumber(promiseOrValue) {
// if it is not a promise, then convert a value
if (!protractor.promise.isPromise(promiseOrValue)) {
return parseInt(promiseOrValue, 10);
}
// if promise - convert result to number
return promiseOrValue.then(function (stringNumber) {
return parseInt(stringNumber, 10);
});
}
And then use the result with .toBeLessThan, etc:
expect(toNumber(itemId)).toBeLessThan(toNumber(anotherItemId));
I forgot of native nature of promises but tnx to Michael Radionov I've remembered what I want to do.
expect(first.then( r => Number(r) )).toBe(next.then( r => Number(r) ));
I guess this stroke looks simple.
UPDATE
ES6:
it('should test numbers', async function () {
let first = Number(await $('#first').getText());
let second = Number(await $('#second').getText());
expect(first).toBeGreaterThan(second);
})
One option to approach it with a custom jasmine matcher:
toBeSorted: function() {
return {
compare: function(actual) {
var expected = actual.slice().sort(function (a, b) {
return +a.localeCompare(+b);
});
return {
pass: jasmine.matchersUtil.equals(actual, expected)
};
}
};
},
Here the matcher takes an actual input array, integer-sort it and compare with the input array.
Usage:
expect(element.all(by.css(".myclass")).getAttribute("id")).toBeSorted();
Note that here we are calling getAttribute("id") on an ElementArrayFinder which would resolve into an array of id attribute values. expect() itself is patched to implicitly resolve promises.

Resources