How to combine nested duplicate dictionary values within an array - javascript

I have a following data structure, array of dictionaries
[
{
"id": 1,
"name": "Some Name 1",
"topics": [
{
"id": 3,
"name": "Topic Name",
"topics": [],
},
{
"id": 3,
"name": "Topic Name",
"topics": [],
}
]
},
{
"id": 2,
"name": "Some Name 2",
"topics": [
{
"id": 3,
"name": "Topic Name",
"topics": [],
},
{
"id": 3,
"name": "Topic Name",
"topics": [],
}
]
},
[
{
"id": 1,
"name": "Some Name 2",
"topics": [
{
"id": 1,
"name": "Topic Name 1",
"topics": [],
},
{
"id": 2,
"name": "Topic Name 2",
"topics": [],
}
]
},
{
"id": 2,
"name": "Some Name 1",
"topics": [
{
"id": 3,
"name": "Topic Name",
"topics": [],
},
{
"id": 3,
"name": "Topic Name",
"topics": [],
}
]
},
I want to essentially be able to combine the arrays within each dictionary if I have the same id, so for each dictionary i will have higher level dictionary items being the same and essentially combine those together to avoid duplication. Preferably i would like it to be recursively being that in case the topics within topics are also duplicate (you can assume the order is always respected).
I would appreciate if you could share a solution to this

Related

typescript: traverse nested array of objects until condition is met

I have nested array like following.
const tree = {
"id": 1,
"name": "mainOrgName",
"children": [
{
"id": 10,
"name": "East Region",
"children": [
{
"id": 11,
"name": "test east sub 1",
"children": [
{
"id": 12,
"name": "sub 22 sub 1",
"children": [
{
"id": 15,
"name": "sub 333 of sub ",
"children": [
{
"id": 16,
"name": "sub 4444",
"children": []
}
]
}
]
}
]
}
]
},
{
"id": 13,
"name": "west region",
"children": [
{
"id": 14,
"name": "test west sub 1",
"children": []
}
]
}
]
}
I need to traverse through tree.children array to get all id and name of sub arrays and its children until we didn't find children array empty. (Note: children array may be empty or may have further levels)
I need result like follows
Expected result
[
{
"name": "East Region",
"value": 10,
"selected": false
},
{
"name": "test east sub 1",
"value": 11,
"selected": false
},
{
"name": "sub 22 sub 1",
"value": 12,
"selected": false
},
{
"name": "sub 333 of sub",
"value": 15,
"selected": false
},
{
"name": "sub 4444",
"value": 16,
"selected": false
},
{
"name": "west region",
"value": 13,
"selected": false
},
{
"name": "test west sub 1",
"value": 14,
"selected": false
},
]
I tried following
const candidates = tree.children.map(org => ({name: org.name, value: org.id, selected: false}));
but it gives me following
[
{
"name": "East Region",
"value": 10,
"selected": false
},
{
"name": "west region",
"value": 13,
"selected": false
}
]
I am trying to get that but not sure how I can put condition that traverse until children is empty and push required fields in final array in required format. May need recursive/call back functions but not sure how I can use that.
Please help to get expected result. Thanks
Try this,
const tree = {
"id": 1,
"name": "mainOrgName",
"children": [
{
"id": 10,
"name": "East Region",
"children": [{
"id": 11,
"name": "test east sub 1",
"children": [{
"id": 12,
"name": "sub 22 sub 1",
"children": [{
"id": 15,
"name": "sub 333 of sub ",
"children": [{
"id": 16,
"name": "sub 4444",
"children": []
}]
}]
}]
}]
},
{
"id": 13,
"name": "west region",
"children": [{
"id": 14,
"name": "test west sub 1",
"children": []
}]
}
]
}
let items = []
let result = lootIt(tree.children)
console.log(result)
function lootIt (arr) {
for(let i = 0 ; i< arr.length ; i++){
let obj = {}
obj['name'] = arr[i]['name']
obj['value'] = arr[i]['id']
obj['selected'] = false
items.push(obj)
if(arr[i].children !== 0){
lootIt(arr[i].children)
}
}
return items
}
You can use recursion to do it
const tree = {
"id": 1,
"name": "mainOrgName",
"children": [
{
"id": 10,
"name": "East Region",
"children": [
{
"id": 11,
"name": "test east sub 1",
"children": [
{
"id": 12,
"name": "sub 22 sub 1",
"children": [
{
"id": 15,
"name": "sub 333 of sub ",
"children": [
{
"id": 16,
"name": "sub 4444",
"children": []
}
]
}
]
}
]
}
]
},
{
"id": 13,
"name": "west region",
"children": [
{
"id": 14,
"name": "test west sub 1",
"children": []
}
]
}
]
}
function mapTree(children){
let result =[]
for(c of children){
result.push({name: c.name, value: c.id, selected: false})
result = result.concat(mapTree(c.children))
}
return result
}
console.log(mapTree(tree.children))

Need to modify BE response to expected response at FE

BE Response:- which I am getting from Backend Service
{
"data": {
"type": "AnyType",
"resources": [
{
"id": 1,
"treeId": "1",
"name": "name1",
"description": "description1",
"children": [
{
"id": 3,
"treeId": "1-3",
"name": "subName1",
"description": "subDescription1",
"children": [
{
"id": 6,
"treeId": "1-3-6",
"name": "subSubName1",
"description": "subSubDesc1",
"children": []
}
]
}
]
},
{
"id": 2,
"treeId": "2",
"name": "name2",
"description": "description2",
"children": [
{
"id": 7,
"treeId": "2-7",
"name": "subName2",
"description": "subDescription2",
"children": []
}
]
}
]
}
}
But I need to modify this response to as below on FE
Expected Response:- means I need to join name and description field text to one(in name field ) as below:-
{
"data": {
"type": "AnyType",
"resources": [
{
"id": 1,
"treeId": "1",
"name": "name1-description1",
"description": "description1",
"children": [
{
"id": 3,
"treeId": "1-3",
"name": "subName1-subDescription1",
"description": "subDescription1",
"children": [
{
"id": 6,
"treeId": "1-3-6",
"name": "subSubName1-subSubDesc1",
"description": "subSubDesc1",
"children": []
}
]
}
]
},
{
"id": 2,
"treeId": "2",
"name": "name2-description2",
"description": "description2",
"children": [
{
"id": 7,
"treeId": "2-7",
"name": "subName2-subDescription2",
"description": "subDescription2",
"children": []
}
]
}
]
}
}
there could be n number of children of each object and children can have an array of objects.
What I have done:- I am able to change the very first name but not children name
let resDataArry = [];
let descData: DynamicResource;
response.forEach((x, index) => {
const descName = x.name + ' ' + x.description;
descData = { ...tree.resources[index], name: descName };
resDataArry.push(descData);
});
return resDataArry;
Please help.
You can use nested Array#forEach to access children array and then concatenate the name and description together.
let data = {
"data": {
"type": "AnyType",
"resources": [
{
"id": 1,
"treeId": "1",
"name": "name1",
"description": "description1",
"children": [
{
"id": 3,
"treeId": "1-3",
"name": "subName1",
"description": "subDescription1",
"children": [
{
"id": 6,
"treeId": "1-3-6",
"name": "subSubName1",
"description": "subSubDesc1",
"children": []
}
]
}
]
},
{
"id": 2,
"treeId": "2",
"name": "name2",
"description": "description2",
"children": [
{
"id": 7,
"treeId": "2-7",
"name": "subName2",
"description": "subDescription2",
"children": []
}
]
}
]
}
}
data.data.resources.forEach(function(item){
item.name = item.name + ' ' + item.description;
item.children.forEach(function(child){
child.name = child.name + ' ' + child.description;
});
});
console.log(data);

Not able to display the json data in coming from service in the below form in angular mat-table

I have data coming from service in the below json array.
"data": [
{
"id": 1,
"roneName": "1",
"description": "Test1",
"companyId": 101,
"permissionList": [
{
"id": 1,
"name": "permission 1",
"description": "",
},
{
"id": 2,
"name": "permission 2",
"description": ""
},
{
"id": 3,
"name": "permission 3",
"description": ""
},
{
"id": 4,
"name": "permission 4",
"description": ""
}
]
},
{
"id": 2,
"roneName": "Test2",
"description": "Test2",
"companyId": 101,
"permissionList": [
{
"id": 1,
"name": "permission 1",
"description": ""
}
]
},
{
"id": 3,
"name": "Test 3",
"description": "Test 3",
"companyId": 101,
"permissionList": [
{
"id": 1,
"name": "Test1",
"description": ""
},
{
"id": 2,
"name": "test 2",
"description": ""
},
{
"id": 3,
"name": "test 3",
"description": "test 3"
}
]
}
]
i want to display data in the angular mat-table in below format
**Permission Name** **roleName1** **roleName2** **roleName3**
permission 1 checkbox checked checkbox checked checkbox checked
permission 2 checkbox unchecked checkbox checked checkbox unchecked
permission 3 checkbox checked checkbox unchecked checkbox checked

How to get size of array embedded array mongoose?

I have a category collection and I want to get category by id with some options. This is collection's structure in database.
[
{
"_id": "5d67296bf35b984e74486924",
"name": "Dinrk",
"images": [],
"recipes": [
{
"name": "Coffee",
"time": 20,
"img": "https://google.com/image-example.jpg",
"des": "This is description",
"serving": 2,
"components": [
{
"name": "Dink 1",
"quantity": "1"
},
{
"name": "Dink 2",
"quantity": "1"
},
{
"name": "Dink 2",
"quantity": "1"
}
],
"cook_steps": [
{
"des": "This is description",
"pictures": []
},
{
"des": "This is description",
"pictures": []
}
]
},
{
"name": "Coffee",
"time": 20,
"img": "https://google.com/image-example.jpg",
"des": "This is description",
"serving": 2,
"components": [
{
"name": "Dink 1",
"quantity": "1"
},
{
"name": "Dink 2",
"quantity": "1"
}
],
"cook_steps": [
{
"des": "This is description",
"pictures": []
},
{
"des": "This is description",
"pictures": []
}
]
}
]
},
{
"_id": "5d67296bf35b984e74486435555",
"name": "Cake",
"images": [],
"recipes": [
{
"name": "Cake",
"time": 20,
"img": "https://google.com/image-example.jpg",
"des": "This is description",
"serving": 2,
"components": [
{
"name": "Cake 1",
"quantity": "1"
},
{
"name": "Cake 2",
"quantity": "1"
},
{
"name": "Cake 2",
"quantity": "1"
}
],
"cook_steps": [
{
"des": "This is description",
"pictures": []
},
{
"des": "This is description",
"pictures": []
}
]
},
{
"name": "Coffee",
"time": 20,
"img": "https://google.com/image-example.jpg",
"des": "This is description",
"serving": 2,
"components": [
{
"name": "Cake 1",
"quantity": "1"
}
],
"cook_steps": [
{
"des": "This is description",
"pictures": []
},
{
"des": "This is description",
"pictures": []
}
]
}
]
}
]
This is my code to try categoryId = "5d67296bf35b984e74486924"
Category.aggregate([
{
$match: {'_id': categoryId}
},
{
$unwind: '$recipes'
},
{
$project: {
'total_components': {'$size': '$recipes.components'},
'total_cook_steps': {'$size': '$recipes.cook_steps'}
}
}
]).then(function(data) {
}, function(err) {
})
And expected result is
{
"_id": "5d67296bf35b984e74486924",
"name": "Dinrk",
"images": [],
"recipes": [
{
"name": "Coffee",
"time": 20,
"img": "https://google.com/image-example.jpg",
"des": "This is description",
"serving": 2,
"total_components": 3,
"total_cook_steps": 2
},
{
"name": "Coffee",
"time": 20,
"img": "https://google.com/image-example.jpg",
"des": "This is description",
"serving": 2,
"total_components": 2,
"total_cook_steps": 2
}
]
}
But when I run above my code, result is [].
If you understand my problem, please help me. I have search a lot, but not found solution. So I want to ask everyone. Thankyou so much.
Your query is not giving you the desired result since Mongoose does not auto-cast the 24 char hex string to ObjectId in its aggregate pipeline since $project and $group can change the schema in surprising ways that it becomes hard to infer what should be an ObjectId.
You need to manually convert the categoryId
string to ObjectId using the mongoose.Types.ObjectId method.
Compute the new fields within a $map operator instead of $unwind as this allows you an aggregate operation with fewer pipeline steps
Category.aggregate([
{ '$match': { '_id': mongoose.Types.ObjectId(categoryId) } },
{ '$addFields': {
'recipes': {
'$map': {
'input': '$recipes',
'in': {
'name': '$$this.name',
'time': '$$this.time',
'img': '$$this.img',
'des': '$$this.des',
'serving': '$$this.serving',
'total_components': { '$size': '$$this.components' },
'total_cook_steps': { '$size': '$$this.cook_steps' }
}
}
}
} }
]).then(function(data) {
}, function(err) {
})

Iterate through each object array based on condition check

I have a scenario were I need to iterate through each parent/child array in an object.
Each Grandparents can have multiple parents, in same fashion each parent can have multiple childs, each child can have multiple subChilds and so on.
I need to check if type is "parent" or "child" while iterating and then push name property to an array as mentioned in expected output.
Input Object:
var inputObject = {
"id": 1,
"name": "Grand Parent 1",
"type": "GrandParent",
"childType": [
{
"id": 2,
"type": "Parent",
"childType": [
{
"id": 3,
"type": "Child",
"childType": [],
"name": "Child 11"
},
{
"id": 4,
"type": "Child",
"childType": [],
"name": "Child 12"
}
],
"name": "Parent 1"
},
{
"id": 5,
"type": "Parent",
"childType": [
{
"id": 6,
"type": "Child",
"childType": [],
"name": "Child 21"
}
],
"name": "Parent 2"
},
{
"id": 7,
"type": "Parent",
"childType": [
{
"id": 8,
"type": "Child",
"childType": [],
"name": "Child 31"
}
],
"name": "Parent 3"
}
]
}
Code Tried:
function handleData({childType, ...rest}){
const res = [];
res.push(rest.name);
if(childType){
if(rest.type == "Child")
res.push(...handleData(childType));
}
return res;
}
const res = handleData(inputObject);
Expected Output:
If type selected is "Parent"
["Parent 1", "Parent 2, Parent 3"]
if type selected is "Child"
["Child 11", "Child 12", "Child 21", "Child 31"]
You can do a recursive function that uses flatMap():
const obj = {id:1,name:"Grand Parent 1",type:"GrandParent",childType:[{id:2,type:"Parent",childType:[{id:3,type:"Child",childType:[],name:"Child 11"},{id:4,type:"Child",childType:[],name:"Child 12"}],name:"Parent 1"},{id:5,type:"Parent",childType:[{id:6,type:"Child",childType:[],name:"Child 21"}],name:"Parent 2"},{id:7,type:"Parent",childType:[{id:8,type:"Child",childType:[],name:"Child 31"}],name:"Parent 3"}]};
const get = (o, t) => o.type === t ? [o.name] : o.childType.flatMap(c => get(c, t));
console.log(get(obj, 'GrandParent'));
console.log(get(obj, 'Parent'));
console.log(get(obj, 'Child'));
You can do that using recursion.
Steps:
Create a wrapper function in which you declare result array.
Inside that make a wrapper function create a function which would be called recursively
Check if type matches the given type add the name of object to result
Then check if elements exists in its childType. If yes yes then call function on each each of its element.
var inputObject = { "id": 1, "name": "Grand Parent 1", "type": "GrandParent", "childType": [ { "id": 2, "type": "Parent", "childType": [ { "id": 3, "type": "Child", "childType": [], "name": "Child 11" }, { "id": 4, "type": "Child", "childType": [], "name": "Child 12" } ], "name": "Parent 1" }, { "id": 5, "type": "Parent", "childType": [ { "id": 6, "type": "Child", "childType": [], "name": "Child 21" } ], "name": "Parent 2" }, { "id": 7, "type": "Parent", "childType": [ { "id": 8, "type": "Child", "childType": [], "name": "Child 31" } ], "name": "Parent 3" } ] }
function handleData(obj,type){
let res = [];
function recursive(obj){
if(type === obj.type) res.push(obj.name);
if(obj.childType.length){
obj.childType.forEach(a => recursive(a));
}
}
recursive(obj)
return res;
}
console.log(handleData(inputObject,"Child"))
console.log(handleData(inputObject,"Parent"))
Recursion is elegant but you could use es6 to do it , if the object childType was very large, recursion might not be applicable (stack overflow), here is a solution using reduce,
function getType({childType}, type) {
return childType.reduce( (acc, {type: objectType, name}) => {
if (objectType === type){
acc.push(name)
}
return acc
}, [])
}
You can solve this problem using recursion. You can try on this way !
var inputObject = {
"id": 1,
"name": "Grand Parent 1",
"type": "GrandParent",
"childType": [
{
"id": 2,
"type": "Parent",
"childType": [
{
"id": 3,
"type": "Child",
"childType": [],
"name": "Child 11"
},
{
"id": 4,
"type": "Child",
"childType": [],
"name": "Child 12"
}
],
"name": "Parent 1"
},
{
"id": 5,
"type": "Parent",
"childType": [
{
"id": 6,
"type": "Child",
"childType": [],
"name": "Child 21"
}
],
"name": "Parent 2"
},
{
"id": 7,
"type": "Parent",
"childType": [
{
"id": 8,
"type": "Child",
"childType": [],
"name": "Child 31"
}
],
"name": "Parent 3"
}
]
};
var resultValues = [];
function getResult(inputObject, propertyName, propertyValue) {
for (var objectProperty in inputObject) {
if (objectProperty == propertyName && inputObject[objectProperty] == propertyValue) {
resultValues.push(inputObject['name']);
console.log(resultValues);
} else {
if (objectProperty == 'childType') {
inputObject[objectProperty].forEach(function (element) {
getResult(element, propertyName, propertyValue)
});
}
}
}
//console.log(resultValues);
}
getResult(inputObject, 'type', 'GrandParent');
getResult(inputObject, 'type', 'Parent');
getResult(inputObject, 'type', 'Child');

Categories