I have the following JSON array I want to create a new field in every object which will be a count of the object
we have to get a count based on status, shop, and name(ownerDetails)
How can I achieve this and I have added my expected output below
var items = [
{
"id": 1,
"status": "ORANGE",
"Shop":"ABC",
"ownerDetails":[ {"name":"test1","address":"test1"}]
},
{
"id": 2,
"status": "GREEN",
"Shop":"ABC",
"ownerDetails":[ {"name":"test1","address":"test1"}]
},
{
"id": 3,
"status": "ORANGE",
"Shop":"ABC",
"ownerDetails":[ {"name":"test1","address":"test1"}]
},
{
"id": 4,
"status": "YELLOW",
"Shop":"ABC",
"ownerDetails":[ {"name":"test1","address":"test1"}]
},
{
"id": 5,
"status": "RED",
"Shop":"ABC",
"ownerDetails":[ {"name":"test1","address":"test1"}]
},
{
"id":6,
"status": "GREEN",
"Shop":"ABC",
"ownerDetails":[ {"name":"test1","address":"test1"}]
},
{
"id": 7,
"status": "GREEN",
"Shop":"XYZ",
"ownerDetails":[ {"name":"test2","address":"test2"}]
},
{
"id": 8,
"status": "ORANGE",
"Shop":"XYZ",
"ownerDetails":[ {"name":"test2","address":"test2"}]
},
{
"id": 9,
"status": "YELLOW",
"Shop":"ABC",
"ownerDetails":[ {"name":"test1","address":"test1"}]
},
{
"id": 10,
"status": "GREEN",
"Shop":"EFG",
"ownerDetails":[ {"name":"test3","address":"test3"}]
},
{
"id": 11,
"status": "GREEN",
"Shop":"EFG",
"ownerDetails":[ ]
}
]
Expected output: So based on each shop, status and name(ownerDetails) we have to count the object
[
{
"id": 1,
"status": "ORANGE"
"Shop":"ABC",
"ownerDetails":[ {"name":"test1","address":"test1"}],
"Count": 2
},
{
"id": 2,
"status": "GREEN"
"Shop":"ABC",
"ownerDetails":[ {"name":"test1","address":"test1"}],
"Count": 2
},
{
"id": 3,
"status": "ORANGE"
"Shop":"ABC",
"ownerDetails":[ {"name":"test1","address":"test1"}],
"Count": 2
},
{
"id": 4,
"status": "YELLOW"
"Shop":"ABC",
"ownerDetails":[ {"name":"test1","address":"test1"}],
"Count": 2
},
{
"id": 5,
"status": "RED"
"Shop":"ABC",
"ownerDetails":[ {"name":"test1","address":"test1"}],
"Count": 1
},
{
"id":6,
"status": "GREEN"
"Shop":"ABC",
"ownerDetails":[ {"name":"test1","address":"test1"}],
"Count": 2
},
{
"id": 7,
"status": "GREEN"
"Shop":"XYZ",
"ownerDetails":[ {"name":"test2","address":"test2"}],
"Count": 1
},
{
"id": 8,
"status": "ORANGE"
"Shop":"XYZ",
"ownerDetails":[ {"name":"test2","address":"test2"}],
"Count": 1
},
{
"id": 9,
"status": "YELLOW"
"Shop":"ABC",
"ownerDetails":[ {"name":"test1","address":"test1"}],
"Count": 2
},
{
"id": 10,
"status": "GREEN"
"Shop":"EFG"
"ownerDetails":[ {"name":"test3","address":"test3"}],
"Count": 1
},
{
"id": 11,
"status": "GREEN",
"Shop":"EFG",
"ownerDetails":[ ],
"Count": 1
}
]
Plese see demo
Thanks #Nico_ for your and #Parth Ravalhelp
Below code is not working when "ownerDetails":[ ] and I got the below error Cannot read properties of undefined (reading 'name') in console
code :
const itemsWithCount = items.map(item => ({
...item,
Count: items.filter(({ status, Shop ,ownerDetails: [{ name }]}) => item.status === status && item.Shop === Shop && item.ownerDetails[0].name === name).length
}));
console.log(itemsWithCount)
Deconstruction of a not defined object
/**
* Problem: The deconstructed object is not defined.
*
* This will throw: TypeError: Cannot read properties of undefined (reading 'name')
*
* This is roughly equivalent of doing const { name } = undefined
*/
const [{ name }] = [];
/**
* Solution: Assign a default value when the deconstructed object is not defined.
*
* If the deconstructing object is undefined,
* assign an empty object as the default value
*/
const [{ name } = {}] = [];
Accessing a property of an undefined object
If you don't check that the ownerDetails array isn't empty before comparing item.ownerDetails[0].name === name, you might end up trying to access a property of an object that doesn't exist. This will result in a TypeError like above.
To deal with this situation, you must:
Verify that there is at least one element in item.ownerDetails;
Verify that name property exists on the first element;
If the tests passed, returns the value of the name property for the first element of ownerDetails. Otherwise, you must provide a default value.
Working example
const items = [
{ id: 1, status: 'ORANGE', Shop: 'ABC', ownerDetails: [{ name: 'test1', address: 'test1' }] },
{ id: 2, status: 'GREEN', Shop: 'ABC', ownerDetails: [{ name: 'test1', address: 'test1' }] },
{ id: 3, status: 'ORANGE', Shop: 'ABC', ownerDetails: [{ name: 'test1', address: 'test1' }] },
{ id: 4, status: 'YELLOW', Shop: 'ABC', ownerDetails: [{ name: 'test1', address: 'test1' }] },
{ id: 5, status: 'RED', Shop: 'ABC', ownerDetails: [{ name: 'test1', address: 'test1' }] },
{ id: 6, status: 'GREEN', Shop: 'ABC', ownerDetails: [{ name: 'test1', address: 'test1' }] },
{ id: 7, status: 'GREEN', Shop: 'XYZ', ownerDetails: [{ name: 'test2', address: 'test2' }] },
{ id: 8, status: 'ORANGE', Shop: 'XYZ', ownerDetails: [{ name: 'test2', address: 'test2' }] },
{ id: 9, status: 'YELLOW', Shop: 'ABC', ownerDetails: [{ name: 'test1', address: 'test1' }] },
{ id: 10, status: 'GREEN', Shop: 'EFG', ownerDetails: [{ name: 'test3', address: 'test3' }] },
{ id: 11, status: 'GREEN', Shop: 'EFG', ownerDetails: [] }
];
const itemsWithCount = items.map(item => ({
...item,
/**
* If the first element of ownerDetails is not defined,
* assign an empty object as the default value for the first
* element of ownerDetails array.
*/
Count: items.filter(({ status, Shop, ownerDetails: [{ name } = {}] }) =>
item.status === status &&
item.Shop === Shop &&
(
(
/**
* 1. Check if ownerDetails is not empty.
*/
item.ownerDetails.length &&
/**
* 2. Check if the first element of item.ownerDetails has a name property.
*
* ESLint best practice:
* ------------------------------------------------------
* Disallow calling some Object.prototype methods directly on objects.
* For more details, see https://eslint.org/docs/rules/no-prototype-builtins
* ------------------------------------------------------
*/
Object.prototype.hasOwnProperty.call(item.ownerDetails[0], 'name') &&
/**
* 3. Accessing and returning name property.
*/
item.ownerDetails[0].name
/**
* Else, compare undefined with name property.
*/
) || undefined
) === name
).length
}));
console.log(itemsWithCount)
Output
[
{ Count: 2, id: 1, ownerDetails: [{ address: 'test1', name: 'test1' }], Shop: 'ABC', status: 'ORANGE' },
{ Count: 2, id: 2, ownerDetails: [{ address: 'test1', name: 'test1' }], Shop: 'ABC', status: 'GREEN' },
{ Count: 2, id: 3, ownerDetails: [{ address: 'test1', name: 'test1' }], Shop: 'ABC', status: 'ORANGE' },
{ Count: 2, id: 4, ownerDetails: [{ address: 'test1', name: 'test1' }], Shop: 'ABC', status: 'YELLOW' },
{ Count: 1, id: 5, ownerDetails: [{ address: 'test1', name: 'test1' }], Shop: 'ABC', status: 'RED' },
{ Count: 2, id: 6, ownerDetails: [{ address: 'test1', name: 'test1' }], Shop: 'ABC', status: 'GREEN' },
{ Count: 1, id: 7, ownerDetails: [{ address: 'test2', name: 'test2' }], Shop: 'XYZ', status: 'GREEN' },
{ Count: 1, id: 8, ownerDetails: [{ address: 'test2', name: 'test2' }], Shop: 'XYZ', status: 'ORANGE' },
{ Count: 2, id: 9, ownerDetails: [{ address: 'test1', name: 'test1' }], Shop: 'ABC', status: 'YELLOW' },
{ Count: 1, id: 10, ownerDetails: [{ address: 'test3', name: 'test3' }], Shop: 'EFG', status: 'GREEN' },
{ Count: 1, id: 11, ownerDetails: [], Shop: 'EFG', status: 'GREEN' }
]
var items = [{
"id": 1,
"status": "ORANGE",
"Shop": "ABC",
"ownerDetails": [{
"name": "test1",
"address": "test1"
}]
},
{
"id": 2,
"status": "GREEN",
"Shop": "ABC",
"ownerDetails": [{
"name": "test1",
"address": "test1"
}]
},
{
"id": 3,
"status": "ORANGE",
"Shop": "ABC",
"ownerDetails": [{
"name": "test1",
"address": "test1"
}]
},
{
"id": 4,
"status": "YELLOW",
"Shop": "ABC",
"ownerDetails": [{
"name": "test1",
"address": "test1"
}]
},
{
"id": 5,
"status": "RED",
"Shop": "ABC",
"ownerDetails": [{
"name": "test1",
"address": "test1"
}]
},
{
"id": 6,
"status": "GREEN",
"Shop": "ABC",
"ownerDetails": [{
"name": "test1",
"address": "test1"
}]
},
{
"id": 7,
"status": "GREEN",
"Shop": "XYZ",
"ownerDetails": [{
"name": "test2",
"address": "test2"
}]
},
{
"id": 8,
"status": "ORANGE",
"Shop": "XYZ",
"ownerDetails": [{
"name": "test2",
"address": "test2"
}]
},
{
"id": 9,
"status": "YELLOW",
"Shop": "ABC",
"ownerDetails": [{
"name": "test1",
"address": "test1"
}]
},
{
"id": 10,
"status": "GREEN",
"Shop": "EFG",
"ownerDetails": [{
"name": "test3",
"address": "test3"
}]
}
];
var mapData = items.map((data) => {
var getList = items.filter(word => word.Shop == data.Shop).length;
return {
id: data.id,
status: data.status,
Shop: data.Shop,
text: data.ownerDetails,
Count: getList
};
});
console.log(mapData);
Note:- Map Data and Counted Similar Shops....
Looping through the array for each element is not an efficient way to go about it. A cleaner and more efficient way would be to create an object with keys as the combination of the values you need to compare.
Following should help:
let items = [
{"id":1,"status":"ORANGE","Shop":"ABC","ownerDetails":[{"name":"test1","address":"test1"}]},
{"id":2,"status":"GREEN","Shop":"ABC","ownerDetails":[{"name":"test1","address":"test1"}]},
{"id":3,"status":"ORANGE","Shop":"ABC","ownerDetails":[{"name":"test1","address":"test1"}]},
{"id":4,"status":"YELLOW","Shop":"ABC","ownerDetails":[{"name":"test1","address":"test1"}]},
{"id":5,"status":"RED","Shop":"ABC","ownerDetails":[{"name":"test1","address":"test1"}]},
{"id":6,"status":"GREEN","Shop":"ABC","ownerDetails":[{"name":"test1","address":"test1"}]},
{"id":7,"status":"GREEN","Shop":"XYZ","ownerDetails":[{"name":"test2","address":"test2"}]},
{"id":8,"status":"ORANGE","Shop":"XYZ","ownerDetails":[{"name":"test2","address":"test2"}]},
{"id":9,"status":"YELLOW","Shop":"ABC","ownerDetails":[{"name":"test1","address":"test1"}]},
{"id":10,"status":"GREEN","Shop":"EFG","ownerDetails":[{"name":"test3","address":"test3"}]},
{"id":11,"status":"GREEN","Shop":"EFG","ownerDetails":[]}
]
let itemMap = {};
for (const item of items) {
const key = `${item.Shop}_${item.status}_${item.ownerDetails[0]?.name}`;
if (itemMap[key]) {
itemMap[key].push({ ...item, Count: itemMap[key][0].Count+1 });
for (const matchedItem of itemMap[key]) {
matchedItem.Count++;
}
} else {
itemMap[key] = [{ ...item, Count: 1 }];
}
}
let processedItems = [];
for (const items of Object.values(itemMap)) {
for (const item of items) {
processedItems.push(item);
}
}
console.log(processedItems);
Note: This would not work if the order of the objects is to be preserved
You can do it with a map to loop through your array (you need to add some coma before the "Shop" by the way.
const items = [
{
"id": 1,
"status": "ORANGE",
"Shop":"ABC",
"ownerDetails":[ {"name":"test1","address":"test1"}]
},
{
"id": 2,
"status": "GREEN",
"Shop":"ABC",
"ownerDetails":[ {"name":"test1","address":"test1"}]
},
{
"id": 3,
"status": "ORANGE",
"Shop":"ABC",
"ownerDetails":[ {"name":"test1","address":"test1"}]
},
{
"id": 4,
"status": "YELLOW",
"Shop":"ABC",
"ownerDetails":[ {"name":"test1","address":"test1"}]
},
{
"id": 5,
"status": "RED",
"Shop":"ABC",
"ownerDetails":[ {"name":"test1","address":"test1"}]
},
{
"id":6,
"status": "GREEN",
"Shop":"ABC",
"ownerDetails":[ {"name":"test1","address":"test1"}]
},
{
"id": 7,
"status": "GREEN",
"Shop":"XYZ",
"ownerDetails":[ {"name":"test2","address":"test2"}]
},
{
"id": 8,
"status": "ORANGE",
"Shop":"XYZ",
"ownerDetails":[ {"name":"test2","address":"test2"}]
},
{
"id": 9,
"status": "YELLOW",
"Shop":"ABC",
"ownerDetails":[ {"name":"test1","address":"test1"}]
},
{
"id": 10,
"status": "GREEN",
"Shop":"EFG",
"ownerDetails":[ {"name":"test3","address":"test3"}]
},
{
"id": 11,
"status": "GREEN",
"Shop":"EFG",
"ownerDetails":[ ]
}
];
const itemsWithCount = items.map(item => ({
...item,
Count: items.filter(({ status, Shop }) => item.status === status && item.Shop === Shop).length
}));
For each item of your array, you keep the current value of the item (the ...item) and you add a Count property, that will get the count for this item by filtering the array to keep only the item that have the same status and shop and you get the length of that array.
Related
I have array object like this, can you help me?
[{id: 1, name: "contractor"},
{id: 2, name: "owner", },
{id: 3, name: "manager", },
{id: 4, name: "customer", },
{id: 5, name: "admin",}]
I want a format like that:
if admin
[admin:
{id: 1, name: "contractor"},
{id: 2, name: "owner", },
{id: 3, name: "manager", },
{id: 4, name: "customer", }]
if contractor
[contractor:
{id: 2, name: "owner", },
{id: 3, name: "manager", },
{id: 4, name: "customer", }]
if owner
[owner:
{id: 3, name: "manager", },
{id: 4, name: "customer", }]
if manager
[manager:
{id: 4, name: "customer", }]
Based on the comments, i guess you need this
let array = [
{id: 1, name: "contractor"},
{id: 2, name: "owner"},
{id: 3, name: "manager"},
{id: 4, name: "customer"},
{id: 5, name: "admin"}
]
const admin = { admin: array.filter(obj => obj.id < 5) }
const contractor = { contractor: array.filter(obj => [2, 3, 4].includes(obj.id)) }
const owner = { owner: array.filter(obj => [3, 4].includes(obj.id)) }
const manager = { manager: array.filter(obj => obj.id === 4) }
console.log(admin, contractor, owner, manager)
IF you want to make it so it will only generate the array if it is included in the name, then you can use array.reduce
const array = [
{id: 1, name: "contractor"},
{id: 2, name: "owner"},
{id: 3, name: "manager"},
{id: 4, name: "customer"},
{id: 5, name: "admin"}
];
const objects = array.reduce((prev, curr) => {
if (prev[curr.name]) {
return {
...prev,
[prev[curr.name]]: prev[curr.name].concat(curr),
}
}
return {
...prev,
[curr.name]: [curr]
}
}, {});
console.log(objects);
You have to sort the data first, as you can see the below code. I hope this would be helpful. thanks
const array = [
{ id: 1, name: "contractor", sort: 2 },
{ id: 2, name: "owner", sort: 3 },
{ id: 3, name: "manager", sort: 4 },
{ id: 4, name: "customer", sort: 5 },
{ id: 5, name: "admin", sort: 1 }
];
let nameArr = [];
let arr = [];
let newArray = [];
const sortedData = array.sort((a, b) => a.sort - b.sort);
sortedData.forEach(ele => {
array.forEach(item => {
if (item.name !== ele.name) {
if (newArray.length) {
if (!nameArr.includes(item.name) && (item.name !== ele.name)) {
arr.push(item);
}
} else {
arr.push(item);
}
}
})
newArray.push({ [ele.name]: arr });
nameArr.push(ele.name);
arr = []
})
console.log(newArray)
Result will be this:
[
{
"admin": [
{
"id": 1,
"name": "contractor",
"sort": 2
},
{
"id": 2,
"name": "owner",
"sort": 3
},
{
"id": 3,
"name": "manager",
"sort": 4
},
{
"id": 4,
"name": "customer",
"sort": 5
}
]
},
{
"contractor": [
{
"id": 2,
"name": "owner",
"sort": 3
},
{
"id": 3,
"name": "manager",
"sort": 4
},
{
"id": 4,
"name": "customer",
"sort": 5
}
]
},
{
"owner": [
{
"id": 3,
"name": "manager",
"sort": 4
},
{
"id": 4,
"name": "customer",
"sort": 5
}
]
},
{
"manager": [
{
"id": 4,
"name": "customer",
"sort": 5
}
]
},
{
"customer": []
}
]
so ive been currently stuck on a JavaScript assignment for hours now.
so ive been given a Json file with the following data :
{
"owner": "Jun31d",
"info": [{ "id": 1, "name": "bob", "flavour": "vanilla", "age": 43 },
{ "id": 2, "name": "Juneid", "flavour": "Chocolate", "age": 19 },
{ "id": 3, "name": "Patrick", "flavour": "Strawberry", "age": 25 },
{ "id": 4, "name": "Jean", "flavour": "Mint", "age": 31 },
{ "id": 5, "name": "Ahmad", "flavour": "vanilla", "age": 18 },
{ "id": 6, "name": "Ali", "flavour": "Bubblegum", "age": 19 },
{ "id": 7, "name": "Frank", "flavour": "Pistachio", "age": 23 }
]
}
The instruction for my assigment is the following :
Instruction
So from what ive understood i need to create a function that holds two string parameters, And ill need to change the property inside of my object that is located in an array to a new property.
And until now heres what i did :
'use strict'
const iceCream = require('./main.json')
let namespace = {
changeProp : function (newprop,oldprop) {
for (let oldprop in iceCream.info) {
}
}
}
I know it is not much at all, but i just want some help to know how can i move forward a little bit more on my assigment.
Any help is appreciated, thank you
You can easily replace the oldKey with newKey using map, Object.keys, and reduce
const data = {
owner: "Jun31d",
info: [
{ id: 1, name: "bob", flavour: "vanilla", age: 43 },
{ id: 2, name: "Juneid", flavour: "Chocolate", age: 19 },
{ id: 3, name: "Patrick", flavour: "Strawberry", age: 25 },
{ id: 4, name: "Jean", flavour: "Mint", age: 31 },
{ id: 5, name: "Ahmad", flavour: "vanilla", age: 18 },
{ id: 6, name: "Ali", flavour: "Bubblegum", age: 19 },
{ id: 7, name: "Frank", flavour: "Pistachio", age: 23 },
],
};
function changePropertyName(arr, oldName, newName) {
return arr.map((o) => {
return Object.keys(o).reduce((acc, curr) => {
curr === oldName ? (acc[newName] = o[oldName]) : (acc[curr] = o[curr]);
return acc;
}, {});
});
}
console.log(changePropertyName(data.info, "flavour", "bestFlavour"));
/* This is not a part of answer. It is just to give the output fill height. So IGNORE IT */
.as-console-wrapper { max-height: 100% !important; top: 0; }
This is how you can do it. Easy, simple and nasty solution.
const data = {
owner: 'Jun31d',
info: [
{ id: 1, name: 'bob', flavour: 'vanilla', age: 43 },
{ id: 2, name: 'Juneid', flavour: 'Chocolate', age: 19 },
{ id: 3, name: 'Patrick', flavour: 'Strawberry', age: 25 },
{ id: 4, name: 'Jean', flavour: 'Mint', age: 31 },
{ id: 5, name: 'Ahmad', flavour: 'vanilla', age: 18 },
{ id: 6, name: 'Ali', flavour: 'Bubblegum', age: 19 },
{ id: 7, name: 'Frank', flavour: 'Pistachio', age: 23 }
]
};
const renameProperty = (a, e, n) =>
a.map((el) => {
el[n] = el[e];
delete el[e];
return el;
});
console.log(renameProperty(data.info, "id", "_id"));
I have below JSON data set
[
{
"campaignId": 111,
"campaignCategory": "Diabetes",
"result": [
{
"campaignType": 1,
"name": "tes1"
},
{
"campaignType": 1,
"name": "test22"
},
{
"campaignType": 3,
"name": "test33"
}
]
},
{
"campaignId": 222,
"campaignCategory": "Orthopedic",
"result": [
{
"campaignType": 1,
"name": "Orthopedic"
}
]
},
{
"campaignId": 333,
"campaignCategory": "Cardiology",
"result": [
{
"campaignType": 3,
"name": "Cardiology"
},
{
"campaignType": 1,
"name": "Cardiology 123"
}
]
}
]
I have tired below filter but that doesn't returned desired data.
_.filter(summary, function (data) {
return (post, _.filter(data.result, {'campaignType': 3}));
I want to get below data after filtering applied.
[{ campaignId: 111, campaignCategory: 'Diabetes', result: [{
campaignType: 3, name: 'test33'
}] },
{ campaignId: 333, campaignCategory: 'Cardiology', result: [{
campaignType: 3, name: 'Cardiology'
}] } ];
Here all the nodes are showing which have campaignType: 3.
Lodash or pure java-script based solution will work.
You can use reduce to find objects which have a .result item with a campaignType of 3, and if found, push them to the accumulator array:
const getOutput = () => {
const output = arr.reduce((a, item) => {
const foundResults = item.result.filter(({ campaignType }) => campaignType === 3);
if (foundResults.length) {
a.push({ ...item, result: foundResults });
}
return a;
}, []);
console.log(output);
};
const arr = [{
campaignId: 111,
campaignCategory: 'Diabetes',
result: [{
campaignType: 1,
name: 'tes1'
}, {
campaignType: 1,
name: 'test22'
}, {
campaignType: 3,
name: 'test33'
}]
},
{
campaignId: 222,
campaignCategory: 'Orthopedic',
result: [{
campaignType: 1,
name: 'Orthopedic'
}]
},
{
campaignId: 333,
campaignCategory: 'Cardiology',
result: [{
campaignType: 3,
name: 'Cardiology'
},
{
campaignType: 1,
name: 'Cardiology 123'
}
]
}
];
getOutput();
You could filter the inner result in advance and take the outer objects if ayn filters elements exists. Then take a new object with a new result.
var data = [{ campaignId: 111, campaignCategory: 'Diabetes', result: [{ campaignType: 1, name: 'tes1' }, { campaignType: 1, name: 'test22' }, { campaignType: 3, name: 'test33' }] }, { campaignId: 222, campaignCategory: 'Orthopedic', result: [{ campaignType: 1, name: 'Orthopedic' }] }, { campaignId: 333, campaignCategory: 'Cardiology', result: [{ campaignType: 3, name: 'Cardiology' }, { campaignType: 1, name: 'Cardiology 123' }] }],
result = data.reduce((r, o) => {
var result = o.result.filter(({ campaignType}) => campaignType === 1);
if (result.length) r.push(Object.assign({}, o, { result }));
return r;
}, []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You could use filter with some and map in vanilla javascript.
var data = [{ campaignId: 111, campaignCategory: 'Diabetes', result: [{
campaignType: 1, name: 'tes1'
},{
campaignType: 1, name: 'test22'
},{
campaignType: 3, name: 'test33'
}] },
{ campaignId: 222, campaignCategory: 'Orthopedic', result: [{
campaignType: 1, name: 'Orthopedic'
}] },
{ campaignId: 333, campaignCategory: 'Cardiology', result: [{
campaignType: 3, name: 'Cardiology'
},
{
campaignType: 1, name: 'Cardiology 123'
}]} ];
var res = data.filter(campaign => campaign.result.some(type => type.campaignType === 3));
res = res.map(campaign => {
campaign.result = campaign.result.filter(type => type.campaignType ===3);
return campaign;
});
console.log(res);
It seems to me you can get this result with a simple Array.reduce and Array.forEach like this:
let data = [{ "campaignId": 111, "campaignCategory": "Diabetes", "result": [{ "campaignType": 1, "name": "tes1" }, { "campaignType": 1, "name": "test22" }, { "campaignType": 3, "name": "test33" } ] }, { "campaignId": 222, "campaignCategory": "Orthopedic", "result": [{ "campaignType": 1, "name": "Orthopedic" }] }, { "campaignId": 333, "campaignCategory": "Cardiology", "result": [{ "campaignType": 3, "name": "Cardiology" }, { "campaignType": 1, "name": "Cardiology 123" } ] } ]
let result = data.reduce((r,{result,...rest}) => {
result.forEach(x => x.campaignType === 3 ? r.push({...rest, result:[x]}) : null)
return r
}, [])
console.log(result)
Im using the dropdown angularjs-dropdown-multiselect. But it has only a single level dropdown. This can be used to show the following response.
[
{ id: 1, label: 'Facility 01' },
{ id: 2, label: 'Facility 02' },
{ id: 3, label: 'Facility 03' },
{ id: 4, label: 'Facility 04' },
{ id: 5, label: 'Facility 05' },
{ id: 6, label: 'Facility 06' },
{ id: 7, label: 'Facility 07' },
{ id: 8, label: 'Facility 08' },
];
But what i need is a multilevel dropdown to show the followind response in the dropdown.
[
{
"id": 8,
"label": "AA",
"items": [
{
"id": 9,
"label": "BB",
"items": [
{
"id": 10,
"label": "DD",
"items": []
}
]
},
{
"id": 11,
"label": "CC",
"items": []
}
]
}
]
I want to display the above response using angularjs-dropdown-multiselect. Can anyone help me with this?
Thanks
You can find solution for you question here.
plnkr.co/edit/gHzV9d?p=preview
I would like to know the correct way to create a nested Json tree Structure object in javascript.
data structure containing objects and arrays. How can I extract the information, i.e. access a specific or multiple values (or id)?
I have a very deep nested tree structure Json and I am given an object that can exist at any depth. I need to be able to iterate through all grandparent / parent / children nodes until I find the requested category, plus be able to capture its grandparent / parent / children categories all the way through.
//input data structure
[{
"Type": "grdparent1",
"name": "grdparent1",
"children": [{
"Type": "grdparent1",
"Id": 45,
"children": []
}, {
"Type": "grdparent1",
"Id": 46,
"children": [{
"Type": "parent1",
"Id": 54,
"children": [{
"Type": "child1",
"Id": 63,
"children": []
}, {
"Type": "child2",
"Id": 64,
"children": []
}]
}, {
"Type": "parent2",
"Id": 57,
"children": []
}]
}]
}, {
"Type": "grdparent2",
"name": "grdparent2",
"children": [{
"Type": "grdparent2",
"Id": 4,
"children": [{
"Type": "parent1",
"Id": 16,
"children": [{
"children": [],
"Type": "child1",
"Id": 28,
}]
}, {
"Type": "parent2",
"Id": 17,
"children": []
}]
}]
}, {
"Type": "grdparent3",
"name": "grdparent3",
"children": []
}, {
"Type": "grdparent4",
"name": "grdparent4",
"children": [{
"Type": "parent1",
"Id": 167,
"children": []
}]
}]
//output
[{
"grdparent1": [{
"Id": 45,
}, {
"Id": 46,
"parent1": [{
"Id": 54,
"child1": {
"Id": 63
}
}, {
"child2": {
"Id": 64
}
}]
}, {
"parent2": [{
"Id": 57
}]
}]
}, {
"grdparent2": [{
"Id": 4,
"parent1": [{
"Id": 16,
"child1": [{
"Id": 28
}]
}, {
"parent2": [{
"Id": 17
}]
}]
}, {
"grdparent4": [{
"parent1": [{
"Id": 167
}]
}]
}]
}]
Here is the code. You have the result in output variable:
var input = [{
"Type": "grdparent1",
"name": "grdparent1",
"children": [{
"Type": "grdparent1",
"Id": 45,
"children": []
}, {
"Type": "grdparent1",
"Id": 46,
"children": [{
"Type": "parent1",
"Id": 54,
"children": [{
"Type": "child1",
"Id": 63,
"children": []
}, {
"Type": "child2",
"Id": 64,
"children": []
}]
}, {
"Type": "parent2",
"Id": 57,
"children": []
}]
}]
}, {
"Type": "grdparent2",
"name": "grdparent2",
"children": [{
"Type": "grdparent2",
"Id": 4,
"children": [{
"Type": "parent1",
"Id": 16,
"children": [{
"children": [],
"Type": "child1",
"Id": 28,
}]
}, {
"Type": "parent2",
"Id": 17,
"children": []
}]
}]
}, {
"Type": "grdparent3",
"name": "grdparent3",
"children": []
}, {
"Type": "grdparent4",
"name": "grdparent4",
"children": [{
"Type": "parent1",
"Id": 167,
"children": []
}]
}];
var output = [];
for(var index = 0; index < input.length; index++) {
if(input[index].children.length > 0) {
var parsedObject = parseTopLevelItem(input[index]);
if(parsedObject) {
output[output.length] = parsedObject;
}
}
}
alert(JSON.stringify(output));
function parseTopLevelItem(item) {
var topLevelReturnObject;
if(item.children.length > 0) {
topLevelReturnObject = {};
for(var i = 0; i < item.children.length; i++) {
var parsedObject = parseChild(item.children[i]);
if(parsedObject) {
var key = parsedObject[0];
if(!topLevelReturnObject[key]) {
topLevelReturnObject[key] = [];
}
topLevelReturnObject[key][(topLevelReturnObject[key]).length] = parsedObject[1];
}
}
}
return topLevelReturnObject;
}
function parseChild(childElement){
var returnObject = [];
returnObject[0] = childElement.Type;
returnObject[1] = {};
returnObject[1].Id = childElement.Id;
for(var i = 0; i < childElement.children.length; i++) {
var parsedObject = parseChild(childElement.children[i]);
if(parsedObject) {
var key = parsedObject[0];
if(!returnObject[1][key]) {
returnObject[1][key] = [];
}
returnObject[1][key][(returnObject[1][key]).length] = parsedObject[1];
}
}
return returnObject;
}
As this has been brought back up...
Here's an approach which uses some fairly standard utility functions to allow for a fairly simple implementation of your node reformatting and wraps that in a simple transformation of your whole forest of nodes. (It's not a tree, precisely, as there isn't a single root. This is commonly called a "forest".)
// utility functions
const groupBy = (fn) => (xs) =>
xs .reduce ((a, x) => ({... a, [fn(x)]: [... (a [fn (x)] || []), x]}), {})
const mapObject = (fn) => (obj) =>
Object .fromEntries (Object .entries (obj) .map (([k, v]) => [k, fn(v)]))
// helper functions
const hasKids = ({children = []}) => children .length > 0
const hasGrandkids = ({children = []}) => children .some (hasKids)
// main functions
const reformat = ({Id, children = []}) => ({
...(Id ? {Id} : {}),
... mapObject (kids => kids .map (reformat)) (groupBy (o => o.Type) (children))
})
const transform = (nodes) =>
nodes .filter (hasGrandkids) .map (reformat)
// sample data
const input = [{Type: "grdparent1", name: "grdparent1", children: [{Type: "grdparent1", Id: 45, children: []}, {Type: "grdparent1", Id: 46, children: [{Type: "parent1", Id: 54, children: [{Type: "child1", Id: 63, children: []}, {Type: "child2", Id: 64, children: []}]}, {Type: "parent2", Id: 57, children: []}]}]}, {Type: "grdparent2", name: "grdparent2", children: [{Type: "grdparent2", Id: 4, children: [{Type: "parent1", Id: 16, children: [{children: [], Type: "child1", Id: 28}]}, {Type: "parent2", Id: 17, children: []}]}]}, {Type: "grdparent3", name: "grdparent3", children: []}, {Type: "grdparent4", name: "grdparent4", children: [{Type: "parent1", Id: 167, children: []}]}]
// demo
console .log (transform (input))
.as-console-wrapper {max-height: 100% !important; top: 0}
The two important utility functions used are
groupBy which groups an array into an object where the keys are generated by the supplied function mapped against the elements, and the values are arrays of the elements that generated that key. That is, for example,
groupBy (({name}) => name[0]) ([
{name: 'alice', age: 26},
{name: 'bob', age: 19},
{name: 'andrew', age: 31},
{name: 'carol', age: 22}
])
//=>
// {
// a: [{name: 'alice', age: 26}, {name: 'andrew', age: 31}],
// b: [{name: 'bob', age: 19}],
// c: [{name: 'carol', age: 22}]
// }
and mapObject, which maps a function over the values of an object, for example,
mapObject (n => n * n) ({a: 2, b: 3, c: 5, d: 7})
//=> {a: 1, b: 4, c: 25, d: 49}
We also have two helper functions which simply determine whether the node has children and whether it has grandchildren. We will use this in transform, to choose only those nodes with grandchildren. And that, in fact, is all transform does: it filters the list of nodes to include only those with grandchildren, and then it calls our reformat function on each of them. (It's not at all clear to me that this is what was desired in the first place. The question title and text refer to searching for nodes, but there is no evidence in any code of actual searching taking place. I'm guessing here in a way that matches the sample output and at least one other answer. This part would be easy enough to refactor.)
reformat is the main function here, formatting a node and recurring on each of its children. This is a fairly tricky reformat, turning child Type names into object keys and including Id in the output only when it's present in the input.
But the code isn't that complex, thanks to the use of the two helper functions. We group the children by their Type property, and then on the resulting object, we use mapObject to apply kids => kids .map (reformat) to each node.
groupBy and mapObject are general-purpose utility functions, and there are equivalents in major libraries like Underscore, Lodash, and Ramda (disclaimer: I'm a Ramda principal team member). But these implementation show that it's fairly easy to maintain your own versions.
This really was an interesting question! Took me a while to figure it out :)
I'm not a big fan of reinventing the wheel. So I'd suggest you use a library. We like object-scan for data processing since it is very powerful once you wrap your head around it. Having said that, this question was tricky! Here is how you could solve it
// const objectScan = require('object-scan');
const data = [{ Type: 'grdparent1', name: 'grdparent1', children: [{ Type: 'grdparent1', Id: 45, children: [] }, { Type: 'grdparent1', Id: 46, children: [{ Type: 'parent1', Id: 54, children: [{ Type: 'child1', Id: 63, children: [] }, { Type: 'child2', Id: 64, children: [] }] }, { Type: 'parent2', Id: 57, children: [] }] }] }, { Type: 'grdparent2', name: 'grdparent2', children: [{ Type: 'grdparent2', Id: 4, children: [{ Type: 'parent1', Id: 16, children: [{ children: [], Type: 'child1', Id: 28 }] }, { Type: 'parent2', Id: 17, children: [] }] }] }, { Type: 'grdparent3', name: 'grdparent3', children: [] }, { Type: 'grdparent4', name: 'grdparent4', children: [{ Type: 'parent1', Id: 167, children: [] }] }];
const convert = (input) => {
objectScan(['**[*]'], {
breakFn: ({ isMatch, key, value, context }) => {
if (isMatch) {
context[key.length] = value.children.map(({ Type }) => Type);
}
},
filterFn: ({ key, value, parent, property, context }) => {
const result = 'Id' in value ? { Id: value.Id } : {};
context[key.length].forEach((type, idx) => {
result[type] = (result[type] || []).concat(value.children[idx]);
});
if (Object.keys(result).length === 0) {
parent.splice(property, 1);
} else {
parent.splice(property, 1, result);
}
}
})(input, []);
};
convert(data);
console.log(data);
// => [ { grdparent1: [ { Id: 45 }, { Id: 46, parent1: [ { Id: 54, child1: [ { Id: 63 } ], child2: [ { Id: 64 } ] } ], parent2: [ { Id: 57 } ] } ] }, { grdparent2: [ { Id: 4, parent1: [ { Id: 16, child1: [ { Id: 28 } ] } ], parent2: [ { Id: 17 } ] } ] }, { parent1: [ { Id: 167 } ] } ]
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan#13.7.1"></script>
Disclaimer: I'm the author of object-scan
Note that the result matches the result of the currently accepted answer.