Objects data to be updated with value from Array - javascript

I'm trying to update the 'positionTitle' field of my graphData object with the 'positionTitle fields of my individualData array.
I need to do it accurately, both sets of data have 'accounts' which both have the same id and fullname for user, I was hoping to try and use this to do the matching.
I want the positionTitle's from the users with same account id or name (whichever is easier) to go into the objects fields.
This is currently what I have:
My Object (that i want to update):
graphData = {
"name": "Annual meetings",
"engagementAreas": [{
"id": "1",
"engagementTypes": [{
"name": "forestry",
"engagements": []
},
{
"name": "houses",
"engagements": [{
"name": "engagement1",
"members": [{
"id": "e334", "account": {
"id": "eefe", "fullName": "jim bean"
},
"position": {
"id": "3434",
"positionTitle": "Manager"
}
}]
}]
},
{
"name": "landscaping",
"engagements": [{
"name": "engagement1343",
"members": [{
"id": "e334", "account": {
"id": "123", "fullName": "john boer"
},
"position": {
"id": "4545",
"positionTitle": "Senior Manager"
}
}]
}]
}
]
},
{
"name": "community days",
"engagementTypes": [{
"name": "skyscraping",
"engagements": []
},
{
"name": "tennis",
"engagements": [{
"name": "engagement346",
"members": [{
"id": "34", "account": {
"id": "0010X000048DDMsQAO", "fullName": "edy long"
},
"position": {
"id": "3999434",
"positionTitle": "Ultime Manager"
}
}]
}]
},
{
"name": "Juicing",
"engagements": [{
"name": "347343",
"members": [{
"id": "4546", "account": {
"id": "001b000003WnPy1AAF", "fullName": "jeff bint"
},
"position": {
"id": "35006",
"positionTitle": "Senior Ultimate Manager"
}
}]
}]
}]
}]
}
My array whose positionTitles I want to take:
IndividualData = [{
"account": {
"id": "001b000003WnPy1AAF",
"fullName": "jeff bint"
},
"positions": [{
"id": "a16b0000004AxeBAAS",
"organizationId": "001b0000005gxmlAAA",
"organizationName": "a",
"positionTitle": "Senior Manager, Energy",
"positionLevel": "5-Middle Management & Advisers",
"isPrimary": true,
"startDate": "2016-10-07",
"endDate": null
}]
}, {
"account": {
"id": "0010X000048DDMsQAO",
"fullName": "edy long"
},
"positions": [{
"id": "a160X000004nKfhQAE",
"organizationId": "001b0000005gxmlAAA",
"organizationName": "a",
"positionTitle": "Managing Director",
"positionLevel": "4-Head of Business Unit/Head of Region",
"isPrimary": true,
"startDate": "2018-03-05",
"endDate": null
}]
}, {
"account": {
"id": "123",
"fullName": "john boer"
},
"positions": [{
"id": "325345634634",
"organizationId": "001b0000005gxmlAAA",
"organizationName": "a",
"positionTitle": "Managing Director",
"positionLevel": "4-Head of Business Unit/Head of Region",
"isPrimary": true,
"startDate": "2018-03-05",
"endDate": null
}]
}
]
The function I'm currently using, which does take the first positiontitle field of the array:
const updatedGraphTable = { ...graphData,
engagementAreas: graphData.engagementAreas.map(area => ({ ...area,
engagementTypes: area.engagementTypes.map(type => ({ ...type,
engagements: type.engagements.map(engagement => ({ ...engagement,
members: engagement.members.map(member => ({ ...member,
position: { ...member.position,
positionTitle: IndividualData[0].positions[0].positionTitle
}
}))
}))}))
}))
};
console.log(updatedGraphTable)
console.log('a' + JSON.stringify(updatedGraphTable))
my expected result with the positions updated:
updatedGraphData = {
"name": "Annual meetings",
"engagementAreas": [{
"id": "1",
"engagementTypes": [{
"name": "forestry",
"engagements": []
},
{
"name": "houses",
"engagements": [{
"name": "engagement1",
"members": [{
"id": "e334", "account": {
"id": "eefe", "fullName": "jim bean"
},
"position": {
"id": "3434",
"positionTitle": "Manager"
}
}]
}]
},
{
"name": "landscaping",
"engagements": [{
"name": "engagement1343",
"members": [{
"id": "e334", "account": {
"id": "123", "fullName": "john boer"
},
"position": {
"id": "4545",
"positionTitle": "Managing Director"
}
}]
}]
}
]
},
{
"name": "community days",
"engagementTypes": [{
"name": "skyscraping",
"engagements": []
},
{
"name": "tennis",
"engagements": [{
"name": "engagement346",
"members": [{
"id": "34", "account": {
"id": "0010X000048DDMsQAO", "fullName": "edy long"
},
"position": {
"id": "3999434",
"positionTitle": "Managing Director"
}
}]
}]
},
{
"name": "Juicing",
"engagements": [{
"name": "347343",
"members": [{
"id": "4546", "account": {
"id": "001b000003WnPy1AAF", "fullName": "jeff bint"
},
"position": {
"id": "35006",
"positionTitle": "Senior Manager, Energy"
}
}]
}]
}]
}]
}
My current result:
{
"name": "Annual meetings",
"engagementAreas": [{
"id": "1",
"engagementTypes": [{
"name": "forestry",
"engagements": []
}, {
"name": "houses",
"engagements": [{
"name": "engagement1",
"members": [{
"id": "e334",
"account": {
"id": "eefe"
},
"position": {
"id": "3434",
"positionTitle": "Senior Manager, Energy"
}
}]
}]
}, {
"name": "landscaping",
"engagements": [{
"name": "engagement1343",
"members": [{
"position": {
"id": "4545",
"positionTitle": "Senior Manager, Energy"
}
}]
}]
}]
}, {
"name": "community days",
"engagementTypes": [{
"name": "skyscraping",
"engagements": []
}, {
"name": "tennis",
"engagements": [{
"name": "engagement346",
"members": [{
"id": "34",
"account": {
"id": "3546"
},
"position": {
"id": "3999434",
"positionTitle": "Senior Manager, Energy"
}
}]
}]
}, {
"name": "Juicing",
"engagements": [{
"name": "347343",
"members": [{
"id": "4546",
"account": {
"id": "3545"
},
"position": {
"id": "35006",
"positionTitle": "Senior Manager, Energy"
}
}]
}]
}]
}]
}

