I'm trying to add a "path" to all nested objects using their ancestor's attributes. Essentially I want to represent a "path" of the hierarchical structure to a nested object using a concatenation of its parent object's attribute values instead of using keys (like I could with dot notation, lodash, etc).
What I've tried:
interface SimpleObject {
name : string
slug : string
slug_path? : string
fields? : SimpleObject[]
}
const addNestedObjSlug = (data:SimpleObject[], parents:SimpleObject[] = []) => {
data.map((obj) => {
obj.slug_path = parents.length > 0 ? `${parents.map(({slug}) => slug).join('.')}.${obj.slug}` : obj.slug
if(obj.fields && obj.fields.length > 0) {
parents.push(obj)
// I think the issue is here, that I probably need to set parents to an empty array at some point
return addNestedObjSlug(obj.fields, parents)
}
})
return data
}
const desiredResult = addNestedObjSlug([
{
name : 'Item 1',
slug : 'i1',
fields : [
{
name : 'Item 1 - 1',
slug : 'i1-1'
},
{
name : 'Item 1 - 2',
slug : 'i1-2'
},
{
name : 'Item 1 - 3',
slug : 'i1-3'
}
]
},
{
name : 'Item 2',
slug : 'i2',
fields : [
{
name : 'Item 2 - 1',
slug : 'i2-1',
fields : [
{
name : 'Item 2 - 1 - 1',
slug : 'i2-1-1'
}
]
}
]
}
])
My expected result is:
[
{
"name": "Item 1",
"slug": "i1",
"fields": [
{
"name": "Item 1 - 1",
"slug": "i1-1",
"slug_path": "i1.i1-1"
},
{
"name": "Item 1 - 2",
"slug": "i1-2",
"slug_path": "i1.i1-2"
},
{
"name": "Item 1 - 3",
"slug": "i1-3",
"slug_path": "i1.i1-3"
}
],
"slug_path": "i1"
},
{
"name": "Item 2",
"slug": "i2",
"fields": [
{
"name": "Item 2 - 1",
"slug": "i2-1",
"fields": [
{
"name": "Item 2 - 1 - 1",
"slug": "i2-1-1",
"slug_path": "i2.i2-1.i2-1-1"
}
],
"slug_path": "i2.i2-1"
}
],
"slug_path": "i2"
}
]
But instead I get the following, where the original nested object's slug is part of the new slug_path attribute of objects which are not ancestors.
[
{
"name": "Item 1",
"slug": "i1",
"fields": [
{
"name": "Item 1 - 1",
"slug": "i1-1",
"slug_path": "i1.i1-1" // correct
},
{
"name": "Item 1 - 2",
"slug": "i1-2",
"slug_path": "i1.i1-2" // correct
},
{
"name": "Item 1 - 3",
"slug": "i1-3",
"slug_path": "i1.i1-3" // correct
}
],
"slug_path": "i1" // correct
},
{
"name": "Item 2",
"slug": "i2",
"fields": [
{
"name": "Item 2 - 1",
"slug": "i2-1",
"fields": [
{
"name": "Item 2 - 1 - 1",
"slug": "i2-1-1",
"slug_path": "i1.i2.i2-1.i2-1-1" // incorrect
}
],
"slug_path": "i1.i2.i2-1" // incorrect
}
],
"slug_path": "i1.i2" // incorrect
}
]
CodePudding user response:
If you don't mind also adding the slugPath to your root elements (which I personally think is better for consistency in any case), then this should do:
const addSlugPath = (items, path = []) =>
items .map (({slug, fields, ...rest}, _, __, newPath = [...path, slug]) => ({
... rest,
slug,
slugPath: newPath .join ('.'),
... (fields ? {fields: addSlugPath (fields, newPath)} : {})
}))
const input = [{name: "Item 1", slug: "i1", fields: [{name: "Item 1 - 1", slug: "i1-1"}, {name: "Item 1 - 2", slug: "i1-2"}, {name: "Item 1 - 3", slug: "i1-3"}]}, {name: "Item 2", slug: "i2", fields: [{name: "Item 2 - 1", slug: "i2-1", fields: [{name: "Item 2 - 1 - 1", slug: "i2-1-1"}]}]}]
console .log (addSlugPath (input))
.as-console-wrapper {max-height: 100% !important; top: 0}
Note that we don't mutate any input data here but only return a copy with the slugPath added appropriately.
We keep an array of nodes in path and add to it at each recursive level. The _ and __ parameters are just to cover the index and full array that map supplies to its callback function.
CodePudding user response:
You could take a closure over the path.
const
add = p => o => {
const
slug_path = p (p && '.') o.slug,
fields = (o.fields || []).map(add(slug_path));
return { ...o, slug_path, ...(fields.length ? { fields } : {}) };
},
addNestedObjSlug = array => array.map(add(''));
console.log(addNestedObjSlug([{ name: 'Item 1', slug: 'i1', fields: [{ name: 'Item 1 - 1', slug: 'i1-1' }, { name: 'Item 1 - 2', slug: 'i1-2' }, { name: 'Item 1 - 3', slug: 'i1-3' }] }, { name: 'Item 2', slug: 'i2', fields: [{ name: 'Item 2 - 1', slug: 'i2-1', fields: [{ name: 'Item 2 - 1 - 1', slug: 'i2-1-1' }] }] }]));
.as-console-wrapper { max-height: 100% !important; top: 0; }
