How to wait array until complete, then execute the function? - javascript

i have some trouble.
I want my function execute when my array is complete.
But the my function execute although my array is not complete.
can you fix my code please..
const historyPermintaanObat2 = obat => {
let array = [];
db.child("itemPermintaan")
.orderByChild("id")
.equalTo(obat.id)
.on("child_added", snapshot => {
let obj = snapshot.val();
let registerPermintaanRef = db.child(
`registerPermintaan/${obj.idPermintaan}`
);
registerPermintaanRef.once("value", snap => {
let username = snap.val().username;
let createdAt = snap.val().createdAt;
let approvedAt = snap.val().approvedAt;
let unit = snap.val().unit;
let obj2 = { ...obj, username, createdAt, approvedAt, unit };
array.push(obj2);
});
});
return array;
};
The result is [] empty array.
If i change the return array.length > 0 && array nothing is happen..

I have figured it out, use once and Promise.all()
const historyPermintaanObat2 = obat => {
db.child("itemPermintaan")
.orderByChild("id")
.equalTo(obat.id)
.once("value", snapshot => {
let array = [];
let obj = Object.values(snapshot.val());
obj.forEach(e => {
let registerPermintaanRef = db.child(
`registerPermintaan/${e.idPermintaan}`
);
let promise = registerPermintaanRef.once("value").then(snap => {
let username = snap.val().username;
let createdAt = snap.val().createdAt;
let approvedAt = snap.val().approvedAt;
let unit = snap.val().unit;
let obj2 = { ...e, username, createdAt, approvedAt, unit };
return obj2;
});
array.push(promise);
});
return Promise.all(array).then(value => {
return value;
});
});
};
Here's the reference i've got Promise.all with Firebase DataSnapshot.forEach

Related

Check if second array has any string from first array