Sure, the trick would be to first map your data into an object (so you don't have to search over both arrays all the time), and then conditionally set the value (as I saw that your first manager, doesn't really have a matching position).
So to create the dictionary for usage in your later model, you can first do
// first map the accountId to positions
const accountIdToPositionDict = individualData.reduce( (current, item) => {
current[item.account.id] = (item.positions.filter( position => position.isPrimary )[0] || {} ).positionTitle;
return current;
}, {} );
This would then have an object where accountIdToPositionDict["123"] would be Managing Director, and then change the duplicating logic into:
const updatedGraphTable = { ...graphData,
engagementAreas: graphData.engagementAreas.map(area => ({ ...area,
engagementTypes: area.engagementTypes.map(type => ({ ...type,
engagements: type.engagements.map(engagement => ({ ...engagement,
members: engagement.members.map(member => ({ ...member,
position: { ...member.position,
// use the found positionTitle, or the original one that was given
positionTitle: member.account && accountIdToPositionDict[member.account.id] || member.position.positionTitle
}
}))
}))}))
}))
};
Where the position would then be set based on the found accountId in the dictionary, or the original title if no match was found
const individualData = [{
"account": {
"id": "001b000003WnPy1AAF",
"fullName": "jeff bint"
},
"positions": [{
"id": "a16b0000004AxeBAAS",
"organizationId": "001b0000005gxmlAAA",
"organizationName": "a",
"positionTitle": "Senior Manager, Energy",
"positionLevel": "5-Middle Management & Advisers",
"isPrimary": true,
"startDate": "2016-10-07",
"endDate": null
}]
}, {
"account": {
"id": "0010X000048DDMsQAO",
"fullName": "edy long"
},
"positions": [{
"id": "a160X000004nKfhQAE",
"organizationId": "001b0000005gxmlAAA",
"organizationName": "a",
"positionTitle": "Managing Director",
"positionLevel": "4-Head of Business Unit/Head of Region",
"isPrimary": true,
"startDate": "2018-03-05",
"endDate": null
}]
}, {
"account": {
"id": "123",
"fullName": "john boer"
},
"positions": [{
"id": "325345634634",
"organizationId": "001b0000005gxmlAAA",
"organizationName": "a",
"positionTitle": "Managing Director",
"positionLevel": "4-Head of Business Unit/Head of Region",
"isPrimary": true,
"startDate": "2018-03-05",
"endDate": null
}]
}
];
const graphData = {
"name": "Annual meetings",
"engagementAreas": [{
"id": "1",
"engagementTypes": [{
"name": "forestry",
"engagements": []
},
{
"name": "houses",
"engagements": [{
"name": "engagement1",
"members": [{
"id": "e334", "account": {
"id": "eefe", "fullName": "jim bean"
},
"position": {
"id": "3434",
"positionTitle": "Manager"
}
}]
}]
},
{
"name": "landscaping",
"engagements": [{
"name": "engagement1343",
"members": [{
"id": "e334", "account": {
"id": "123", "fullName": "john boer"
},
"position": {
"id": "4545",
"positionTitle": "Senior Manager"
}
}]
}]
}
]
},
{
"name": "community days",
"engagementTypes": [{
"name": "skyscraping",
"engagements": []
},
{
"name": "tennis",
"engagements": [{
"name": "engagement346",
"members": [{
"id": "34", "account": {
"id": "0010X000048DDMsQAO", "fullName": "edy long"
},
"position": {
"id": "3999434",
"positionTitle": "Ultime Manager"
}
}]
}]
},
{
"name": "Juicing",
"engagements": [{
"name": "347343",
"members": [{
"id": "4546", "account": {
"id": "001b000003WnPy1AAF", "fullName": "jeff bint"
},
"position": {
"id": "35006",
"positionTitle": "Senior Ultimate Manager"
}
}]
}]
}]
}]
};
// first map the accountId to positions
const accountIdToPositionDict = individualData.reduce( (current, item) => {
current[item.account.id] = (item.positions.filter( position => position.isPrimary )[0] || {} ).positionTitle;
return current;
}, {} );
// then use it in the mapping function
const updatedGraphTable = { ...graphData,
engagementAreas: graphData.engagementAreas.map(area => ({ ...area,
engagementTypes: area.engagementTypes.map(type => ({ ...type,
engagements: type.engagements.map(engagement => ({ ...engagement,
members: engagement.members.map(member => ({ ...member,
position: { ...member.position,
// use the found positionTitle, or the original one that was given
positionTitle: member.account && accountIdToPositionDict[member.account.id] || member.position.positionTitle
}
}))
}))}))
}))
};
console.log( updatedGraphTable );

Try the code below.
accountPositions = {};
IndividualData.forEach((data) => {
accountPositions[data.account.id] = data.positions.filter((pos) => {return pos.isPrimary})[0].positionTitle;
});
graphData.engagementAreas.forEach((area) => {
area.engagementTypes.forEach((type) => {
type.engagements.forEach((engagement) => {
engagement.members.forEach((member) => {
if (!accountPositions[member.account.id]) return;
console.log('position updated from ', member.position.positionTitle, 'to', accountPositions[member.account.id]);
member.position.positionTitle = accountPositions[member.account.id];
});
});
});
});

Related

Extracting all nested IDs

