recursion with Promises - javascript
I have a collection in MongoDB like this
[
{
"classId": "1",
"name": "Input",
"definition": [
{
"property": [
{
"classId": "12",
"name": "One"
},
{
"classId": "8",
"name": "Comment"
}
]
}
]
},
{
"classId": "8",
"name": "CommentDetail",
"definition": [
{
"property": [
{
"classId": "10",
"name": "user"
},
{
"classId": "10",
"name": "message"
}
]
}
]
},
{
"classId": "10",
"name": "String",
"definition": []
},
{
"classId": "12",
"name": "Int",
"definition": []
}
]
Based on db above, I have a model to display
data = {
name:'',
template: ''
}
With classId=1, the expectation result is
{
"Name": "Input",
"temlate": "{['One': 'Int','Comment': ['user': 'String','message':'String']]}"
}
I try to using recursive promise to implement it. When property[] is empty, the result will be return.
Here is my function:
const getObjectTypeByClassId = (snapshotId, artifactId, objectType) => {
return artifactDetailModel.find({
'snapshotId': snapshotId,
'artifactId': artifactId
})
.then(data => {
let artifact = data[0];
let definition;
let definitionData = {};
return Promise.resolve()
.then(() => {
definition = artifact.data.teamworks.twClass[0].definition[0];
if (!lodash.isUndefined(definition.property)) {
const listOfProperty = definition.property;
for (let property of listOfProperty) {
classId = commonUtil.getArtifactId(property.classRef[0]);
if (!lodash.isUndefined(classId)) {
return getObjectTypeByClassId(snapshotId, classId, objectType);
}
}
} else {
definitionData.nameType = artifact.data.teamworks.twClass[0].elementAttribute.name;
definitionData.classId = artifact.data.teamworks.twClass[0].elementAttribute.id;
definitionData.template = bpmMapping.objectType[artifact.data.teamworks.twClass[0].elementAttribute.name];
return objectTypeModel.create(definitionData)
.then(obj => {
const response = {
name: objectType.name,
isArrayOf: objectType.isArrayOf,
nameType: obj.nameType,
template: obj.template,
}
return response;
})
}
})
})
}
Run with my function, the response is
data: {
Name: Input
temlate: user: String,
}
Please advice me.
I tried it to some extent, but wasn't able to get it right. Plus your expected output is not the valid JSON "temlate": {[]} doesn't make sense.
It has nothing to do with Promise. You have to DFS you db array and created expected output. Here is what I have donup tillll now, you can think along those lines. But this is far from the solution.
let mainArray = [
{
"classId": "1",
"name": "Input",
"definition": [
{
"property": [
{
"classId": "12",
"name": "One"
},
{
"classId": "8",
"name": "Comment"
}
]
}
]
},
{
"classId": "8",
"name": "CommentDetail",
"definition": [
{
"property": [
{
"classId": "10",
"name": "user"
},
{
"classId": "10",
"name": "message"
}
]
}
]
},
{
"classId": "10",
"name": "String",
"definition": []
},
{
"classId": "12",
"name": "Int",
"definition": []
}
]
function dfs(root, createdRoot, fn, level) {
fn(root,createdRoot, level);
if(root.definition)/*if definition exists => keep traversing*/
root.definition[0].property.forEach(function (child) {
createdRoot.template = createdRoot.template || [];
let tempObj = {};
let lookupObj = lookupByClassId(child.classId);
tempObj[child.name] = lookupObj.name;
createdRoot.template.push(tempObj);
dfs(child,tempObj, fn, level + 1);
});
else /*if definition doesn't exist, look into the array*/
{
createdRoot.template = lookupByClassId(root.classId);
}
}
function lookupByClassId(classId){
for(let i=0;i<mainArray.length;++i){
let element =mainArray[i]
if(element.classId == classId)
return element;
}
}
let root = lookupByClassId(1);
createdRoot ={};
function func1(root, createdRoot, level) {
createdRoot.name = root.name;
console.log(root.classId);
}
dfs(root, createdRoot, func1, 0);
here is the solution from a guy on the internet. it works well. Thanks, #Cuong Quach
var MongoClient = require('mongodb').MongoClient;
var database = 'testdequy';
var collection = 'classes';
MongoClient.connect("mongodb://localhost:27017/" + database, function (err, db) {
findDetails("1").then((r) => {
console.log("r>>>", r);
})
function findDetails(classId) {
var results = { Name: "", Template: "" };
var template = {};
return new Promise((main_resolve, reject) => {
process(template, "" , classId)
var works = 0;
function process(parent, childKey, objectId) {
return new Promise((resolve, reject) => {
db.collection(collection).find({ classId: objectId })
.toArray((err, docs) => {
if (results.Name == "") {
results.Name = docs[0].name;
}
let objectItem;
if (childKey == "")
objectItem = parent;
else
objectItem = parent[childKey];
console.log("\ndocs", docs[0], objectId, objectItem)
if (docs[0].definition.length == 0 || docs[0].definition[0].property == undefined) {
let name = docs[0].name;
parent[childKey] = name;
console.log("\nNo child", docs[0],parent, objectItem, docs[0].name)
resolve(0);
} else {
docs[0].definition[0].property.forEach((item) => {
works++;
//console.log("item", item)
let id = item.classId;
let name = item.name;
objectItem[name] = {};
process(objectItem, name, id).then((len)=>{
works--;
if(len == 0 && works == 0) main_resolve(template);
})
})
resolve(docs[0].definition[0].property.length)
}
})
})
}
})
}
});
Related
is there a possible way to put the object Json outsite the object
is there a way to change this json "forms": [ { "_id": "Untitled Form", "title": "Untitled Form", "answer": [ { "username": "jansenstan2410#gmail.com", "date": "2022-11-02", "formId": "6361c5aaf7a02c177ebebb27", "answers": { "test": [ "New Option" ], "email": "john#nabatisnack.com", "dropdown": "New Option", "radio": "3", "date": "2022-11-01T00:00:00.000Z", "time": "17:25" } }, { "username": "adam#wegodev.coms", "date": "2022-11-03", "formId": "6361c5aaf7a02c177ebebb27", "answers": { "test": [ "New Option" ], "email": "123123.12312#e.com", "dropdown": "3", "radio": "3", "date": "2022-11-16T00:00:00.000Z", "time": "09:44" } } ] } ] i try to loop it using javascript but it dont return the same value as i expected and only increasing the value into the array.., i try some logic but it wont return the same value as i want also const newData = forms.map((item) => { var mappedAns = item.answer.map((data) => { let data_fix = {}; Object.keys(data.answers).forEach((key) => { data_fix[ key .replace(/[{()}]/g, "") .replace(/ :/g, "") .replace(/ /g, "_") .toLowerCase() ] = data.answers[key]; }); return { ...data, data_fix }; }); return { ...item, answer: mappedAns }; });
const newData = forms.map((item) => { var mappedAns = item.answer.map((data) => { Object.keys(data.answers).forEach((key) => { data[ key .replace(/[{()}]/g, "") .replace(/ :/g, "") .replace(/ /g, "_") .toLowerCase() ] = data.answers[key]; }); delete data.answers; return { ...data }; }); return { ...item, answer: mappedAns }; });
How in JS to merge in one object two json objects where the ID of on object correspond on the same ID of the second object
My question relates to the fact I'm querying 2 different objects from DB and the result is in JSON. I need to merge them into one. The 2 objects have in common this two key/value IRBId = ... and id = ... and they look as an example OBJ 1 { "data":{ "IRBs":{ "nodes":[ { "id":"8", "name":"Admin ", }, { "id":"9", "name":"Again", } ], } } } OBJ 2 { "data":{ "informedConsentForms":{ "count":3, "nodes":[ { "id":"93", ... "IRBId":"9", }, { "id":"92", ... "IRBId":"8", }, { "id":"91", ... "IRBId":"8", } ], } }, As you will see above OBJ 2 and OBJ 1 corresponding with the same at IRBid and id. What I need is to merge the two OBJ where IRBId OBJ 2 === id OBJ 1 The result I would expect after the merge is OBJ merged { [{ "id":"93", ... "IRBId":"9", "irb": { "name":"Again ", ... } }, { "id":"92", ... "IRBId":"8", "irb": { "name":"Admin ", ... } }, { "id":"91", ... "IRBId":"8", "irb": { "name":"Admin ", ... } ], }, I don't know how to make it looks like this.
Try using Array.reduce Logic Loop through second object data nodes Find the matching nodes from object 1 data nodes. Push to accumulator with required details. (I have added only the nodes that was mentioned in in Expected resut, you can add asmuch as you need.) const obj1 = { "data": { "IRBs": { "nodes": [ { "id": "8", "name": "Admin ", }, { "id": "9", "name": "Again", } ], } } } const obj2 = { "data": { "informedConsentForms": { "count": 3, "nodes": [ { "id": "93", "IRBId": "9", }, { "id": "92", "IRBId": "8", }, { "id": "91", "IRBId": "8", } ], } }, }; const obj1List = obj1.data.IRBs.nodes; const output = obj2.data.informedConsentForms.nodes.reduce((acc, curr) => { const matchingNode = obj1List.find((item) => item.id === curr.IRBId); if (matchingNode) { acc.push({ id: curr.id, IRBId: curr.IRBId, irb: { name: matchingNode.name } }) } return acc; }, []); console.log(output);
You need to use the map function on the nodes in the first object to construct a new object that contains the second and first object's attributes. const obj1 = { "data": { "IRBs": { "nodes": [{ "id": "8", "obj1": "one", "name": "Admin ", }, { "id": "9", "obj1": "two", "name": "Again", } ] } } }; const obj2 = { "data": { "informedConsentForms": { "count": 3, "nodes": [{ "id": "93", "obj2": "1", "IRBId": "9", }, { "id": "92", "obj2": "2", "IRBId": "8", }, { "id": "91", "obj2": "3", "IRBId": "8", } ], } } }; const obj1Data = obj1.data.IRBs.nodes; const obj2Data = obj2.data.informedConsentForms.nodes; const res = obj2Data.map(item => { const obj1Item = obj1Data.find(obj1Item => item.IRBId === obj1Item.id); return obj1Item ? { ...item, "irb": { ...obj1Item}} : { ...item}; }); console.log(res);
i am using nested loop, try this one const obj2 = { "data":{ "informedConsentForms":{ "count":3, "nodes":[ { "id":"93", "IRBId":"9", }, { "id":"92", "IRBId":"8", }, { "id":"91", "IRBId":"8", } ], } }, } const obj1 = { "data":{ "IRBs":{ "nodes":[ { "id":"8", "name":"Admin ", }, { "id":"9", "name":"Again", } ], } } } const result = []; const obj2Nodes = obj2.data.informedConsentForms.nodes; for(let i = 0; i < obj2Nodes.length; i++) { const obj1Nodes = obj1.data.IRBs.nodes for(let j = 0; j < obj1Nodes.length; j++) { if(obj2Nodes[i].IRBId === obj1Nodes[j].id) { const {id, ...reObj1Nodes} = obj1Nodes[j]; result.push({ ...obj2Nodes[i], 'irb': { ...reObj1Nodes } }) } } } console.log(result)
Skip JSON.map() for the subsequent elements
DEMO (Please check the browser console for output) I have a JSON customerItemResponse in a format { "totalResults": someNumber, "results": [ { "totalItem": 406, "customerId": "10000" }, { "totalItem": 468, "customerId": "10001" }, { "totalItem": 20, "customerId": "10002" }, ... Then I have another JSON customerInfo: { "totalResults": someNumber, "results": [ { "customerId": "10000", "region": "4", "area": "42", }, { "customerId": "10001", "region": "4", "area": "43", }, { "customerId": "10002", "region": "5", "area": "52", }, Now I have to create a JSON in a format [ { region:'4' regionDetails:[ { area:'42' customerDetails:[ { customerId:'10000' totalItem:406 }, { customerId:'10005' totalItem:301 }, ] }, { area:'11' customerDetails:[ { customerId:'10010' totalItem:11 }, { customerId:'10021' totalItem:105 }, ] }, ] }, { region:'5' regionDetails:[ { area:'52' customerDetails:[ { customerId:'10002' totalItem:52 }, { customerId:'10027' totalItem:310 }, ] }, { area:'41' customerDetails:[ { customerId:'10017' totalItem:109 }, { customerId:'10041' totalItem:450 }, ] }, ] } ] This is the logic I have written: customerData=<CustomerDataInterface[]>[] mapJson() { this.customerItemResponse.map((res, index) => { this.customerInfo.find((obj) => { if (obj.customerId == res.customerId) { this.customerData.length ? this.customerData.map((data, index1) => { if (data.region == obj.region) { data.regionDetails.length ? data.regionDetails.map((regDetails, index2) => { if (regDetails.area == obj.area) { regDetails.dealerDetails.push({ customerId: obj.customerId, totalItem: res.totalItem, }); return; } if (index2 == data.regionDetails.length - 1) { data.regionDetails.push({ area: obj.area, dealerDetails: [] }); } }) : data.regionDetails.push({ area: obj.area, dealerDetails: [] }); return; } if (index1 == this.customerData.length - 1) { this.customerData.push({ region: obj.region, regionDetails: [] }); } }) : this.customerData.push({ region: obj.region, regionDetails: [] }); } }); }); console.log(this.customerData); } Now the output of the console has several region repeated. And suppose if I have 6 unique region but the this.customerData.length is 31. I think return; is not working as expected. And is not skipping the subsequent element.
here is an efficient way to resolving the issue using js Maps. We can build maps with info about corresponding region and then areas. and after the data is built into maps - convert it back to simple js structures, such as object and arrays mapJson() { const customerToTotalMap = new Map(this.customerItemResponse.map(({customerId, totalItem}) => [customerId, totalItem])); const regionsMap = new Map(); for(let {customerId, region, area} of this.customerInfo) { let regionAreas; if(regionsMap.has(region)) { regionAreas = regionsMap.get(region); } else { regionAreas = new Map(); regionsMap.set(region, regionAreas); } let areaInfo; if(regionAreas.has(area)) { areaInfo = regionAreas.get(area); } else { areaInfo = []; regionAreas.set(area, areaInfo); } areaInfo.push({customerId, totalItem: customerToTotalMap.get(customerId)}); } this.customerData = [...regionsMap.entries()].map(([region, areas]) => ({ region, regionDetails: [...areas.entries()].map(([area, customerDetails]) => ({ area, customerDetails })) })) console.log(this.customerData); }
This is similar to #Andrei's answer. It creates an object literal as mapper. Also, it uses mapping between the region and area when they are created. So, finally you can just get the values of the regionMapper object without going through the mapper objects again const customerItemResponse=[{customerId:10000,totalItem:77},{customerId:10001,totalItem:37},{customerId:10002,totalItem:295},{customerId:10003,totalItem:458},{customerId:10004,totalItem:248},{customerId:10005,totalItem:35},{customerId:10006,totalItem:280},{customerId:10007,totalItem:147},{customerId:10008,totalItem:439},{customerId:10009,totalItem:401},{customerId:10010,totalItem:489},{customerId:10011,totalItem:414},{customerId:10012,totalItem:287},{customerId:10013,totalItem:391},{customerId:10014,totalItem:125},{customerId:10015,totalItem:207},{customerId:10016,totalItem:197},{customerId:10017,totalItem:151},{customerId:10018,totalItem:225},{customerId:10019,totalItem:333},{customerId:10020,totalItem:361},{customerId:10021,totalItem:225},{customerId:10022,totalItem:242},{customerId:10023,totalItem:150},{customerId:10024,totalItem:52},{customerId:10025,totalItem:475},{customerId:10026,totalItem:494},{customerId:10027,totalItem:30},{customerId:10028,totalItem:189},{customerId:10029,totalItem:112},{customerId:10030,totalItem:482},{customerId:10031,totalItem:283},{customerId:10032,totalItem:159},{customerId:10033,totalItem:440},{customerId:10034,totalItem:461},{customerId:10035,totalItem:76},{customerId:10036,totalItem:84},{customerId:10037,totalItem:392},{customerId:10038,totalItem:296},{customerId:10039,totalItem:293},{customerId:10040,totalItem:135},{customerId:10041,totalItem:348},{customerId:10042,totalItem:338},{customerId:10043,totalItem:444},{customerId:10044,totalItem:15},{customerId:10045,totalItem:32},{customerId:10046,totalItem:67},{customerId:10047,totalItem:277},{customerId:10048,totalItem:65},{customerId:10049,totalItem:95},{customerId:10050,totalItem:290}], customerInfo=[{customerId:10000,region:"3",area:"32"},{customerId:10001,region:"2",area:"22"},{customerId:10002,region:"2",area:"25"},{customerId:10003,region:"3",area:"31"},{customerId:10004,region:"2",area:"25"},{customerId:10005,region:"1",area:"11"},{customerId:10006,region:"1",area:"14"},{customerId:10007,region:"5",area:"55"},{customerId:10008,region:"5",area:"51"},{customerId:10009,region:"4",area:"45"},{customerId:10010,region:"1",area:"14"},{customerId:10011,region:"1",area:"12"},{customerId:10012,region:"3",area:"33"},{customerId:10013,region:"2",area:"25"},{customerId:10014,region:"4",area:"41"},{customerId:10015,region:"3",area:"32"},{customerId:10016,region:"5",area:"55"},{customerId:10017,region:"2",area:"23"},{customerId:10018,region:"3",area:"33"},{customerId:10019,region:"5",area:"51"},{customerId:10020,region:"4",area:"42"},{customerId:10021,region:"1",area:"12"},{customerId:10022,region:"1",area:"14"},{customerId:10023,region:"1",area:"14"},{customerId:10024,region:"1",area:"13"},{customerId:10025,region:"4",area:"45"},{customerId:10026,region:"3",area:"34"},{customerId:10027,region:"2",area:"24"},{customerId:10028,region:"4",area:"45"},{customerId:10029,region:"2",area:"22"},{customerId:10030,region:"2",area:"22"},{customerId:10031,region:"2",area:"21"},{customerId:10032,region:"3",area:"33"},{customerId:10033,region:"1",area:"11"},{customerId:10034,region:"3",area:"33"},{customerId:10035,region:"3",area:"32"},{customerId:10036,region:"2",area:"22"},{customerId:10037,region:"4",area:"41"},{customerId:10038,region:"3",area:"31"},{customerId:10039,region:"5",area:"51"},{customerId:10040,region:"2",area:"23"},{customerId:10041,region:"4",area:"45"},{customerId:10042,region:"1",area:"14"},{customerId:10043,region:"5",area:"54"},{customerId:10044,region:"3",area:"34"},{customerId:10045,region:"5",area:"51"},{customerId:10046,region:"4",area:"42"},{customerId:10047,region:"5",area:"53"},{customerId:10048,region:"1",area:"11"},{customerId:10049,region:"3",area:"35"},{customerId:10050,region:"5",area:"51"}]; const customerItemMapper = {} for (const c of customerItemResponse) customerItemMapper[c.customerId] = c.totalItem const regionMapper = {}, areaMapper = {}; for (const { customerId, region, area } of customerInfo) { let regionKey = `Region_${region}`, areaKey = `Area_${area}`, totalItem = customerItemMapper[customerId]; if (!(regionKey in regionMapper)) regionMapper[regionKey] = { region, regionDetails: [] } if (!(areaKey in areaMapper)) { const o = { area, customerDetails: [] } areaMapper[areaKey] = o; regionMapper[regionKey].regionDetails.push(o) // area-region relation } areaMapper[areaKey].customerDetails.push({ customerId, totalItem }) } console.log(Object.values(regionMapper))
How to parse FractalTransformer with normalizr
I'm trying to use paularmstrong/normalizr on JSON that comes from FractalTransformer and whose nested childs have "data" attribute. Example of JSON: { "data": { "object": "Offer", "id": "5g6aqocew4qjzl40", "real_id": 26, "name": "Random Name", "created_at": { "date": "2019-06-18 11:13:08.000000", "timezone_type": 3, "timezone": "UTC" }, "readable_created_at": "1 year ago", "site": { "data": { "object": "Site", "id": "65zody8vj29vlegd", "name": "Test Site", "real_id": 1 } }, "countries": { "data": [ { "object": "Country", "code": "US", "name": "United States" }, { "object": "Country", "code": "DE", "name": "Germany" } ] } }, "meta": { "include": [ "site", "countries" ], "custom": [] } } Schemas I use: export const offerSchema = new schema.Entity('offers') export const siteSchema = new schema.Entity('sites', {}, { processStrategy: (value) => { return { ...value.data } }, idAttribute: (value) => { return value.data.id }, }) export const countrySchema = new schema.Entity('countries') offerSchema.define({ site: siteSchema, countries: [countrySchema], }) Now the issue is that I remove 'data' from the site since it's just one object successfully, but I can't do it in the country case. Whatever I tried with custom processStrategy fails, as country is object that has data which is array (I assume this is where the issue is, going from Entity to Array). And in idAttribute function I always get complete array so can't determine the ID of single entry. So the end result is that the ID of countries is undefined. Any ides?
I actually managed with another approach. I added processStrategy on the parent, 'Offer' in this case, so all 'data' parts get stripped before they reach other child schemas. const normalizrStripDataOptions = { processStrategy: (value) => { const ret = { ...value } Object.keys(ret).forEach((key) => { if (ret[key] !== null) { if (ret[key].data && Array.isArray(ret[key].data)) { ret[key] = [...ret[key].data] } if (ret[key].data && typeof ret[key].data === 'object') { ret[key] = { ...ret[key].data } } } }) return ret }, } export const offerSchema = new schema.Entity('offers', {}, normalizrStripDataOptions) export const siteSchema = new schema.Entity('sites') export const countrySchema = new schema.Entity('countries') offerSchema.define({ site: siteSchema, countries: [countrySchema], })
How i can get data from another object?
Plunker I have two structures - ingredients and recipes [{ "id":"1", "name": "Cucumber" }, .. ] and [{ "id":"1", "name": "Salad1", "recipein":[1, 3, 5] }, { ... } ] and i want to show names of ingredients in each salad by press a button. I filtered object to get ID of object, then i try to get a array of ingredients getSalad(param:number) { this.saladId = this.recipe.filter(rec => { return rec.id.includes(param); }) this.getNameOfIngredients(this.saladId) } getNameOfIngredients(saladArray:any) { var ingredientsId = saladArray.map(function(num) { return num.recipein; }); i getting array [1,2,4] now i want to show all names of ingredients from this.ingredients with this array of id's. How can i do this? Plunker
I made updates in your plunker. I think thats what are you looking for: Plunker getSalad(param:number) { this.saladId = this.recipe.filter(rec => +rec.id === param )[0]; if(!this.saladId){ this.currentSalad = "Salad not found"; return; } this.currentSalad = this.getNameOfIngredients(this.saladId) } getNameOfIngredients(saladArray:any) { return this.ingredients.filter( ing => { return saladArray.recipein.indexOf(+ing.id) !== -1; });
let _ingredients = [] this.ingredients.foreach((ingr)=>{ if(this.ingreIDArry.indexof(ingr.id) > -1){ _ingredients.push(ingr.name) } }) return _ingredients is this what you want?
if you can flatten the array, it would be very straightforward for us to do lookups. Here is what you could do. const salads = [{ "id": "1", "name": "Salad1", "recipein": [1, 3, 5] }]; const ingredients = [{ "id": "1", "name": "Cucumber" }, { "id": "2", "name": "Cucumber2" }, { "id": "3", "name": "Cucumber3" }, { "id": "4", "name": "Cucumber4" }, { "id": "5", "name": "Cucumber5" } ]; const flattenIngredients = (() => { const output = {}; ingredients.forEach((ingredient) => { output[ingredient.id] = ingredient; }); return output; })(); const getSalad = (saladId) => { const filteredSalad = salads.filter((salad) => { return saladId == salad.id; }); if (filteredSalad.length > 0) { const salad = filteredSalad[0]; return salad.recipein.map((receip) => flattenIngredients[receip].name); } } console.log(getSalad(1));