I would like to send a POST request to a certain app through their API. What I am trying to do is to process the input data (called data) and send a POST request on one record by one record in the loop. Then, I delete the corresponding object in data for optimization purpose. I know that because of the asynchronous feature of JavaScript, the loop finishes before the function gets called. However, even though I wrap the api function in IIFE or wrap it in an async function with await(the code is below), the compiler still gives me function calls with the same parameter which is the last object. So, when I see created records on the app, David's information was generated three times. The screenshot below is each record object after being processed. If you could tell me ways of triggering the api call before the next iteration in the loop, that would be greatly appreciated.
const obj = [];
var record = {};
var data = [
{
"userId": "123",
"name": "John",
"phoneNumber": "123-456-6789"
},
{
"userId": "345",
"name": "Summer",
"phoneNumber": "535-631-9742"
},
{
"userId" : "789",
"name": "David",
"phoneNumber": "633-753-1352"
}
]
var dataLen = data.length;
var people = data;
createKeyValue = ((key, value) => {
var temp = {};
temp["value"] = value;
obj[key] = temp;
});
apiCall = ((record) => {
clientInformation.record.addRecord.then((resp) => {
console.log(resp);
}).catch((err) => {
console.log(err);
});
});
async function asyncFunction(record) {
let promise = new Promise((resolve, reject) => {
setTimeout(() => apiCall(record), 1000)
});
let result = await promise;
console.log(result);
}
while (dataLen > 0) {
for (let [key, value] of Object.entries(data[0])) {
switch(key) {
case 'userId':
createKeyValue(key, value);
break;
case 'name':
createKeyValue(key, value);
break;
default:
}
}
record["record"] = obj;
asyncFunction(record);
data.shift();
dataLen -= 1;
}
Here is the screenshot of how each processed data looks like.
I think you haven't understand how the for loop inside the while works. The data should be incremented each time to get the next array inside data.
The data[0] => { userId: 123 ... }, data[1] => { userId: 345 ... } and so on .
At each for loop iteration checks the 3 elements of each sub array, so each time temp stores the key values for userId and name. So when the loop finishes, the temp contains as key => userId, name and the corresponding values.
var data = [
{
"userId": "123",
"name": "John",
"phoneNumber": "123-456-6789"
},
{
"userId": "345",
"name": "Summer",
"phoneNumber": "535-631-9742"
},
{
"userId" : "789",
"name": "David",
"phoneNumber": "633-753-1352"
}
]
var dataLen = data.length;
let i = 0 ;
while ( i < dataLen) {
let temp = [];
for (let [key, value] of Object.entries(data[i])) {
if(key == 'userId' || key == 'name'){
temp[key] = value;
}
}
//Just to print the values and understand
for(let k in temp){
console.log(k+" -> "+temp[k]);
}
//here you will pass the temp values to functions
console.log(" At each iteration execute the required functions ");
//asyncFunction(temp);
i += 1;
}
Related
I have a use case where I need to make a query to a NoSql table (AWS DynamoDb),
which will return me a list,
each result further needs to query back on the same table.
My table has 2 important columns -
1. sub_service_id
2. parent_id
The query is made on the parent_id column of the table
each record will have only 1 parent_id,
but a parent_id can be present in multiple rows, therefore 1 parent can have multiple children.
My table looks like follows -
Except for the 1st call, all remaining calls on the parent_id field,
are made using the sub_service_id of each record.
As you can see in the image,
1st record is the parent of 2nd record,
2nd record is the parent of the 3rd record.
But theoretically, the 1st, 2nd and 3rd records can each have multiple children records.
The NoSql table (AWS DynamoDb) can be queried using the Primary Key(sub_service_id) or an Index (parent_id)
I tried the following code, but I'm not able to figure out the logic -
var tree = {};
var requestBody = "";
module.exports.listServiceRelation = async (constants, requestBody) => {
let requestContext = requestBody.context;
requestBody = requestBody.request;
let connection = await dynamoDb.getDynamoConnection(constants);
console.log('db connection created!');
let apiResponse = await getSubServiceHierarchy(constants, connection, requestBody.service_id);
};
let getSubServiceHierarchy = async (constants, connection, parent_id) => {
let subServices = await dynamoDb.getSubServiceByParentId(constants, connection, parent_id);
if(subServices != undefined && subServices.Items != undefined && subServices.Items.length > 0){
let subServicesArr = [];
for(let i in subServices.Items){
let temp = {};
let currObj = subServices.Items[i];
temp.sub_service_id = currObj.sub_service_id;
temp.sub_service_name = currObj.sub_service_name;
temp.parent_id = currObj.parent_id;
subServicesArr.push(temp);
}
tree[parent_id] = subServicesArr;
for(let i in subServices.Items){
let currObj = subServices.Items[i];
let grandChildren = await dynamoDb.getSubServiceByParentId(constants, connection, currObj.sub_service_id);
if(grandChildren != undefined && grandChildren.Items != undefined && grandChildren.Items.length > 0){
getSubServiceHierarchy(constants, connection, grandChildren);
}
}
}
else{
return [];
}
};
I need help with a pseudo-code logic for my use case,
My Output needs to be something like this -
[
{
"sub_service_id": "1",
"parent_id": "main_service_id",
"children": [{
"sub_service_id": "2",
"parent_id": "1",
"children": [{
"sub_service_id": "3",
"parent_id": "2",
"children": []
}]
}]
},
{
"sub_service_id": "4",
"parent_id": "main_service_id",
"children": []
}
]
Is it possible to create a function who return an array of result of several api call ?
Instead of this :
var func1;
var func2;
var func3;
apicall1().then((res) => {
func1 = res;
});
apicall1("string").then((res) => {
func2 = res;
});
apicall1(int).then((res) => {
func3 = res;
});
Have something like this :
var result = [];
var Json = "{
"functions": [{
"name": "apicall1",
"args": null
}, {
"name": "apicall2",
"args": "string"
}, {
"name": "apicall2",
"args": [0, "string"]
}]
}";
MyFunction(Json) {
for (i = 0; i < functions.lenght; i += 1) {
functions[i].name(functions[i].args).then((res) => { result.push(res); });
}
return result;
}
I juste search something to avoid to have X callapi one behind the other.
Thanks ;D
You can use Promise.all to get results in an array:
Promise.all([apicall1(), apicall1("string"), apicall1(int)])
.then(results => {
// Destructure the results into separate variables
let [func1, func2, func3] = results;
//access the results here
});
You should use
Promise.all([ api1,api2,api2,...]).then(function(results){
}).catch(function(err){
})
result will be an array with all responses in respective indexes. Here one thing you need to handle is exception. if any of the API call occurs in any kind of exception it will go to catch.
If you want to trigger the calls one after the other, you can use async/await :
https://hackernoon.com/6-reasons-why-javascripts-async-await-blows-promises-away-tutorial-c7ec10518dd9
let result = [];
let funcs = [{
"name": "apicall1",
"args": null
}, {
"name": "apicall2",
"args": "string"
}, {
"name": "apicall2",
"args": [0, "string"]
}]
async makeCalls() {
for (let func of funcs) {
let res = await func.name(func.args)
result.push(res)
}
return result;
}
makeCalls()
I am having issue with my recursive function getPath, as it is returning an empty array, when it should be returning an array that looks something like this:
['main', 'children', 'name']
I am not sure if the logic pare is right, as that isn't what the question is about, the question is, why is my array empty? It is pushing data onto the array, but the final result is an empty array.
let dataScope = [{
"name": "main",
"location": [":data"]
}, {
"name": "child",
"location": ["main", "children"]
}]
function getLocation(key) {
let val = dataScope.find(i => i.name == key)
return val ? val.location : []
}
function getPath(items) {
let path = []
let item = items.shift()
if (item) {
let loc = getLocation(item)
if (loc.length > 0 && loc.join('.') != ':data') {
path.push(...getPath(loc))
console.log('added to array')
}
}
return path
}
console.log(getPath(['child', 'name']))
You don't do anything with loc so, it seems nothing gets pushed to the array
Note: I'm still trying to get to grips with why your original code results in an empty array - however, this code produces the expected result :p
let dataScope = [{
"name": "main",
"location": [":data"]
}, {
"name": "child",
"location": ["main", "children"]
}]
function getLocation(key) {
let val = dataScope.find(i => i.name == key);
return val ? val.location : []
}
function getPath(items, indent = 0) {
let z = items.join(',');
console.log(`${' '.repeat(indent)}called with ${z}`);
let path = [];
let item = items.shift();
let loc = [];
if (item) {
loc = getLocation(item);
if (loc.length > 0 && loc.join('.') != ':data') {
path.push(...getPath(loc.slice(), indent + 4)); // .slice() so loc isn't mutated
console.log(`${' '.repeat(indent)}${z} has path [${path.join(',')}]`);
}
path.push(...loc); // add loc to the path - comment this out to see the difference
}
console.log(`${' '.repeat(indent)}${z} returns [${path.join(',')}]`);
return path
}
console.log(`[${getPath(['child', 'name'])}]`)
First youre passing an array of names to getPath but then later youre passing the location array. Which one should it be? Logic needs tweaking. And also there's nothing in the dataset using the value "name" so your test is incorrect as well.
Its because you're doing recursion sending dataScope location but you implemented getPath expecting dataScope keys:
let dataScope = [{
"name": "main",
"location": [":data"]
}, {
"name": "child",
"location": ["main", "children"]
}]
function getLocation(key) {
let val = dataScope.find(i => i.name == key)
return val ? val.location : []
}
function getPath(keys) { // changing items name to keys for clarification
let path = []
let key = keys.shift()
if (key) {
let loc = getLocation(key);
if (loc.length > 0 && loc.join('.') != ':data') {
path.push(...loc) // push locs into array
getPath(keys) // call getPath with remaining keys
console.log('added to array')
}
}
return path
}
console.log(getPath(['child', 'main']))
You will not have :data into your path result because of this statement: loc.join('.') != ':data'. If you remove it you will get your expected output.
How do I sort the following structure in Firebase by sortOrder?
categories {
{
"b": {
"name": "Banana",
"sortOrder": 2
},
"a": {
"name": "Apple",
"sortOrder": 1
}
}
}
From the documentation it looks as simple as:
ref('categories').orderByChild('sortOrder').once('value') ...
However, the first node returned is banana. It doesn't matter what string value I use. For example, the following returns the same results:
ref('categories').orderByChild('xxx').once('value') ...
Full function:
public list(): Observable<Category[]> {
let ref = firebase.database().ref('categories').orderByChild('sortOrder');
return Observable.fromPromise(<Promise<any>>ref.once('value'))
.flatMap(snapshot => {
let objects = snapshot.val();
let categories: Array<Category> = new Array();
for (let key in objects) {
let category: Category = objects[key];
category.code = key;
categories.push(category);
}
return Observable.of(categories);
}
);
}
The problem is that when you access the children via the snapshot's value's keys, the order is indeterminate.
You need to use the snapshot's forEach method:
return Observable.fromPromise(<Promise<any>>ref.once('value'))
.flatMap(snapshot => {
let categories: Array<Category> = new Array();
snapshot.forEach(childSnapshot => {
let category: Category = childSnapshot.val();
category.code = childSnapshot.key;
categories.push(category);
});
return Observable.of(categories);
}
);
Also, you could just use map and return categories.
How can i find data that is related to the already known data?
( I'm a newb. )
For example here is my json :
[
{ "id": "1", "log": "1","pass": "1111" },
{ "id": 2, "log": "2","pass": "2222" },
{ "id": 3, "log": "3","pass": "3333" }
]
Now i know that "log" is 1 and i want to find out the data "pass" that is related to it.
i've tried to do it so :
The POST request comes with log and pass data , i search the .json file for the same log value and if there is the same data then i search for related pass
fs.readFile("file.json", "utf8", function (err, data) {
var jsonFileArr = [];
jsonFileArr = JSON.parse(data); // Parse .json objekts
var log = loginData.log; // The 'log' data that comes with POST request
/* Search through .json file for the same data*/
var gibtLog = jsonFileArr.some(function (obj) {
return obj.log == log;
});
if (gotLog) { // If there is the same 'log'
var pass = loginData.pass; // The 'pass' data that comes with POST request
var gotPass = jsonFileArr.some(function (obj) {
// How to change this part ?
return obj.pass == pass;
});
}
else
console.log("error");
});
The problem is that when i use
var gotPass = jsonFileArr.some(function (obj) {
return obj.pass == pass;
});
it searches through the whole .json file and not through only one objekt.
Your main problem is that .some() returns a boolean, whether any of the elements match your predicate or not, but not the element itself.
You want .find() (which will find and return the first element matching the predicate):
const myItem = myArray.find(item => item.log === "1"); // the first matching item
console.log(myItem.pass); // "1111"
Note that it is possible for .find() to not find anything, in which case it returns undefined.
The .some() method returns a boolean that just tells you whether there is at least one item in the array that matches the criteria, it doesn't return the matching item(s). Try .filter() instead:
var jsonFileArr = JSON.parse(data);
var log = loginData.log;
var matchingItems = jsonFileArr.filter(function (obj) {
return obj.log == log;
});
if (matchingItems.length > 0) { // Was at least 1 found?
var pass = matchingItems[0].pass; // The 'pass' data that comes with the first match
} else
console.log("error"); // no matches
Using ES6 Array#find is probably the easiest, but you could also do (among other things)
const x = [{
"id": "1",
"log": "1",
"pass": "1111"
}, {
"id": 2,
"log": "2",
"pass": "2222"
}, {
"id": 3,
"log": "3",
"pass": "3333"
}];
let myItem;
for (let item of x) {
if (item.log === '1') {
myItem = item;
break;
}
}
console.log(myItem);