I have below JSON for example where I need to extract all categories Id from the last subcategory if its not empty. For instance: On below JSON I need Ids like mens-apparel-ricky inside last categories object but if this categories was empty I like to extract the last ID like womens-new-arrivals. Basically it should be the list of all these ID's extracted.
Thanks in Advance
"categories": [{
"id": "featured",
"name": "NEW ARRIVALS",
"categories": [{
"id": "featured-new-arrivals",
"name": "New Arrivals",
"categories": [{
"id": "mens-new-arrivals",
"name": "Mens",
"categories": []
}, {
"id": "womens-new-arrivals",
"name": "Womens",
"categories": [{
"id": "mens-apparel-ricky",
"name": "Relaxed",
"categories": []
}, {
"id": "new-arrivals-kids",
"name": "Kids",
"categories": []
}, {
"id": "new-arrivals-accessories",
"name": "Accessories",
"categories": []
}]
}, {
"id": "collections",
"name": "Featured",
"categories": [{
"id": "spring-shop",
"name": "Spring Shop",
"categories": [{
"id": "spring-shop-mens",
"name": "Spring Shop Men's",
"categories": []
}, {
"id": "spring-shop-womens",
"name": "Spring Shop Women's",
"categories": []
}]
}, {
"id": "the-festival-shop",
"name": "The Festival Shop",
"categories": [{
"id": "mens-festival-shop",
"name": "Mens Festival Shop",
"categories": []
}, {
"id": "womens-festival-shop",
"name": "Womens Festival Shop",
"categories": []
}]
}, {
"id": "buddha-collections",
"name": "The Icons Shop",
"categories": [{
"id": "buddha-collections-men",
"name": "Mens",
"categories": []
}, {
"id": "buddha-collections-women",
"name": "Womens",
"categories": []
}]
}, {
"id": "all-black",
"name": "All Black Everything",
"categories": []
}]
}]
}, {
"id": "denim",
"name": "DENIM",
"categories": [{
"id": "denim-view-all",
"name": "Shop All Denim",
"categories": [{
"id": "denim-view-all-mens",
"name": "Mens Denim",
"categories": []
}, {
"id": "denim-view-all-womens",
"name": "Womens Denim",
"categories": []
}, {
"id": "denim-view-all-kids",
"name": "Kids Denim",
"categories": []
}]
}, {
"id": "denim-collections",
"name": "Featured",
"categories": [{
"id": "denim-collections-signature-stitch",
"name": "Big T & Super T Stitch",
"categories": []
}, {
"id": "denim-collections-lighten-up",
"name": "Light Wash Denim",
"categories": []
}, {
"id": "dark-wash-denim",
"name": "Dark Wash Denim",
"categories": []
}]
}]
}]
}
This is what I have tried but being new to coding getting difficult to get list form this complex structure.
var rootCategories = (jsonData.categories);
for (var i=0; i < rootCategories.length; i++)
{
var firstChild = (jsonData.categories[i].categories);
for (var j=0; j < firstChild.length; j++)
{
if ((firstChild[j].categories[j].length != 0)) {
catId = firstChild[j].categories[j].id
console.log(catId);
}
}
}
Below is one possible way to achieve the target.
Code Snippet
// recursive method to get "id"s
const getIds = arr => (
arr.flatMap( // iterate using ".flatMap()" to avoid nesting
({id, categories}) => { // de-structure to directly access "id" & "categories"
if ( // if "categories" is not empty, recurse to next level
Array.isArray(categories) &&
categories.length > 0
) {
return getIds(categories);
} else { // if it is empty, simply return the id
return id;
}
}
)
);
const rawData = {
"categories": [{
"id": "featured",
"name": "NEW ARRIVALS",
"categories": [{
"id": "featured-new-arrivals",
"name": "New Arrivals",
"categories": [{
"id": "mens-new-arrivals",
"name": "Mens",
"categories": []
}, {
"id": "womens-new-arrivals",
"name": "Womens",
"categories": [{
"id": "mens-apparel-ricky",
"name": "Relaxed",
"categories": []
}, {
"id": "new-arrivals-kids",
"name": "Kids",
"categories": []
}, {
"id": "new-arrivals-accessories",
"name": "Accessories",
"categories": []
}]
}, {
"id": "collections",
"name": "Featured",
"categories": [{
"id": "spring-shop",
"name": "Spring Shop",
"categories": [{
"id": "spring-shop-mens",
"name": "Spring Shop Men's",
"categories": []
}, {
"id": "spring-shop-womens",
"name": "Spring Shop Women's",
"categories": []
}]
}, {
"id": "the-festival-shop",
"name": "The Festival Shop",
"categories": [{
"id": "mens-festival-shop",
"name": "Mens Festival Shop",
"categories": []
}, {
"id": "womens-festival-shop",
"name": "Womens Festival Shop",
"categories": []
}]
}, {
"id": "buddha-collections",
"name": "The Icons Shop",
"categories": [{
"id": "buddha-collections-men",
"name": "Mens",
"categories": []
}, {
"id": "buddha-collections-women",
"name": "Womens",
"categories": []
}]
}, {
"id": "all-black",
"name": "All Black Everything",
"categories": []
}]
}]
}, {
"id": "denim",
"name": "DENIM",
"categories": [{
"id": "denim-view-all",
"name": "Shop All Denim",
"categories": [{
"id": "denim-view-all-mens",
"name": "Mens Denim",
"categories": []
}, {
"id": "denim-view-all-womens",
"name": "Womens Denim",
"categories": []
}, {
"id": "denim-view-all-kids",
"name": "Kids Denim",
"categories": []
}]
}, {
"id": "denim-collections",
"name": "Featured",
"categories": [{
"id": "denim-collections-signature-stitch",
"name": "Big T & Super T Stitch",
"categories": []
}, {
"id": "denim-collections-lighten-up",
"name": "Light Wash Denim",
"categories": []
}, {
"id": "dark-wash-denim",
"name": "Dark Wash Denim",
"categories": []
}]
}]
}]
}]
};
console.log(getIds(rawData.categories));
.as-console-wrapper { max-height: 100% !important; top: 0 }
Explanation
Inline comments added in the snippet above.
If you're not afraid of reference loops (shouldn't technically happen in a serialized REST response) you can try recurring function as the simplest way to achieve this
function getIDs(node, isRoot = false) {
if (!node.categories.length) // if I understood your question correctly, you don't want parents to be printed out, otherwise just check for isRoot instead
return isRoot || console.log(node.id);
// with above you won't access non-existent id of the root collection
// because it will stop checking after first true statement
for (const category of categories)
getIDs(category);
}
getIDs(collection, true);

Itarate over deeply nested array of objects and generate new array

