使用父对象值将路径添加到嵌套对象的递归函数

Recursive function to add path to nested object using parent object values

我正在尝试使用其祖先的属性向所有嵌套对象添加“路径”。本质上,我想使用其父对象的属性值的串联而不是使用键(就像我可以使用点符号、lodash 等)来表示嵌套对象的层次结构的“路径”。

我尝试过的:

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'
                    }
                ]
            }
        ]
    }
])

我的预期结果是:

[
    {
        "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"
    }
]

但我得到以下结果,其中原始嵌套对象的 slug 是非祖先对象的新 slug_path 属性的一部分。

[
    {
        "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
    }
]

你可以关闭路径。

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; }

如果您不介意也将 slugPath 添加到您的根元素(我个人认为在任何情况下这对于一致性更好),那么应该这样做:

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}

请注意,我们不会在此处更改任何输入数据,而只是 return 适当添加了 slugPath 的副本。

我们在 path 中保留一个节点数组,并在每个递归级别添加到它。 ___ 参数只是为了覆盖 map 提供给回调函数的索引和完整数组。