I am trying to find if second array has any string from the first one.
Not sure what I am doing wrong
const domainAlertList = ["#domain1", "#tomato2", "#carrot3"];
const search = ["test#domain1.com", "test#tomato2.com"];
const myFunc = () => {
return search.some((r) => domainAlertList.includes(r));
};
console.log(myFunc());
It returns false instead of true
const domainAlertList = ["#domain1", "#tomato2", "#carrot3"];
const search = ["test#domain1.com", "test#tomato2.com"];
const myFunc = () => {
return domainAlertList.some((r) => {
return search.some(t => t.includes(r))
});
};
console.log(myFunc());
There are many ways to approach this. One of them could be the indexOf() method.
let str = "test#domain1.com";
str.indexOf('domain1') !== -1 // true
source
You have to map through the second array too like this
const domainAlertList = ["#domain1", "#tomato2", "#carrot3"];
const search = ["test#domain1.com", "test#tomato2.com"];
const myFunc = () => {
return search.some((r) => domainAlertList.some(domain=>{domain.includes(r)});
};
console.log(myFunc());
You can iterate and use array filter function.
const domainAlertList = ["#domain1", "#tomato2", "#carrot3"];
const search = ["test#domain1.com", "test#tomato2.com"];
let r = []
domainAlertList.forEach(i => {
r.push( search.filter(i1 => i1.includes(i)) )
})
newArr = r.map((a) => {
if(a.length != 0) {
return a
}
}).filter((a) => a);
console.log(newArr)
const domainAlertList = ["#domain1", "#domain1", "#carrot3"];
const search = ["test#domain1.com", "test#domain1.com"];
const myFunc = () => {
return domainAlertList.some((domain) =>
search.some((email) => email.includes(domain))
);
};
console.log(myFunc());

create nested object from string JavaScript

I have a string like this:
let user = "req.user.role"
is there any way to convert this as nested objects for using in another value like this?
let converted_string = req.user.role
I know I can split the user with user.split(".")
my imagination :
let user = "req.user.role".split(".")
let converted_string = user[0].user[1].user[2]
I found the nearest answer related to my question : Create nested object from query string in Javascript
Try this
let user = "req.user.role";
let userObj = user.split('.').reduceRight((obj, next) => ({
[next]: obj
}), {});
console.log(userObj);
Or this, for old browsers
var user = "req.user.role";
var userArray = user.split('.'), userObj = {}, temp = userObj;
for (var i = 0; i < userArray.length; i++) {
temp = temp[userArray[i]] = {};
}
console.log(userObj);
The function getvalue() will return the nested property of a given global variable:
var user="req.user.role";
var req={user:{role:"admin"}};
function getvalue(str){
return str.split('.').reduce((r,c,i)=>i?r[c]:window[c], '');
}
console.log(getvalue(user));
I'll take my shot at this:
let user = "req.user.role"
const trav = (str, o) => {
const m = str.split('.')
let res = undefined
let i = 0
while (i < m.length) {
res = (res || o)[m[i]]
if (!res) break
i++
}
return res
}
const val = trav(user, {
req: {
user: {
role: "admin"
}
}
})
console.log(val)
this function will traversed the passed in object for the entire length of the provided string.split "." list returning either a value or undefined.
You can do it like this:
let userSplitted = "req.user.role".split('.');
let obj, o = obj = {};
userSplitted.forEach(key=>{o=o[key]={}});

JavaScript Asynchronous in Redux..?

Action in fuction (action for data imported from firstore)
Here you get index data.
export async function getUserIndex (data){
let db = loadFB().firestore();
console.log(data)
let date = moment(data).utc().format()
let query = db.collection('users').where("create_date", ">", date)
console.log(query)
return await query.get().then(docs=>{
let result = docs.size
console.log("result!!", result)
return result
})
}
components in function
async getuserIndex_component(date){
let number =""
number = await userAction.getUserIndex(date)
console.log("number",number)
return await number
}
const {user_list} = this.props; //user_list = [{id:aa, pw:bb},{id:cc, pw:dd}...} data
let data = user_list
let groups = {}
let number =0
for (let index_data of data) {
let date = moment(index_data.create_date).format("YYYY-MM-DD").slice(0, 10)
let index = this.getuserIndex_component(date) //<==here log Promise{<pendding>}
console.log(index)
if (groups[date]) {
let group = groups[date];
group.push({ index: index-number, ...index_data });
} else {
number =0;
groups[date] = [{ index: index-number, ...index_data }]
}
number++;
}
const dates = Object.keys(groups)
const user_list_result = []
for (let date of dates) {
user_list_result.push(...(groups[date]))
}
return(...)
I am using redux. in action
Made a "getUserIndex()."
The component will then access the "getUserIndex" and retrieve data. But there is only pending in the console.log, and the data I want does not appear.
In this situation, I would like to know how to deal with async in for of.
I want to know what part I'm making a mistake.
In summary, functions in components are executed later than the render. So the index in the render does not contain the data. What is the solution to this?
As getuserIndex_component is asynchronous, it will return a promise. You will need to wait for that promise to resolve before the index result is available.
Firstly, you should be able to simplify getuserIndex_component to
async getuserIndex_component(date) {
let number = await userAction.getUserIndex(date)
console.log("number", number)
return number
}
And when you remove the console.log it could simply be
async getuserIndex_component(date) {
return userAction.getUserIndex(date)
}
As I mentioned in the comments. You can send off all of the async calls, then just wait for all of the promises to resolve via Promise.all.
Something like this. (I couldn't test any of this, but it should demonstrate the general idea.)
const { user_list } = this.props; //user_list = [{id:aa, pw:bb},{id:cc, pw:dd}...} data
let data = user_list
let groups = {}
let number = 0
const dates = []
const promises = data.map((index_data) => {
const date = moment(index_data.create_date).format("YYYY-MM-DD").slice(0, 10)
dates.push(date) // To use after all promises resolve.
return this.getuserIndex_component(date) // The index promise.
})
const indexes = await Promise.all(promises)
for (let i = 0; i < dates.length; i++) {
const index = indexes[i]
const date = dates[i]
console.log(index)
if (groups[date]) {
let group = groups[date];
group.push({
index: index - number,
...index_data
});
} else {
number = 0;
groups[date] = [{
index: index - number,
...index_data
}]
}
number++;
}
const dates = Object.keys(groups)
const user_list_result = []
for (let date of dates) {
user_list_result.push(...(groups[date]))
}
return (...)

How to remove a specific result within an list of results within an array

I have the following function which is grabbing results from my database, and then passing some results to a mapbox geocoder. However i want to put some error catching in it so when the geocoder returns a null/undefined value for the address, it'll remove not only that undefined value but everything else attached to that result.
I'm trying to remove a specific element using the .splice method, whilst it's detecting that a undefined value exists, it'll then remove the whole addresses result from the array rather than just the one address and all the other results. (I'm assuming that say if address 3 is undefined, you can remove everything in the array that is at position 3 since that'll be the results that are attached to that address value),
So as described below
let projects = values.map(elmt => elmt[5])
Is the bit i want to check for any undefined or null valyes coming back from the geocoder.
funciton
function grabProjects() {
sql = 'SELECT o.Name, o.Project_Type, o.sDate, o.eDate, p.Role, p.Project_Org_Address, p.Project_Org_Name,p.Country_Name, p.Country_Name, f.Funder_Name, per.Person_Name FROM project_name_table o INNER JOIN project_collaborators p ON o.Project_ID = p.Project_ID INNER JOIN project_funders f ON f.Project_ID = o.Project_ID INNER JOIN person per ON o.person_fk = per.Person_ID GROUP BY o.Project_ID LIMIT 20'
projectsArray = []
let query = conn.query(sql, (err, results) => {
if (err) throw err;
const geoPromise = param => new Promise((resolve, reject) => {
geo.geocode('mapbox.places', param, function (err, geoData) {
if (err) return reject(err);
if (geoData) {
resolve(geoData.features[0])
} else {
reject('No result found');
}
});
});
const promises = results.map(result =>
Promise.all([
result.Name,
result.Project_Type,
result.sDate,
result.eDate,
result.Role,
geoPromise(result.Project_Org_Address),
result.Project_Org_Name,
geoPromise(result.Country_Name),
result.Country_Name,
result.Funder_Name,
result.Person_Name
])
);
Promise.all(promises)
.then((values) => {
let pNames = values.map(elmt => elmt[0])
let pType = values.map(elmt => elmt[1])
let sDate = values.map(elmt => elmt[2])
let eDate = values.map(elmt => elmt[3])
let roles = values.map(elmt => elmt[4])
let projects = values.map(elmt => elmt[5])
let collabNames = values.map(elmt => elmt[6])
let countryNames = values.map(elmt => elmt[7])
let countryProjects = values.map(elmt => elmt[8])
let names = values.map(elmt => elmt[9])
let person_name = values.map(elmt => elmt[10])
projectsArray.push(pNames, pType, sDate, eDate, roles, projects, collabNames, countryNames, countryProjects, names, person_name)
console.log(projectsArray.length)
for (i = 0; i < projectsArray.length; i++) {
console.log(projectsArray[5][i])
if (projectsArray[5][i] === undefined) {
console.log('true')
projectsArray.splice(i, 1)
console.log('new length', projectsArray.length)
}
else {
console.log('false')
}
}
})
});
return projectsArray
}
I think this code can be refactored better to achieve what you want to do. As noted in my comment you'd be better off not adding the new row unless it passes your test. Something along the lines of:
function grabProjects() {
// ...
projectsArray = []
let query = conn.query(sql, (err, results) => {
...//
results.forEach(async (result) => {
const parsedResult = result
const address = await geoPromise(result.Project_Org_Address;
const country = await geoPromise(result.Country_Name);
result.address = address;
result.country = country;
const propUndefined = Object.keys(result).filter((key) => result[key] === undefined).length > 0;
if (!propUndefined) {
projectsArray.push(Object.keys(result).map((key) => result[key]);
}
//...
return projectsArray;
});

Transform elements of an array into objects / Javascript

I have a challenge to create a simple Notes manager in JS, I've written a function that takes one string, gives it and id and pushes it to an array of notes.
let nextId = 0;
const getId = () => nextId++;
let notes = [{id: getId(), value: 'Note'}];
const addNote = (input) => {
notes.push({id:getId(), value: input});
console.log('Note added');
I now struggle with a function that will take multiple strings as parameters
('own', 'snail', 'platypus')
create an object for each element with id/value(string) and push it to the main array.
The result should look like:
[{ id: 1, value: 'owl'},
{ id: 2, value: 'snail'}]
So far I have this, it assigns ID correctly, but the loop fails
const batchAddNotes = (values) => {
let obj = {};
for (i = 0; i < values.length; i++) {
obj.id = (getId());
obj.value = (values[i]);}
return obj;};
To have your variables in a certain scope, I'd pack it all in a class (or a function). As you're using arrow functions, the class should be ok. To add multiple nodes the way you've shown; using var-args, you can create a method that expects those with (...input)
class Notes {
constructor() {
this.nextId = 0;
this.nodes = [{
id: this.getId(),
value: 'Note'
}];
}
addNote(input) {
this.nodes.push({
id: this.getId(),
value: input
})
}
getId() {
return this.nextId++;
}
addNotes(...input) {
input.forEach(e => this.addNote(e));
}
}
const notes = new Notes();
notes.addNotes('own', 'snail', 'platypus');
console.log(notes.nodes);
Use the functions arguments object. It's an array of all the arguments that are being passed to a function. Then you can loop over them and run your functionality on them each time.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments
You could use the arguments passed in your function as var args
const addNote = _ => {
for(var i = 0; i < arguments.length; i++){
notes.push({id:getId(), value: arguments[i]});
console.log('Note added');
}
}
use rest params :
const myFn = (...values) => {
let tmpArr = [];
for(let i = 0 ; i < values.length ; i++){
tmpArr.push({
id : i + 1,
value : values[i]
});
}
return tmpArr;
}
const result = myFn('own', 'snail', 'platypus');
console.log(result);
This is how it look like when using Rest Params and reusing your first function. (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters).
You can add so many notes as you want (addMultipleNotes can receive indefinite number of arguments )
let nextId = 0;
const getId = () => nextId++;
let notes = [{id: getId(), value: 'Note'}];
const addSingleNote = (input) => {
notes.push({id:getId(), value: input});
console.log('Note added');
};
const addMultipleNotes = (...args) => {
for(let i = 0; i < args.length; i++){
addSingleNote(args[i]);
}
};
addMultipleNotes('one', 'two', 'three');
console.log(notes);
First of all, note how I've used an IIFE and a closure to create an id generator.
In the other hand, rest parameters, Array#map and parameter spread are your friends:
const incrId = (() => {
let id = 0
return () => ++id
})()
const valuesToNotes = (...values) => values.map(value => ({
id: incrId(),
value
}))
const notes = []
// Parameter spread (i.e. '...') gives each
// array item in the output of valuesToNotes
// as if you would use Function#apply
notes.push(...valuesToNotes('a', 'b', 'c'))
console.log(notes)
Yet another more functional approach which doesn't mutate the input notes and produces a new one with existing notes plus the ones transformed from values:
const concat = xs => ys => xs.concat(ys)
const map = f => xs => xs.map(f)
const pipe = xs => x => xs.reduce((r, f) => f(r), x)
const incrId = (() => {
let id = 0
return () => ++id
})()
const valueToNote = value => ({
id: incrId(),
value
})
const notes = []
const appendNotes = pipe([map(valueToNote), concat(notes)])
const moreNotes = appendNotes(['a', 'b', 'c'])
console.log(moreNotes)

Categories