I have a deeply nested array like below. I want to flat the structure.
data = [
{
"id": 4321,
"name": "category1",
"parentId": null,
"children": [
{
"id": 1234,
"name": "category1",
"parentId": 4321,
"children": [
{
"id": 8327548,
"name": "001",
"parentId": 1234
},
{
"id": 8327549,
"name": "002",
"parentId": 1234
},
]
},
{
"id": 6786,
"name": "Associations",
"parentId": 4321
},
{
"id": 8262439,
"name": "category1",
"parentId": 4321
},
{
"id": 8245,
"name": "Rights",
"parentId": 4321,
"children": [
{
"id": 2447,
"name": "Organizations",
"parentId": 8245
},
{
"id": 9525,
"name": "Services",
"parentId": 8245
},
{
"id": 8448,
"name": "Organizations",
"parentId": 8245
}
]
},
{
"id": 8262446,
"name": "Women's Rights",
"parentId": 4321
}
]
},
{
"id": 21610,
"name": "Agriculture",
"parentId": null,
"children": [
{
"id": 3302,
"name": "categoryABC",
"parentId": 21610,
"children": [
{
"id": 85379,
"name": "categoryABC - General",
"parentId": 3302
},
{
"id": 85380,
"name": "categoryABC Technology",
"parentId": 3302
}
]
},
{
"id": 8303,
"name": "Fungicides",
"parentId": 21610,
"children": [
{
"id": 8503,
"name": "Fungicides - General",
"parentId": 8303
}
]
},
]
},
];
Expected output
output = [
{
"id": 8327548,
"name": "001",
"parentId": 1234
},
{
"id": 8327549,
"name": "002",
"parentId": 1234
},
...OTHER OBJECTS....
]
What I have tried so far. This is not pushing the inner children items.
function flat(array) {
var result = [];
array.forEach(function (a) {
result.push(a);
if (Array.isArray(a.children)) {
result = result.concat(flat(a.children));
}
});
return result;
}
let results = flat(data)
console.log("test", results)
Stack Snippet:
const data = [
{
"id": 4321,
"name": "category1",
"parentId": null,
"children": [
{
"id": 1234,
"name": "category1",
"parentId": 4321,
"children": [
{
"id": 8327548,
"name": "001",
"parentId": 1234
},
{
"id": 8327549,
"name": "002",
"parentId": 1234
},
]
},
{
"id": 6786,
"name": "Associations",
"parentId": 4321
},
{
"id": 8262439,
"name": "category1",
"parentId": 4321
},
{
"id": 8245,
"name": "Rights",
"parentId": 4321,
"children": [
{
"id": 2447,
"name": "Organizations",
"parentId": 8245
},
{
"id": 9525,
"name": "Services",
"parentId": 8245
},
{
"id": 8448,
"name": "Organizations",
"parentId": 8245
}
]
},
{
"id": 8262446,
"name": "Women's Rights",
"parentId": 4321
}
]
},
{
"id": 21610,
"name": "Agriculture",
"parentId": null,
"children": [
{
"id": 3302,
"name": "categoryABC",
"parentId": 21610,
"children": [
{
"id": 85379,
"name": "categoryABC - General",
"parentId": 3302
},
{
"id": 85380,
"name": "categoryABC Technology",
"parentId": 3302
}
]
},
{
"id": 8303,
"name": "Fungicides",
"parentId": 21610,
"children": [
{
"id": 8503,
"name": "Fungicides - General",
"parentId": 8303
}
]
},
]
},
];
function flat(array) {
var result = [];
array.forEach(function (a) {
result.push(a);
if (Array.isArray(a.children)) {
result = result.concat(flat(a.children));
}
});
return result;
}
let results = flat(data)
console.log("test", results)
Can someone help me please?
I'd suggest a recursive approach, walking through the input structure and pushing each object found to the result array.
data = [ { "id": 4321, "name": "category1", "parentId": null, "children": [ { "id": 1234, "name": "category1", "parentId": 4321, "children": [ { "id": 8327548, "name": "001", "parentId": 1234 }, { "id": 8327549, "name": "002", "parentId": 1234 }, ] }, { "id": 6786, "name": "Associations", "parentId": 4321 }, { "id": 8262439, "name": "category1", "parentId": 4321 }, { "id": 8245, "name": "Rights", "parentId": 4321, "children": [ { "id": 2447, "name": "Organizations", "parentId": 8245 }, { "id": 9525, "name": "Services", "parentId": 8245 }, { "id": 8448, "name": "Organizations", "parentId": 8245 } ] }, { "id": 8262446, "name": "Women's Rights", "parentId": 4321 } ] }, { "id": 21610, "name": "Agriculture", "parentId": null, "children": [ { "id": 3302, "name": "categoryABC", "parentId": 21610, "children": [ { "id": 85379, "name": "categoryABC - General", "parentId": 3302 }, { "id": 85380, "name": "categoryABC Technology", "parentId": 3302 } ] }, { "id": 8303, "name": "Fungicides", "parentId": 21610, "children": [ { "id": 8503, "name": "Fungicides - General", "parentId": 8303 } ] }, ] }, ];
function flat(input, result = []) {
let newObj = null;
for(let k in input) {
if (typeof(input[k]) === 'object') {
flat(input[k], result);
} else {
if (!newObj) {
newObj = {};
result.push(newObj);
}
newObj[k] = input[k];
}
}
return result;
}
console.log(flat(data))
.as-console-wrapper { max-height: 100% !important; top: 0; }
You need to delete a.children from the original array:
if (Array.isArray(a.children)) {
result = result.concat(flat(a.children));
delete a.children;
}
(As #T.J.Crowde suggested, I left the snippet with a smaller array)
const data = [
{
"id": 4321,
"name": "category1",
"parentId": null,
"children": [
{
"id": 1234,
"name": "category1",
"parentId": 4321,
"children": [
{
"id": 8327548,
"name": "001",
"parentId": 1234
},
{
"id": 8327549,
"name": "002",
"parentId": 1234
},
]
},
]
},
];
function flat(array) {
var result = [];
array.forEach(function (a) {
result.push(a);
if (Array.isArray(a.children)) {
result = result.concat(flat(a.children));
delete a.children;
}
});
return result;
}
let results = flat(data)
console.log("test", results)

how to find the ancestors of a particular object

let obj = {
"options": [
{
"id": "a",
"name": "a",
"options": [
{
"id": "a.1",
"options": [
{
"id": "a.1.1",
"name": "a.1.1"
},
{
"id": "a.1.2",
"name": "a.1.2"
},
{
"id": "a.1.3",
"name": "a.1.3"
}
]
},
{
"id": "a.2",
"name": "a.2",
"options": [
{
"id": "a.2.1",
"name": "a.2.1"
},
{
"id": "a.2.2",
"name": "a.2.2"
},
{
"id": "a.2.3",
"name": "a.2.3"
}
]
},
{
"id": "a.3",
"name": "a.3",
"options": [
{
"id": "a.3.1",
"name": "a.3.1"
},
{
"id": "a.3.2",
"name": "a.3.1"
},
{
"id": "a.3.3",
"name": "a.3.1"
}
]
}
]
},
{
"name": "b",
"id": "b",
"options": [
{
"id": "b.1",
"name": "b.1",
"options": [
{
"id": "b.1.1",
"name": "b.1.1"
},
{
"id": "b.1.2",
"name": "b.1.2"
},
{
"id": "b.1.3",
"name": "b.1.3"
}
]
},
{
"id": "b.2",
"name": "b.2",
"options": [
{
"id": "b.2.1",
"name": "b.2.1"
},
{
"id": "b.2.2",
"name": "b.2.2"
},
{
"id": "b.2.3",
"name": "b.2.3"
}
]
},
{
"id": "b.3",
"name": "b.3.1",
"options": [
{
"id": "b.3.1",
"name": "b.3.1"
},
{
"id": "b.3.2",
"name": "b.3.2"
},
{
"id": "b.3.3",
"name": "b.3.3"
}
]
}
]
},
{
"id": "c",
"name": "c",
"options": [
{
"id": "c.1",
"name": "c.1",
"options": [
{
"id": "c.1.1",
"name": "c.1.1"
},
{
"id": "c.1.2",
"name": "c.1.2"
},
{
"id": "c.1.3",
"name": "c.1.3"
}
]
},
{
"id": "c.2",
"name": "c.2",
"options": [
{
"id": "c.2.1",
"name": "c.2.1"
},
{
"id": "c.2.2",
"name": "c.2.2"
},
{
"id": "c.2.3",
"name": "c.2.3"
}
]
},
{
"id": "c.3",
"name": "c.3",
"options": [
{
"id": "c.3.1",
"name": "c.3.1"
},
{
"id": "c.3.2",
"name": "c.3.2"
},
{
"id": "c.3.3",
"name": "c.3.3"
}
]
}
]
}
]
}
" I have this object
I neeed to create a function getHierarchy, that an option ID as its input, finds the option in the
list and returns the ID of all it's parents.
for example,getHierarchy("a.1.3") should return the following result ["a","a.1","a.1.3"]
getHierarchy("c.3.3") should return the following result ["c","c.3","c.3.3"] "
assuming id is unique;
function getHierarchy(object, id, prev) {
if (!object.options) return;
for (const child of object.options) {
if(child.id === id) {
return [...prev, object.id, child.id];
}
result = getHierarchy(child, id, [...prev, object.id]);
if (result) return result;
}
}
getHierarchy(obj, "a.1.3", []);
Here an recursive solution i hope this helps you.
let obj = {
"options": [
{
"id": "a",
"name": "a",
"options": [
{
"id": "a.1",
"options": [
{
"id": "a.1.1",
"name": "a.1.1"
},
{
"id": "a.1.2",
"name": "a.1.2"
},
{
"id": "a.1.3",
"name": "a.1.3"
}
]
},
{
"id": "a.2",
"name": "a.2",
"options": [
{
"id": "a.2.1",
"name": "a.2.1"
},
{
"id": "a.2.2",
"name": "a.2.2"
},
{
"id": "a.2.3",
"name": "a.2.3"
}
]
},
{
"id": "a.3",
"name": "a.3",
"options": [
{
"id": "a.3.1",
"name": "a.3.1"
},
{
"id": "a.3.2",
"name": "a.3.1"
},
{
"id": "a.3.3",
"name": "a.3.1"
}
]
}
]
},
{
"name": "b",
"id": "b",
"options": [
{
"id": "b.1",
"name": "b.1",
"options": [
{
"id": "b.1.1",
"name": "b.1.1"
},
{
"id": "b.1.2",
"name": "b.1.2"
},
{
"id": "b.1.3",
"name": "b.1.3"
}
]
},
{
"id": "b.2",
"name": "b.2",
"options": [
{
"id": "b.2.1",
"name": "b.2.1"
},
{
"id": "b.2.2",
"name": "b.2.2"
},
{
"id": "b.2.3",
"name": "b.2.3"
}
]
},
{
"id": "b.3",
"name": "b.3.1",
"options": [
{
"id": "b.3.1",
"name": "b.3.1"
},
{
"id": "b.3.2",
"name": "b.3.2"
},
{
"id": "b.3.3",
"name": "b.3.3"
}
]
}
]
},
{
"id": "c",
"name": "c",
"options": [
{
"id": "c.1",
"name": "c.1",
"options": [
{
"id": "c.1.1",
"name": "c.1.1"
},
{
"id": "c.1.2",
"name": "c.1.2"
},
{
"id": "c.1.3",
"name": "c.1.3"
}
]
},
{
"id": "c.2",
"name": "c.2",
"options": [
{
"id": "c.2.1",
"name": "c.2.1"
},
{
"id": "c.2.2",
"name": "c.2.2"
},
{
"id": "c.2.3",
"name": "c.2.3"
}
]
},
{
"id": "c.3",
"name": "c.3",
"options": [
{
"id": "c.3.1",
"name": "c.3.1"
},
{
"id": "c.3.2",
"name": "c.3.2"
},
{
"id": "c.3.3",
"name": "c.3.3"
}
]
}
]
}
]
}
function getHierarchy(opts, path, index = 1, result = []) {
if(!opts) return result;
let objPaths = path.split(".");
let _path = objPaths.slice(0, index).join(".");
let option = opts.find(({id}) => id === _path);
if(!option) return result;
let { options, id } = option;
return getHierarchy(options, path, ++index, [...result,id]);
}
let result = getHierarchy(obj.options, "a.1.3");
console.log(result);

Javascript Recursive function to build Tree

Hi I am using JavaScript and jQuery as client side script. I am little bit new to Recursive functions. I have a JSON data as below and I have tried to make a tree structure using below JSON data by writing a recursive function but I am not able to build the tree structure.
var jsonData = { "$id": "45", "_children": [{ "$id": "46", "_children": [{ "$id": "47", "_children": [{ "$id": "48", "_children": [{ "$id": "49", "_children": null, "id": "Test1", "text": "Text1", "name": "name1", "parent": null, "root": { "$ref": "49" }, "depth": 0, "children": [] }], "id": "id1", "text": "text2", "name": "name2", "parent": null, "root": { "$ref": "48" }, "depth": 0, "children": [{ "$ref": "49" }] }], "id": "id3", "text": "text4", "name": "name4", "parent": null, "root": { "$ref": "47" }, "depth": 0, "children": [{ "$ref": "48" }] }, { "$id": "50", "_children": [{ "$id": "51", "_children": [{ "$id": "52", "_children": null, "id": "id6", "text": "text6", "name": "name6", "parent": null, "root": { "$ref": "52" }, "depth": 0, "children": [] }], "id": "id7", "text": "text7", "name": "name7", "parent": null, "root": { "$ref": "51" }, "depth": 0, "children": [{ "$ref": "52" }] }], "id": "id8", "text": "text8", "name": "name8", "parent": null, "root": { "$ref": "50" }, "depth": 0, "children": [{ "$ref": "51" }] }], "id": "id9", "text": "text9", "name": "name9", "parent": null, "root": { "$ref": "46" }, "depth": 0, "children": [{ "$ref": "47" }, { "$ref": "50" }] }, { "$id": "53", "_children": [{ "$id": "54", "_children": null, "id": "id10", "text": "text10", "name": "name10", "parent": null, "root": { "$ref": "54" }, "depth": 0, "children": [] }], "id": "id11", "text": "text11", "name": "name11", "parent": null, "root": { "$ref": "53" }, "depth": 0, "children": [{ "$ref": "54" }] }], "id": "0", "text": "0", "name": "", "parent": null, "root": { "$ref": "45" }, "depth": 0, "children": [{ "$ref": "46" }, { "$ref": "53" }] }
Required Output:
var treeNode = {
id: 101, // random
text: object.name,
icon: "fas fa-plus",
subNode: {
// id, text, icon and subNode of Children object
// recursive data, So on....
}
};
Can anyone suggest me or help me to write javascript or jQuery Recursive function based on above JSON data so I can build tree structure. I know I am asking about help because I do have less knowledge about recursive function.
If we abstract this a bit, it's pretty easy to write a general-purpose tree-mapping function. Then we can supply two callback functions: one to find the child nodes of the input and one to build the output node based on the input and the mapped children. Such a function turns out to be surprisingly simple:
const mapTree = (getChildren, transformNode) => (tree) =>
transformNode (
tree,
(getChildren (tree) || []) .map (mapTree (getChildren, transformNode))
)
For your data, getChildren is simply (node) => node._children
And the node transformation might be as simple as:
const transformNode = (node, children) =>
({
id: node.$id, // or a randomizing call?
text: node.name,
icon: "fas fa-plus", // is this really a fixed value?
subNode: children
})
Putting this together we get
const mapTree = (getChildren, transformNode) => (tree) =>
transformNode (
tree,
(getChildren (tree) || []) .map (mapTree (getChildren, transformNode))
)
const kids = (node) => node._children
const transformNode = (node, children) =>
({
id: node.$id,
text: node.name,
icon: "fas fa-plus",
subNode: children
})
const myTransform = mapTree (kids, transformNode)
const jsonData = { "$id": "45", "_children": [{ "$id": "46", "_children": [{ "$id": "47", "_children": [{ "$id": "48", "_children": [{ "$id": "49", "_children": null, "id": "Test1", "text": "Text1", "name": "name1", "parent": null, "root": { "$ref": "49" }, "depth": 0, "children": [] }], "id": "id1", "text": "text2", "name": "name2", "parent": null, "root": { "$ref": "48" }, "depth": 0, "children": [{ "$ref": "49" }] }], "id": "id3", "text": "text4", "name": "name4", "parent": null, "root": { "$ref": "47" }, "depth": 0, "children": [{ "$ref": "48" }] }, { "$id": "50", "_children": [{ "$id": "51", "_children": [{ "$id": "52", "_children": null, "id": "id6", "text": "text6", "name": "name6", "parent": null, "root": { "$ref": "52" }, "depth": 0, "children": [] }], "id": "id7", "text": "text7", "name": "name7", "parent": null, "root": { "$ref": "51" }, "depth": 0, "children": [{ "$ref": "52" }] }], "id": "id8", "text": "text8", "name": "name8", "parent": null, "root": { "$ref": "50" }, "depth": 0, "children": [{ "$ref": "51" }] }], "id": "id9", "text": "text9", "name": "name9", "parent": null, "root": { "$ref": "46" }, "depth": 0, "children": [{ "$ref": "47" }, { "$ref": "50" }] }, { "$id": "53", "_children": [{ "$id": "54", "_children": null, "id": "id10", "text": "text10", "name": "name10", "parent": null, "root": { "$ref": "54" }, "depth": 0, "children": [] }], "id": "id11", "text": "text11", "name": "name11", "parent": null, "root": { "$ref": "53" }, "depth": 0, "children": [{ "$ref": "54" }] }], "id": "0", "text": "0", "name": "", "parent": null, "root": { "$ref": "45" }, "depth": 0, "children": [{ "$ref": "46" }, { "$ref": "53" }] }
console .log (myTransform (jsonData))
This does something slightly different from your requested output. You had written subNode: { ... }, but instead I'm returning an array of objects, subNodes: [ ... ], as I don't make any real sense of a plain object here.
Also, this will yield an empty subNodes array if an input node has no children. If you would rather not have the subNodes property, you could replace
subNode: children
with something like
...(children .length ? {subNode: children} : {})
Obviously, you don't need the named helpers and could call mapTree with anonymous functions like this:
const myTransform = mapTree (
(node) => node._children,
(node, children) =>
({
id: node.$id,
text: node.name,
icon: "fas fa-plus",
subNode: children
})
)
This mapTree function was very easy to write, as I didn't have to think about any details of the output or input formats as I wrote it. But perhaps that abstraction is not helpful to me, and I'm never going to use it except here. If so, I can simply rework the abstract version by plugging the hard-coded callbacks directly. With only a little manipulation, that will turn it into this version:
const newTransform = (node) =>
({
id: node.$id,
text: node.name,
icon: "fas fa-plus",
subNode: (node._children || []).map(newTransform)
})
const jsonData = { "$id": "45", "_children": [{ "$id": "46", "_children": [{ "$id": "47", "_children": [{ "$id": "48", "_children": [{ "$id": "49", "_children": null, "id": "Test1", "text": "Text1", "name": "name1", "parent": null, "root": { "$ref": "49" }, "depth": 0, "children": [] }], "id": "id1", "text": "text2", "name": "name2", "parent": null, "root": { "$ref": "48" }, "depth": 0, "children": [{ "$ref": "49" }] }], "id": "id3", "text": "text4", "name": "name4", "parent": null, "root": { "$ref": "47" }, "depth": 0, "children": [{ "$ref": "48" }] }, { "$id": "50", "_children": [{ "$id": "51", "_children": [{ "$id": "52", "_children": null, "id": "id6", "text": "text6", "name": "name6", "parent": null, "root": { "$ref": "52" }, "depth": 0, "children": [] }], "id": "id7", "text": "text7", "name": "name7", "parent": null, "root": { "$ref": "51" }, "depth": 0, "children": [{ "$ref": "52" }] }], "id": "id8", "text": "text8", "name": "name8", "parent": null, "root": { "$ref": "50" }, "depth": 0, "children": [{ "$ref": "51" }] }], "id": "id9", "text": "text9", "name": "name9", "parent": null, "root": { "$ref": "46" }, "depth": 0, "children": [{ "$ref": "47" }, { "$ref": "50" }] }, { "$id": "53", "_children": [{ "$id": "54", "_children": null, "id": "id10", "text": "text10", "name": "name10", "parent": null, "root": { "$ref": "54" }, "depth": 0, "children": [] }], "id": "id11", "text": "text11", "name": "name11", "parent": null, "root": { "$ref": "53" }, "depth": 0, "children": [{ "$ref": "54" }] }], "id": "0", "text": "0", "name": "", "parent": null, "root": { "$ref": "45" }, "depth": 0, "children": [{ "$ref": "46" }, { "$ref": "53" }] }
console .log (newTransform (jsonData))
There's an important point here. This generic function was much easier to write than if I'd tried to write something to convert your format directly. While there is a danger in too-early abstraction, it also can offer significant benefits. I might well choose to keep only that last version, but the generic abstraction simplified the development of it.
It can be something like that, with using the json data model
<!doctype html>
<html>
<head>
<link rel="stylesheet" href="lib/style.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
</head>
<body>
<div id="myDiv"></div>
</body>
<script>
var treeData={
"Id":"10",
"text":"Document Categories",
"icon":"fas fa-plus",
"subNode":
[
{
"Id":"11",
"text":"Pdf Documents",
"icon":"fas fa-plus",
"subNode":[
{
"Id":"31",
"text":"Book Pdfs",
"icon":"fas fa-plus",
"subNode":[]
},
{
"Id":"32",
"text":"EPub",
"icon":"fas fa-plus",
"subNode":[
{
"Id":"20",
"text":"EBook Epubs1",
"icon":"fas fa-plus",
"subNode":[]
},
{
"Id":"30",
"text":"EBook Epubs2",
"icon":"fas fa-plus",
"subNode":[]
}
]
}
]
},
{
"Id":"33",
"text":"Text Documents",
"icon":"fas fa-plus",
"subNode":[
{
"Id":"32",
"text":"Book Text",
"icon":"fas fa-plus",
"subNode":[]
},
{
"Id":"35",
"text":"Automatic Text",
"icon":"fas fa-plus",
"subNode":[]
}
]
}
]
};
var newTree = AddRecursive(null, treeData);
var treeDiv = $('#myDiv');
treeDiv.append(newTree);
function AddRecursive(tree, data) {
if (tree == null) {
tree = $('<ul/>');
tree.attr('id', 'treeID');
}
var listU = $('<ul />');
listU.addClass('ul class');
var listItem = $('<li />');
listItem.addClass('li class');
listItem.attr('data-id', data.Id);
var link = $('<a />');
var i = $('<i/>').addClass('fa fa-folder');
link.append(i);
//link.addClass("linkClass");
link.append(data.text);
listItem.append(link);
if (data.subNode.length > 0) {
var span = $(' <span />');
span.addClass('fa-chevron-down');
link.append(span);
}
listU.append(listItem);
tree.append(listU);
for (i in data.subNode) {
AddRecursive(listItem, data.subNode[i]);
}
return tree;
}
</script>
</html>

Why can't I produce unique objects array in this snippet?

I have the following code snippet, where I want original array (filters) to be filtered and get unique filters array.
I think I tried all the methods possible and it (uniqueFilters array) is still same 11 member original array.
Whats wrong?
How can I ensure that this array is actually filtered down to just a few unique elements in it? (they all are coming from the same place and are the same)
UPDATE:
- the answer successfully resolved the issue in js code.
- in the actual app I had to also deal with typescript to use the suggested answer. So maybe will help someone:
let newUniqueFilters = Array.from(new Map(filters.map(f => [f._id, f] as [string, any])).values());
var filters = [{
"_id": "filter1",
"filterIndex": 1,
"filterLabel": "Blur",
"filterURL": "url(#filter1)",
"filterEffects": [{
"name": "feGaussianBlur",
"attributes": [{
"name": "stdDeviation",
"value": 5
}]
}]
}, {
"_id": "filter2",
"filterIndex": 2,
"filterLabel": "Shadow",
"filterURL": "url(#filter2)",
"filterEffects": [{
"name": "feOffset",
"attributes": [{
"name": "dx",
"value": 20
}, {
"name": "dy",
"value": 20
}, {
"name": "result",
"value": "offOut"
}, {
"name": "in",
"value": "SourceAlpha"
}]
}, {
"name": "feGaussianBlur",
"attributes": [{
"name": "stdDeviation",
"value": 7
}, {
"name": "in",
"value": "offOut"
}, {
"name": "result",
"value": "blurOut"
}]
}, {
"name": "feBlend",
"attributes": [{
"name": "mode",
"value": "normal"
}, {
"name": "in2",
"value": "blurOut"
}, {
"name": "in",
"value": "SourceGraphic"
}]
}]
}, {
"_id": "filter2",
"filterIndex": 2,
"filterLabel": "Shadow",
"filterURL": "url(#filter2)",
"filterEffects": [{
"name": "feOffset",
"attributes": [{
"name": "dx",
"value": 20
}, {
"name": "dy",
"value": 20
}, {
"name": "result",
"value": "offOut"
}, {
"name": "in",
"value": "SourceAlpha"
}]
}, {
"name": "feGaussianBlur",
"attributes": [{
"name": "stdDeviation",
"value": 7
}, {
"name": "in",
"value": "offOut"
}, {
"name": "result",
"value": "blurOut"
}]
}, {
"name": "feBlend",
"attributes": [{
"name": "mode",
"value": "normal"
}, {
"name": "in2",
"value": "blurOut"
}, {
"name": "in",
"value": "SourceGraphic"
}]
}]
}, {
"_id": "filter1",
"filterIndex": 1,
"filterLabel": "Blur",
"filterURL": "url(#filter1)",
"filterEffects": [{
"name": "feGaussianBlur",
"attributes": [{
"name": "stdDeviation",
"value": 5
}]
}]
}, {
"_id": "filter1",
"filterIndex": 1,
"filterLabel": "Blur",
"filterURL": "url(#filter1)",
"filterEffects": [{
"name": "feGaussianBlur",
"attributes": [{
"name": "stdDeviation",
"value": 5
}]
}]
}, {
"_id": "filter1",
"filterIndex": 1,
"filterLabel": "Blur",
"filterURL": "url(#filter1)",
"filterEffects": [{
"name": "feGaussianBlur",
"attributes": [{
"name": "stdDeviation",
"value": 5
}]
}]
}, {
"_id": "filter2",
"filterIndex": 2,
"filterLabel": "Shadow",
"filterURL": "url(#filter2)",
"filterEffects": [{
"name": "feOffset",
"attributes": [{
"name": "dx",
"value": 20
}, {
"name": "dy",
"value": 20
}, {
"name": "result",
"value": "offOut"
}, {
"name": "in",
"value": "SourceAlpha"
}]
}, {
"name": "feGaussianBlur",
"attributes": [{
"name": "stdDeviation",
"value": 7
}, {
"name": "in",
"value": "offOut"
}, {
"name": "result",
"value": "blurOut"
}]
}, {
"name": "feBlend",
"attributes": [{
"name": "mode",
"value": "normal"
}, {
"name": "in2",
"value": "blurOut"
}, {
"name": "in",
"value": "SourceGraphic"
}]
}]
}, {
"_id": "filter1",
"filterIndex": 1,
"filterLabel": "Blur",
"filterURL": "url(#filter1)",
"filterEffects": [{
"name": "feGaussianBlur",
"attributes": [{
"name": "stdDeviation",
"value": 5
}]
}]
}, {
"_id": "filter2",
"filterIndex": 2,
"filterLabel": "Shadow",
"filterURL": "url(#filter2)",
"filterEffects": [{
"name": "feOffset",
"attributes": [{
"name": "dx",
"value": 20
}, {
"name": "dy",
"value": 20
}, {
"name": "result",
"value": "offOut"
}, {
"name": "in",
"value": "SourceAlpha"
}]
}, {
"name": "feGaussianBlur",
"attributes": [{
"name": "stdDeviation",
"value": 7
}, {
"name": "in",
"value": "offOut"
}, {
"name": "result",
"value": "blurOut"
}]
}, {
"name": "feBlend",
"attributes": [{
"name": "mode",
"value": "normal"
}, {
"name": "in2",
"value": "blurOut"
}, {
"name": "in",
"value": "SourceGraphic"
}]
}]
}, {
"_id": "filter1",
"filterIndex": 1,
"filterLabel": "Blur",
"filterURL": "url(#filter1)",
"filterEffects": [{
"name": "feGaussianBlur",
"attributes": [{
"name": "stdDeviation",
"value": 5
}]
}]
}, {
"_id": "filter2",
"filterIndex": 2,
"filterLabel": "Shadow",
"filterURL": "url(#filter2)",
"filterEffects": [{
"name": "feOffset",
"attributes": [{
"name": "dx",
"value": 20
}, {
"name": "dy",
"value": 20
}, {
"name": "result",
"value": "offOut"
}, {
"name": "in",
"value": "SourceAlpha"
}]
}, {
"name": "feGaussianBlur",
"attributes": [{
"name": "stdDeviation",
"value": 7
}, {
"name": "in",
"value": "offOut"
}, {
"name": "result",
"value": "blurOut"
}]
}, {
"name": "feBlend",
"attributes": [{
"name": "mode",
"value": "normal"
}, {
"name": "in2",
"value": "blurOut"
}, {
"name": "in",
"value": "SourceGraphic"
}]
}]
}]
// first method:
var uniqueFilters = [];
for(let i = 0; i < filters.length; i++) {
if(uniqueFilters.indexOf(filters[i]) == -1){
uniqueFilters.push(filters[i])
}
}
console.log("first method")
console.log("original array length:" + filters.length)
console.log("unique array length:" + uniqueFilters.length)
// second method:
//var uniqueFilters = filters.filter(function(elem, index, self) {
// return index == self.indexOf(elem);
//});
//console.log("second method")
//console.log("original array length:" + filters.length)
//console.log("unique array length:" + uniqueFilters.length)
// suggested method 1:
var newUniqueFilters = Array.from(new Map(filters.map(f => [f._id, f])).values());
console.log(newUniqueFilters)
You can use a Map:
filters = Array.from(new Map(filters.map(f => [f._id, f])).values());
This assumes that it is enough to compare the _id values, which uniquely identify filters.
Note that comparing objects themselves (with indexOf or similar methods) will never show duplicates, as all the objects are different references (copies), even though they look the same. In general {} === {} is always false.
It's not working because every element in filter array has different memory location and object are compared by the memory location.
So every time you compare that object exist of filter array in uniqueFilters, it simply doesn't exist because every object has different memory location.
so it will push every element in uniqueFilters.
Try changing this line
if(uniqueFilters.indexOf(filters[i]) == -1){
to
if(uniqueFilters.indexOf(filters[i]) === -1){
If not worikingn you can too try comparing a specific attibute to all uniqueFilter

Categories