将 JSON 对象转换为嵌套值、标签、子项
Transform JSON object to nested value, label, children
我有以下 JSON:
{
"notebook": [{
"categories": [{
"key": "notebook",
"value": "Notebook"
}],
"companies": [{
"key": "apple",
"value": "Apple"
}],
"brands": [{
"key": "macbook",
"value": "MacBook"
}],
"modelYears": [{
"key": "2021",
"value": "2021"
}],
"models": [{
"key": "2021 Pro",
"value": "2021 Pro"
}]
},
{
"categories": [{
"key": "notebook",
"value": "Notebook"
}],
"companies": [{
"key": "ibm",
"value": "IBM"
},
{
"key": "lenovo",
"value": "Lenovo"
}
],
"brands": [{
"key": "thinkpad",
"value": "Thinkpad"
}],
"modelYears": [{
"key": "2019",
"value": "2019"
}],
"models": [{
"key": "e590",
"value": "E590"
}]
},
{
"categories": [{
"key": "notebook",
"value": "Notebook"
}],
"companies": [{
"key": "ibm",
"value": "IBM"
},
{
"key": "lenovo",
"value": "Lenovo"
}
],
"brands": [{
"key": "thinkpad",
"value": "Thinkpad"
}],
"modelYears": [{
"key": "2019",
"value": "2019"
}],
"models": [{
"key": "p1",
"value": "P1"
}]
}
],
"smartphone": [{
"categories": [{
"key": "smartphone",
"value": "Smartphone"
}],
"companies": [{
"key": "apple",
"value": "Apple"
}],
"brands": [{
"key": "iphone",
"value": "IPHONE"
}],
"modelYears": [{
"key": "2019",
"value": "2019"
}],
"models": [{
"key": "11pro",
"value": "11 Pro"
}]
},
{
"categories": [{
"key": "smartphone",
"value": "Smartphone"
}],
"companies": [{
"key": "apple",
"value": "Apple"
}],
"brands": [{
"key": "iphone",
"value": "IPHONE"
}],
"modelYears": [{
"key": "2020",
"value": "2020"
}],
"models": [{
"key": "12",
"value": "12"
}]
}
]
}
我想要的结构是:
[
{
value: "notebook",
label: "Notebook",
children: [
{
value: "apple",
label: "Apple",
children: [
{
value: "macbook",
label: "MacBook",
children: [
{
value: "2021pro",
label: "2021 Pro",
},
{
value: "2019",
label: "2019",
},
],
},
],
},
{
value: "ibm",
label: "IBM",
children: [
{
value: "thinkpad",
label: "Thinkpad",
children: [
{
value: "e590",
label: "E590",
},
{
value: "p1",
label: "P1",
},
],
},
],
},
{
value: "lenovo",
label: "Lenovo",
children: [
{
value: "thinkpad",
label: "Thinkpad",
children: [
{
value: "e590",
label: "E590",
},
{
value: "p1",
label: "P1",
},
],
},
],
},
],
},
{
value: "Smartphone",
label: "Smartphone",
children: [
{
value: "apple",
label: "Apple",
children: [
{
value: "iphone",
label: "IPHONE",
children: [
{
value: "11pro",
label: "11 Pro",
},
{
value: "12",
label: "12",
},
],
},
],
},
],
},
];
如何做到这一点?
寻找 Array.prototype.reduce / Array.prototype.map
的解决方案
const data = {
"notebook": [{
"categories": [{
"key": "notebook",
"value": "Notebook"
}],
"companies": [{
"key": "apple",
"value": "Apple"
}],
"brands": [{
"key": "macbook",
"value": "MacBook"
}],
"modelYears": [{
"key": "2021",
"value": "2021"
}],
"models": [{
"key": "2021 Pro",
"value": "2021 Pro"
}]
},
{
"categories": [{
"key": "notebook",
"value": "Notebook"
}],
"companies": [{
"key": "ibm",
"value": "IBM"
},
{
"key": "lenovo",
"value": "Lenovo"
}
],
"brands": [{
"key": "thinkpad",
"value": "Thinkpad"
}],
"modelYears": [{
"key": "2019",
"value": "2019"
}],
"models": [{
"key": "e590",
"value": "E590"
}]
},
{
"categories": [{
"key": "notebook",
"value": "Notebook"
}],
"companies": [{
"key": "ibm",
"value": "IBM"
},
{
"key": "lenovo",
"value": "Lenovo"
}
],
"brands": [{
"key": "thinkpad",
"value": "Thinkpad"
}],
"modelYears": [{
"key": "2019",
"value": "2019"
}],
"models": [{
"key": "p1",
"value": "P1"
}]
}
],
"smartphone": [{
"categories": [{
"key": "smartphone",
"value": "Smartphone"
}],
"companies": [{
"key": "apple",
"value": "Apple"
}],
"brands": [{
"key": "iphone",
"value": "IPHONE"
}],
"modelYears": [{
"key": "2019",
"value": "2019"
}],
"models": [{
"key": "11pro",
"value": "11 Pro"
}]
},
{
"categories": [{
"key": "smartphone",
"value": "Smartphone"
}],
"companies": [{
"key": "apple",
"value": "Apple"
}],
"brands": [{
"key": "iphone",
"value": "IPHONE"
}],
"modelYears": [{
"key": "2020",
"value": "2020"
}],
"models": [{
"key": "12",
"value": "12"
}]
}
]
};
const transforemd = Object.entries(data).map(([product, data]) => {
const item = {
children: []
};
data.forEach(value => {
item.value = value.categories?.[0].key;
item.label = value.categories?.[0].value;
value.companies?.forEach(company => {
let sub = {
value: company.key,
label: company.value,
children: []
};
let toFind = item.children.find(element => element.value === company.key);
if (toFind)
sub = toFind;
else
item.children.push(sub);
value.brands?.forEach(brand => {
let entry = {
value: brand.key,
label: brand.value,
children: []
};
let toFind = sub.children.find(element => element.value === brand.key);
if (toFind)
entry = toFind;
else
sub.children.push(entry);
value.models?.forEach(model => {
let product = {
value: model.key,
label: model.value,
};
entry.children.push(product);
});
});
});
});
return item;
});
console.log(transforemd);
这是我会做的:
- 首先我会稍微改变初始数据:
mydata = [...mydata.notebook, ...mydata.smartphone];
这与调用 Array.flat()
相同(仅在逻辑上),我从 object 开始删除 notebook
和 smartphone
键,合并所有嵌套的 objects 在同一个数组中。其实你不需要它们,你只是对category
.
感兴趣
- 下一部分可能是创建一些实用方法,例如:
function getOrCreateChild(children, entry) {
let child = children.find(c => c.value === entry.key && c.label === entry.value);
if (child === undefined) {
child = {
value: entry.key,
label: entry.value,
children: []
}
children.push(child);
}
return child;
}
仅当访问的键(被视为 (value, label)
对)尚不存在时,您才想将新的 child 附加到 children
。
另一个包含主要逻辑的是:
const hierarchyOrder = ["categories", "companies", "brands", "models"];
function fillNestedLevels(children, cur, hierarchyLevel) {
if (hierarchyLevel === hierarchyOrder.length)
return;
const entryArray = cur[hierarchyOrder[hierarchyLevel]];
for (const entry of entryArray) {
let child = getOrCreateChild(children, entry);
fillNestedLevels(child.children, cur, hierarchyLevel + 1);
}
}
为初始数组中的每个 object (cur
) 调用此方法,其目的是用确定的正确级别的条目填充 children
数组通过 hierarchyOrder[hierarchyLevel]
。为了避免重复children我们使用前面介绍的方法,即getOrCreateChild
.
最后,入口点是一个 reduce
函数,它仅使用累加器(以空数组开头)和数组元素调用上述方法。
const result = mydata.reduce((acc, cur) => {
fillNestedLevels(acc, cur, 0);
return acc;
}, [])
总计:
let mydata = {
"notebook": [{
"categories": [{
"key": "notebook",
"value": "Notebook"
}],
"companies": [{
"key": "apple",
"value": "Apple"
}],
"brands": [{
"key": "macbook",
"value": "MacBook"
}],
"modelYears": [{
"key": "2021",
"value": "2021"
}],
"models": [{
"key": "2021 Pro",
"value": "2021 Pro"
}]
},
{
"categories": [{
"key": "notebook",
"value": "Notebook"
}],
"companies": [{
"key": "ibm",
"value": "IBM"
},
{
"key": "lenovo",
"value": "Lenovo"
}
],
"brands": [{
"key": "thinkpad",
"value": "Thinkpad"
}],
"modelYears": [{
"key": "2019",
"value": "2019"
}],
"models": [{
"key": "e590",
"value": "E590"
}]
},
{
"categories": [{
"key": "notebook",
"value": "Notebook"
}],
"companies": [{
"key": "ibm",
"value": "IBM"
},
{
"key": "lenovo",
"value": "Lenovo"
}
],
"brands": [{
"key": "thinkpad",
"value": "Thinkpad"
}],
"modelYears": [{
"key": "2019",
"value": "2019"
}],
"models": [{
"key": "p1",
"value": "P1"
}]
}
],
"smartphone": [{
"categories": [{
"key": "smartphone",
"value": "Smartphone"
}],
"companies": [{
"key": "apple",
"value": "Apple"
}],
"brands": [{
"key": "iphone",
"value": "IPHONE"
}],
"modelYears": [{
"key": "2019",
"value": "2019"
}],
"models": [{
"key": "11pro",
"value": "11 Pro"
}]
},
{
"categories": [{
"key": "smartphone",
"value": "Smartphone"
}],
"companies": [{
"key": "apple",
"value": "Apple"
}],
"brands": [{
"key": "iphone",
"value": "IPHONE"
}],
"modelYears": [{
"key": "2020",
"value": "2020"
}],
"models": [{
"key": "12",
"value": "12"
}]
}
]
}
mydata = [...mydata.notebook, ...mydata.smartphone];
const hierarchyOrder = ["categories", "companies", "brands", "models"];
function getOrCreateChild(children, entry) {
let child = children.find(c => c.value === entry.key && c.label === entry.value);
if (child === undefined) {
child = {
value: entry.key,
label: entry.value,
children: []
}
children.push(child);
}
return child;
}
function fillNestedLevels(children, cur, hierarchyLevel) {
if (hierarchyLevel === hierarchyOrder.length)
return;
const entryArray = cur[hierarchyOrder[hierarchyLevel]];
for (const entry of entryArray) {
let child = getOrCreateChild(children, entry);
fillNestedLevels(child.children, cur, hierarchyLevel + 1);
}
}
const result = mydata.reduce((acc, cur) => {
fillNestedLevels(acc, cur, 0);
return acc;
}, []);
console.log(result);
一些最后的想法:
- 看起来代码很多,但非常简单易维护。在这种情况下,我不鼓励您搜索“one-line”解决方案,这些解决方案对于您和您的同事来说可能真的很难理解。
- 这也是一个非常灵活的解决方案。如果将来您想要包含
modelYears
,您只需将其附加到 hierarchyOrder
。
- 为了保持一致性,即使嵌套最多的 object 也有
children
属性。这当然是你可以控制的,但我想忽略它们不会有什么不同。
这是我的方法。它使用内置数组函数,如 reduce
、filter
、map
.
const out = Object.values(input).map(e => {
const _get = key => ({ [key]: e.map(m => [...m[key]]).flat()
.filter((m, i, a) => !a.slice(i + 1).find(c => c.key === m.key)) });
const _join = (parent, children) => {
const [pkey, ckey] = [Object.keys(parent)[0], Object.keys(children)[0]];
return {
[pkey]: parent[pkey].map(p => ({ value: p.key, label: p.value,
children: e.reduce((o, m) =>
o.concat(m[pkey].find(pp => pp.key === p.key) ?
children[ckey].filter(child => m[ckey]
.find(mm => mm.key === (child.key || child.value)) &&
!o.find(c => (c.key || c.value) === (child.key || child.value))) : []), [])
})
)
};
};
return { value: e[0].categories[0].key, label: e[0].categories[0].value,
children: Object.values(_join(_get("companies"), _join(_get("brands"), _get("models"))))[0] };
});
它有两个功能:
- _get()
- _join()
- 调用
_get()
时,您将要搜索的键作为参数传递。它可以是 companies
、brands
、models
等。它 returns 一个数组,其中包含您作为参数传递的所有键值。例如
_get("companies")
returns
{
"companies": [
{
"key": "apple",
"value": "Apple"
},
{
"key": "ibm",
"value": "IBM"
},
{
"key": "lenovo",
"value": "Lenovo"
}
]
}
_join()
函数有神奇的作用。它需要一个父组和一个子组,并将它们转换为您想要的格式:
这是一个工作示例
const input = {
"notebook": [{
"categories": [{
"key": "notebook",
"value": "Notebook"
}],
"companies": [{
"key": "apple",
"value": "Apple"
}],
"brands": [{
"key": "macbook",
"value": "MacBook"
}],
"modelYears": [{
"key": "2021",
"value": "2021"
}],
"models": [{
"key": "2021 Pro",
"value": "2021 Pro"
}]
},
{
"categories": [{
"key": "notebook",
"value": "Notebook"
}],
"companies": [{
"key": "ibm",
"value": "IBM"
},
{
"key": "lenovo",
"value": "Lenovo"
}
],
"brands": [{
"key": "thinkpad",
"value": "Thinkpad"
}],
"modelYears": [{
"key": "2019",
"value": "2019"
}],
"models": [{
"key": "e590",
"value": "E590"
}]
},
{
"categories": [{
"key": "notebook",
"value": "Notebook"
}],
"companies": [{
"key": "ibm",
"value": "IBM"
},
{
"key": "lenovo",
"value": "Lenovo"
}
],
"brands": [{
"key": "thinkpad",
"value": "Thinkpad"
}],
"modelYears": [{
"key": "2019",
"value": "2019"
}],
"models": [{
"key": "p1",
"value": "P1"
}]
}
],
"smartphone": [{
"categories": [{
"key": "smartphone",
"value": "Smartphone"
}],
"companies": [{
"key": "apple",
"value": "Apple"
}],
"brands": [{
"key": "iphone",
"value": "IPHONE"
}],
"modelYears": [{
"key": "2019",
"value": "2019"
}],
"models": [{
"key": "11pro",
"value": "11 Pro"
}]
},
{
"categories": [{
"key": "smartphone",
"value": "Smartphone"
}],
"companies": [{
"key": "apple",
"value": "Apple"
}],
"brands": [{
"key": "iphone",
"value": "IPHONE"
}],
"modelYears": [{
"key": "2020",
"value": "2020"
}],
"models": [{
"key": "12",
"value": "12"
}]
}
]
}
const out = Object.values(input).map(e => {
const _get = key => ({ [key]: e.map(m => [...m[key]]).flat()
.filter((m, i, a) => !a.slice(i + 1).find(c => c.key === m.key)) });
const _join = (parent, children) => {
const [pkey, ckey] = [Object.keys(parent)[0], Object.keys(children)[0]];
return {
[pkey]: parent[pkey].map(p => ({ value: p.key, label: p.value,
children: e.reduce((o, m) =>
o.concat(m[pkey].find(pp => pp.key === p.key) ?
children[ckey].filter(child => m[ckey]
.find(mm => mm.key === (child.key || child.value)) &&
!o.find(c => (c.key || c.value) === (child.key || child.value))) : []), [])
})
)
};
};
return { value: e[0].categories[0].key, label: e[0].categories[0].value,
children: Object.values(_join(_get("companies"), _join(_get("brands"), _get("models"))))[0] };
});
console.log(out);
.as-console-wrapper { max-height: 100% !important; top: 0; }
请注意 Stack overflow 代码段可能会遗漏一些重复的对象。自己试试就更好了
我有以下 JSON:
{
"notebook": [{
"categories": [{
"key": "notebook",
"value": "Notebook"
}],
"companies": [{
"key": "apple",
"value": "Apple"
}],
"brands": [{
"key": "macbook",
"value": "MacBook"
}],
"modelYears": [{
"key": "2021",
"value": "2021"
}],
"models": [{
"key": "2021 Pro",
"value": "2021 Pro"
}]
},
{
"categories": [{
"key": "notebook",
"value": "Notebook"
}],
"companies": [{
"key": "ibm",
"value": "IBM"
},
{
"key": "lenovo",
"value": "Lenovo"
}
],
"brands": [{
"key": "thinkpad",
"value": "Thinkpad"
}],
"modelYears": [{
"key": "2019",
"value": "2019"
}],
"models": [{
"key": "e590",
"value": "E590"
}]
},
{
"categories": [{
"key": "notebook",
"value": "Notebook"
}],
"companies": [{
"key": "ibm",
"value": "IBM"
},
{
"key": "lenovo",
"value": "Lenovo"
}
],
"brands": [{
"key": "thinkpad",
"value": "Thinkpad"
}],
"modelYears": [{
"key": "2019",
"value": "2019"
}],
"models": [{
"key": "p1",
"value": "P1"
}]
}
],
"smartphone": [{
"categories": [{
"key": "smartphone",
"value": "Smartphone"
}],
"companies": [{
"key": "apple",
"value": "Apple"
}],
"brands": [{
"key": "iphone",
"value": "IPHONE"
}],
"modelYears": [{
"key": "2019",
"value": "2019"
}],
"models": [{
"key": "11pro",
"value": "11 Pro"
}]
},
{
"categories": [{
"key": "smartphone",
"value": "Smartphone"
}],
"companies": [{
"key": "apple",
"value": "Apple"
}],
"brands": [{
"key": "iphone",
"value": "IPHONE"
}],
"modelYears": [{
"key": "2020",
"value": "2020"
}],
"models": [{
"key": "12",
"value": "12"
}]
}
]
}
我想要的结构是:
[
{
value: "notebook",
label: "Notebook",
children: [
{
value: "apple",
label: "Apple",
children: [
{
value: "macbook",
label: "MacBook",
children: [
{
value: "2021pro",
label: "2021 Pro",
},
{
value: "2019",
label: "2019",
},
],
},
],
},
{
value: "ibm",
label: "IBM",
children: [
{
value: "thinkpad",
label: "Thinkpad",
children: [
{
value: "e590",
label: "E590",
},
{
value: "p1",
label: "P1",
},
],
},
],
},
{
value: "lenovo",
label: "Lenovo",
children: [
{
value: "thinkpad",
label: "Thinkpad",
children: [
{
value: "e590",
label: "E590",
},
{
value: "p1",
label: "P1",
},
],
},
],
},
],
},
{
value: "Smartphone",
label: "Smartphone",
children: [
{
value: "apple",
label: "Apple",
children: [
{
value: "iphone",
label: "IPHONE",
children: [
{
value: "11pro",
label: "11 Pro",
},
{
value: "12",
label: "12",
},
],
},
],
},
],
},
];
如何做到这一点?
寻找 Array.prototype.reduce / Array.prototype.map
的解决方案const data = {
"notebook": [{
"categories": [{
"key": "notebook",
"value": "Notebook"
}],
"companies": [{
"key": "apple",
"value": "Apple"
}],
"brands": [{
"key": "macbook",
"value": "MacBook"
}],
"modelYears": [{
"key": "2021",
"value": "2021"
}],
"models": [{
"key": "2021 Pro",
"value": "2021 Pro"
}]
},
{
"categories": [{
"key": "notebook",
"value": "Notebook"
}],
"companies": [{
"key": "ibm",
"value": "IBM"
},
{
"key": "lenovo",
"value": "Lenovo"
}
],
"brands": [{
"key": "thinkpad",
"value": "Thinkpad"
}],
"modelYears": [{
"key": "2019",
"value": "2019"
}],
"models": [{
"key": "e590",
"value": "E590"
}]
},
{
"categories": [{
"key": "notebook",
"value": "Notebook"
}],
"companies": [{
"key": "ibm",
"value": "IBM"
},
{
"key": "lenovo",
"value": "Lenovo"
}
],
"brands": [{
"key": "thinkpad",
"value": "Thinkpad"
}],
"modelYears": [{
"key": "2019",
"value": "2019"
}],
"models": [{
"key": "p1",
"value": "P1"
}]
}
],
"smartphone": [{
"categories": [{
"key": "smartphone",
"value": "Smartphone"
}],
"companies": [{
"key": "apple",
"value": "Apple"
}],
"brands": [{
"key": "iphone",
"value": "IPHONE"
}],
"modelYears": [{
"key": "2019",
"value": "2019"
}],
"models": [{
"key": "11pro",
"value": "11 Pro"
}]
},
{
"categories": [{
"key": "smartphone",
"value": "Smartphone"
}],
"companies": [{
"key": "apple",
"value": "Apple"
}],
"brands": [{
"key": "iphone",
"value": "IPHONE"
}],
"modelYears": [{
"key": "2020",
"value": "2020"
}],
"models": [{
"key": "12",
"value": "12"
}]
}
]
};
const transforemd = Object.entries(data).map(([product, data]) => {
const item = {
children: []
};
data.forEach(value => {
item.value = value.categories?.[0].key;
item.label = value.categories?.[0].value;
value.companies?.forEach(company => {
let sub = {
value: company.key,
label: company.value,
children: []
};
let toFind = item.children.find(element => element.value === company.key);
if (toFind)
sub = toFind;
else
item.children.push(sub);
value.brands?.forEach(brand => {
let entry = {
value: brand.key,
label: brand.value,
children: []
};
let toFind = sub.children.find(element => element.value === brand.key);
if (toFind)
entry = toFind;
else
sub.children.push(entry);
value.models?.forEach(model => {
let product = {
value: model.key,
label: model.value,
};
entry.children.push(product);
});
});
});
});
return item;
});
console.log(transforemd);
这是我会做的:
- 首先我会稍微改变初始数据:
mydata = [...mydata.notebook, ...mydata.smartphone];
这与调用 Array.flat()
相同(仅在逻辑上),我从 object 开始删除 notebook
和 smartphone
键,合并所有嵌套的 objects 在同一个数组中。其实你不需要它们,你只是对category
.
- 下一部分可能是创建一些实用方法,例如:
function getOrCreateChild(children, entry) {
let child = children.find(c => c.value === entry.key && c.label === entry.value);
if (child === undefined) {
child = {
value: entry.key,
label: entry.value,
children: []
}
children.push(child);
}
return child;
}
仅当访问的键(被视为 (value, label)
对)尚不存在时,您才想将新的 child 附加到 children
。
另一个包含主要逻辑的是:
const hierarchyOrder = ["categories", "companies", "brands", "models"];
function fillNestedLevels(children, cur, hierarchyLevel) {
if (hierarchyLevel === hierarchyOrder.length)
return;
const entryArray = cur[hierarchyOrder[hierarchyLevel]];
for (const entry of entryArray) {
let child = getOrCreateChild(children, entry);
fillNestedLevels(child.children, cur, hierarchyLevel + 1);
}
}
为初始数组中的每个 object (cur
) 调用此方法,其目的是用确定的正确级别的条目填充 children
数组通过 hierarchyOrder[hierarchyLevel]
。为了避免重复children我们使用前面介绍的方法,即getOrCreateChild
.
最后,入口点是一个 reduce
函数,它仅使用累加器(以空数组开头)和数组元素调用上述方法。
const result = mydata.reduce((acc, cur) => {
fillNestedLevels(acc, cur, 0);
return acc;
}, [])
总计:
let mydata = {
"notebook": [{
"categories": [{
"key": "notebook",
"value": "Notebook"
}],
"companies": [{
"key": "apple",
"value": "Apple"
}],
"brands": [{
"key": "macbook",
"value": "MacBook"
}],
"modelYears": [{
"key": "2021",
"value": "2021"
}],
"models": [{
"key": "2021 Pro",
"value": "2021 Pro"
}]
},
{
"categories": [{
"key": "notebook",
"value": "Notebook"
}],
"companies": [{
"key": "ibm",
"value": "IBM"
},
{
"key": "lenovo",
"value": "Lenovo"
}
],
"brands": [{
"key": "thinkpad",
"value": "Thinkpad"
}],
"modelYears": [{
"key": "2019",
"value": "2019"
}],
"models": [{
"key": "e590",
"value": "E590"
}]
},
{
"categories": [{
"key": "notebook",
"value": "Notebook"
}],
"companies": [{
"key": "ibm",
"value": "IBM"
},
{
"key": "lenovo",
"value": "Lenovo"
}
],
"brands": [{
"key": "thinkpad",
"value": "Thinkpad"
}],
"modelYears": [{
"key": "2019",
"value": "2019"
}],
"models": [{
"key": "p1",
"value": "P1"
}]
}
],
"smartphone": [{
"categories": [{
"key": "smartphone",
"value": "Smartphone"
}],
"companies": [{
"key": "apple",
"value": "Apple"
}],
"brands": [{
"key": "iphone",
"value": "IPHONE"
}],
"modelYears": [{
"key": "2019",
"value": "2019"
}],
"models": [{
"key": "11pro",
"value": "11 Pro"
}]
},
{
"categories": [{
"key": "smartphone",
"value": "Smartphone"
}],
"companies": [{
"key": "apple",
"value": "Apple"
}],
"brands": [{
"key": "iphone",
"value": "IPHONE"
}],
"modelYears": [{
"key": "2020",
"value": "2020"
}],
"models": [{
"key": "12",
"value": "12"
}]
}
]
}
mydata = [...mydata.notebook, ...mydata.smartphone];
const hierarchyOrder = ["categories", "companies", "brands", "models"];
function getOrCreateChild(children, entry) {
let child = children.find(c => c.value === entry.key && c.label === entry.value);
if (child === undefined) {
child = {
value: entry.key,
label: entry.value,
children: []
}
children.push(child);
}
return child;
}
function fillNestedLevels(children, cur, hierarchyLevel) {
if (hierarchyLevel === hierarchyOrder.length)
return;
const entryArray = cur[hierarchyOrder[hierarchyLevel]];
for (const entry of entryArray) {
let child = getOrCreateChild(children, entry);
fillNestedLevels(child.children, cur, hierarchyLevel + 1);
}
}
const result = mydata.reduce((acc, cur) => {
fillNestedLevels(acc, cur, 0);
return acc;
}, []);
console.log(result);
一些最后的想法:
- 看起来代码很多,但非常简单易维护。在这种情况下,我不鼓励您搜索“one-line”解决方案,这些解决方案对于您和您的同事来说可能真的很难理解。
- 这也是一个非常灵活的解决方案。如果将来您想要包含
modelYears
,您只需将其附加到hierarchyOrder
。 - 为了保持一致性,即使嵌套最多的 object 也有
children
属性。这当然是你可以控制的,但我想忽略它们不会有什么不同。
这是我的方法。它使用内置数组函数,如 reduce
、filter
、map
.
const out = Object.values(input).map(e => {
const _get = key => ({ [key]: e.map(m => [...m[key]]).flat()
.filter((m, i, a) => !a.slice(i + 1).find(c => c.key === m.key)) });
const _join = (parent, children) => {
const [pkey, ckey] = [Object.keys(parent)[0], Object.keys(children)[0]];
return {
[pkey]: parent[pkey].map(p => ({ value: p.key, label: p.value,
children: e.reduce((o, m) =>
o.concat(m[pkey].find(pp => pp.key === p.key) ?
children[ckey].filter(child => m[ckey]
.find(mm => mm.key === (child.key || child.value)) &&
!o.find(c => (c.key || c.value) === (child.key || child.value))) : []), [])
})
)
};
};
return { value: e[0].categories[0].key, label: e[0].categories[0].value,
children: Object.values(_join(_get("companies"), _join(_get("brands"), _get("models"))))[0] };
});
它有两个功能:
- _get()
- _join()
- 调用
_get()
时,您将要搜索的键作为参数传递。它可以是companies
、brands
、models
等。它 returns 一个数组,其中包含您作为参数传递的所有键值。例如
_get("companies")
returns
{
"companies": [
{
"key": "apple",
"value": "Apple"
},
{
"key": "ibm",
"value": "IBM"
},
{
"key": "lenovo",
"value": "Lenovo"
}
]
}
_join()
函数有神奇的作用。它需要一个父组和一个子组,并将它们转换为您想要的格式:
这是一个工作示例
const input = {
"notebook": [{
"categories": [{
"key": "notebook",
"value": "Notebook"
}],
"companies": [{
"key": "apple",
"value": "Apple"
}],
"brands": [{
"key": "macbook",
"value": "MacBook"
}],
"modelYears": [{
"key": "2021",
"value": "2021"
}],
"models": [{
"key": "2021 Pro",
"value": "2021 Pro"
}]
},
{
"categories": [{
"key": "notebook",
"value": "Notebook"
}],
"companies": [{
"key": "ibm",
"value": "IBM"
},
{
"key": "lenovo",
"value": "Lenovo"
}
],
"brands": [{
"key": "thinkpad",
"value": "Thinkpad"
}],
"modelYears": [{
"key": "2019",
"value": "2019"
}],
"models": [{
"key": "e590",
"value": "E590"
}]
},
{
"categories": [{
"key": "notebook",
"value": "Notebook"
}],
"companies": [{
"key": "ibm",
"value": "IBM"
},
{
"key": "lenovo",
"value": "Lenovo"
}
],
"brands": [{
"key": "thinkpad",
"value": "Thinkpad"
}],
"modelYears": [{
"key": "2019",
"value": "2019"
}],
"models": [{
"key": "p1",
"value": "P1"
}]
}
],
"smartphone": [{
"categories": [{
"key": "smartphone",
"value": "Smartphone"
}],
"companies": [{
"key": "apple",
"value": "Apple"
}],
"brands": [{
"key": "iphone",
"value": "IPHONE"
}],
"modelYears": [{
"key": "2019",
"value": "2019"
}],
"models": [{
"key": "11pro",
"value": "11 Pro"
}]
},
{
"categories": [{
"key": "smartphone",
"value": "Smartphone"
}],
"companies": [{
"key": "apple",
"value": "Apple"
}],
"brands": [{
"key": "iphone",
"value": "IPHONE"
}],
"modelYears": [{
"key": "2020",
"value": "2020"
}],
"models": [{
"key": "12",
"value": "12"
}]
}
]
}
const out = Object.values(input).map(e => {
const _get = key => ({ [key]: e.map(m => [...m[key]]).flat()
.filter((m, i, a) => !a.slice(i + 1).find(c => c.key === m.key)) });
const _join = (parent, children) => {
const [pkey, ckey] = [Object.keys(parent)[0], Object.keys(children)[0]];
return {
[pkey]: parent[pkey].map(p => ({ value: p.key, label: p.value,
children: e.reduce((o, m) =>
o.concat(m[pkey].find(pp => pp.key === p.key) ?
children[ckey].filter(child => m[ckey]
.find(mm => mm.key === (child.key || child.value)) &&
!o.find(c => (c.key || c.value) === (child.key || child.value))) : []), [])
})
)
};
};
return { value: e[0].categories[0].key, label: e[0].categories[0].value,
children: Object.values(_join(_get("companies"), _join(_get("brands"), _get("models"))))[0] };
});
console.log(out);
.as-console-wrapper { max-height: 100% !important; top: 0; }
请注意 Stack overflow 代码段可能会遗漏一些重复的对象。自己试试就更好了