如何将我的 ltree 点符号层次结构改进为嵌套 JSON 脚本?
How to improve my ltree dot notation hierarchy to nested JSON script?
我从数据库中收到一组行,其中 parentPath
列格式化为表示层次结构的 ltree 字符串(点表示法)。我想 return 来自我的 nodejs 服务器的具有嵌套类别的相同数据。
我拥有的数据:
const categories = [
{
id: 1,
name: "ALL",
parentPath: "categories",
translationFr: "Toutes les catégories"
},
{
id: 2,
name: "LEISURE",
parentPath: "categories.leisure",
translationFr: "Loisirs"
},
{
id: 35,
name: "AUTO",
parentPath: "categories.services.auto",
translationFr: "Auto-écoles"
},
{
id: 3,
name: "MUSEUMS",
parentPath: "categories.leisure.museums",
translationFr: "Musées"
},
{
id: 4,
name: "MONUMENTS",
parentPath: "categories.leisure.monuments",
translationFr: "Monuments et patrimoine"
},
{
id: 5,
name: "OPERAS",
parentPath: "categories.leisure.operas",
translationFr: "Opéras"
},
{
id: 6,
name: "THEATER",
parentPath: "categories.leisure.theater",
translationFr: "Théâtres"
},
{
id: 7,
name: "CINEMAS",
parentPath: "categories.leisure.cinemas",
translationFr: "Cinémas"
},
{
id: 8,
name: "CONCERT",
parentPath: "categories.leisure.concert",
translationFr: "Salles de concert"
},
{
id: 9,
name: "FITNESS",
parentPath: "categories.leisure.fitness",
translationFr: "Salles de fitness"
},
{
id: 10,
name: "CLUBS",
parentPath: "categories.leisure.clubs",
translationFr: "Clubs de sport"
},
{
id: 11,
name: "SENSATIONAL",
parentPath: "categories.leisure.sensational",
translationFr: "Sensationnel"
},
{
id: 12,
name: "HEALTH",
parentPath: "categories.health",
translationFr: "Bien-être et beauté"
},
{
id: 13,
name: "HAIR",
parentPath: "categories.health.hair",
translationFr: "Salons de coiffure"
},
{
id: 14,
name: "CARE",
parentPath: "categories.health.care",
translationFr: "Soins"
},
{
id: 15,
name: "SPAS",
parentPath: "categories.health.spas",
translationFr: "Spas"
},
{
id: 16,
name: "MASSAGE",
parentPath: "categories.health.massage",
translationFr: "Massage"
},
{
id: 17,
name: "FOOD",
parentPath: "categories.food",
translationFr: "Restaurants et Bars"
},
{
id: 18,
name: "NIGHTCLUB",
parentPath: "categories.food.nightclub",
translationFr: "Boîtes de nuit"
},
{
id: 19,
name: "RESTAURANTS",
parentPath: "categories.food.restaurants",
translationFr: "Restaurants"
},
{
id: 20,
name: "BARS",
parentPath: "categories.food.bars",
translationFr: "Bars"
},
{
id: 21,
name: "FASTFOOD",
parentPath: "categories.food.fastfood",
translationFr: "Sur le pouce"
}
]
我想要什么:
[
{
name: "LEISURE",
translationFr: "Loisirs",
children: [
{ name: "MUSEUMS", translationFr: "Musées" },
...
]
},
{
name: "HEALTH",
translationFr: "Santé et bien-être",
children: [...]
},
...
]
我用下面的代码成功得到了我想要的东西:
const result = categories
.filter(c => c.name != "ALL") // remove the root of the tree from array
.reduce((r, s) => {
// Select everything before the last "." AKA parent category
const parentCategory = s.parentPath.substring(
0,
s.parentPath.lastIndexOf(".")
);
let object = r.find(o => o.parentPath === parentCategory);
if (!object) {
r.push({ ...s, children: [] });
} else {
object.children.push(s);
}
return r;
}, []);
但是这个脚本有几个弱点:
- 它不适用于 4 层或更多层的嵌套
- 如果按此顺序呈现行将不起作用(3 级嵌套在 2 级嵌套之前):
categories.leisure.sensational
categories.leisure
categories.leisure.museums
这里有没有天才知道我们该如何继续?
你可以这样累积:
项目的深度或顺序无关紧要,但如果缺少部分路径 (如 categories.services
) 该子树将不会包含在输出中.
var children = function (key) {
return this[key] || (this[key] = [])
}.bind({});
categories.forEach((item) => {
children(item.parentPath.replace(/(^|\.)\w+$/g, "")).push({
...item,
children: children(item.parentPath)
});
});
console.log(children(""));
或内联 (因为您不能重复使用 children() 函数):
var root = categories.reduce((children, item) => {
children(item.parentPath.replace(/(^|\.)\w+$/g, "")).push({
...item,
children: children(item.parentPath)
});
return children;
}, function (key) {
return this[key] || (this[key] = [])
}.bind({}))("");
console.log(root);
const categories = [
{
id: 1,
name: "ALL",
parentPath: "categories",
translationFr: "Toutes les catégories"
},
{
id: 2,
name: "LEISURE",
parentPath: "categories.leisure",
translationFr: "Loisirs"
},
{
id: 35,
name: "AUTO",
parentPath: "categories.services.auto",
translationFr: "Auto-écoles"
},
{
id: 3,
name: "MUSEUMS",
parentPath: "categories.leisure.museums",
translationFr: "Musées"
},
{
id: 4,
name: "MONUMENTS",
parentPath: "categories.leisure.monuments",
translationFr: "Monuments et patrimoine"
},
{
id: 5,
name: "OPERAS",
parentPath: "categories.leisure.operas",
translationFr: "Opéras"
},
{
id: 6,
name: "THEATER",
parentPath: "categories.leisure.theater",
translationFr: "Théâtres"
},
{
id: 7,
name: "CINEMAS",
parentPath: "categories.leisure.cinemas",
translationFr: "Cinémas"
},
{
id: 8,
name: "CONCERT",
parentPath: "categories.leisure.concert",
translationFr: "Salles de concert"
},
{
id: 9,
name: "FITNESS",
parentPath: "categories.leisure.fitness",
translationFr: "Salles de fitness"
},
{
id: 10,
name: "CLUBS",
parentPath: "categories.leisure.clubs",
translationFr: "Clubs de sport"
},
{
id: 11,
name: "SENSATIONAL",
parentPath: "categories.leisure.sensational",
translationFr: "Sensationnel"
},
{
id: 12,
name: "HEALTH",
parentPath: "categories.health",
translationFr: "Bien-être et beauté"
},
{
id: 13,
name: "HAIR",
parentPath: "categories.health.hair",
translationFr: "Salons de coiffure"
},
{
id: 14,
name: "CARE",
parentPath: "categories.health.care",
translationFr: "Soins"
},
{
id: 15,
name: "SPAS",
parentPath: "categories.health.spas",
translationFr: "Spas"
},
{
id: 16,
name: "MASSAGE",
parentPath: "categories.health.massage",
translationFr: "Massage"
},
{
id: 17,
name: "FOOD",
parentPath: "categories.food",
translationFr: "Restaurants et Bars"
},
{
id: 18,
name: "NIGHTCLUB",
parentPath: "categories.food.nightclub",
translationFr: "Boîtes de nuit"
},
{
id: 19,
name: "RESTAURANTS",
parentPath: "categories.food.restaurants",
translationFr: "Restaurants"
},
{
id: 20,
name: "BARS",
parentPath: "categories.food.bars",
translationFr: "Bars"
},
{
id: 21,
name: "FASTFOOD",
parentPath: "categories.food.fastfood",
translationFr: "Sur le pouce"
}
];
var root = categories.reduce((children, item) => {
children(item.parentPath.replace(/(^|\.)\w+$/g, "")).push({
...item,
children: children(item.parentPath)
});
return children;
}, function(key){
return this[key] || (this[key]=[])
}.bind({}) )("");
console.log(root);
我从数据库中收到一组行,其中 parentPath
列格式化为表示层次结构的 ltree 字符串(点表示法)。我想 return 来自我的 nodejs 服务器的具有嵌套类别的相同数据。
我拥有的数据:
const categories = [
{
id: 1,
name: "ALL",
parentPath: "categories",
translationFr: "Toutes les catégories"
},
{
id: 2,
name: "LEISURE",
parentPath: "categories.leisure",
translationFr: "Loisirs"
},
{
id: 35,
name: "AUTO",
parentPath: "categories.services.auto",
translationFr: "Auto-écoles"
},
{
id: 3,
name: "MUSEUMS",
parentPath: "categories.leisure.museums",
translationFr: "Musées"
},
{
id: 4,
name: "MONUMENTS",
parentPath: "categories.leisure.monuments",
translationFr: "Monuments et patrimoine"
},
{
id: 5,
name: "OPERAS",
parentPath: "categories.leisure.operas",
translationFr: "Opéras"
},
{
id: 6,
name: "THEATER",
parentPath: "categories.leisure.theater",
translationFr: "Théâtres"
},
{
id: 7,
name: "CINEMAS",
parentPath: "categories.leisure.cinemas",
translationFr: "Cinémas"
},
{
id: 8,
name: "CONCERT",
parentPath: "categories.leisure.concert",
translationFr: "Salles de concert"
},
{
id: 9,
name: "FITNESS",
parentPath: "categories.leisure.fitness",
translationFr: "Salles de fitness"
},
{
id: 10,
name: "CLUBS",
parentPath: "categories.leisure.clubs",
translationFr: "Clubs de sport"
},
{
id: 11,
name: "SENSATIONAL",
parentPath: "categories.leisure.sensational",
translationFr: "Sensationnel"
},
{
id: 12,
name: "HEALTH",
parentPath: "categories.health",
translationFr: "Bien-être et beauté"
},
{
id: 13,
name: "HAIR",
parentPath: "categories.health.hair",
translationFr: "Salons de coiffure"
},
{
id: 14,
name: "CARE",
parentPath: "categories.health.care",
translationFr: "Soins"
},
{
id: 15,
name: "SPAS",
parentPath: "categories.health.spas",
translationFr: "Spas"
},
{
id: 16,
name: "MASSAGE",
parentPath: "categories.health.massage",
translationFr: "Massage"
},
{
id: 17,
name: "FOOD",
parentPath: "categories.food",
translationFr: "Restaurants et Bars"
},
{
id: 18,
name: "NIGHTCLUB",
parentPath: "categories.food.nightclub",
translationFr: "Boîtes de nuit"
},
{
id: 19,
name: "RESTAURANTS",
parentPath: "categories.food.restaurants",
translationFr: "Restaurants"
},
{
id: 20,
name: "BARS",
parentPath: "categories.food.bars",
translationFr: "Bars"
},
{
id: 21,
name: "FASTFOOD",
parentPath: "categories.food.fastfood",
translationFr: "Sur le pouce"
}
]
我想要什么:
[
{
name: "LEISURE",
translationFr: "Loisirs",
children: [
{ name: "MUSEUMS", translationFr: "Musées" },
...
]
},
{
name: "HEALTH",
translationFr: "Santé et bien-être",
children: [...]
},
...
]
我用下面的代码成功得到了我想要的东西:
const result = categories
.filter(c => c.name != "ALL") // remove the root of the tree from array
.reduce((r, s) => {
// Select everything before the last "." AKA parent category
const parentCategory = s.parentPath.substring(
0,
s.parentPath.lastIndexOf(".")
);
let object = r.find(o => o.parentPath === parentCategory);
if (!object) {
r.push({ ...s, children: [] });
} else {
object.children.push(s);
}
return r;
}, []);
但是这个脚本有几个弱点:
- 它不适用于 4 层或更多层的嵌套
- 如果按此顺序呈现行将不起作用(3 级嵌套在 2 级嵌套之前):
categories.leisure.sensational
categories.leisure
categories.leisure.museums
这里有没有天才知道我们该如何继续?
你可以这样累积:
项目的深度或顺序无关紧要,但如果缺少部分路径 (如 categories.services
) 该子树将不会包含在输出中.
var children = function (key) {
return this[key] || (this[key] = [])
}.bind({});
categories.forEach((item) => {
children(item.parentPath.replace(/(^|\.)\w+$/g, "")).push({
...item,
children: children(item.parentPath)
});
});
console.log(children(""));
或内联 (因为您不能重复使用 children() 函数):
var root = categories.reduce((children, item) => {
children(item.parentPath.replace(/(^|\.)\w+$/g, "")).push({
...item,
children: children(item.parentPath)
});
return children;
}, function (key) {
return this[key] || (this[key] = [])
}.bind({}))("");
console.log(root);
const categories = [
{
id: 1,
name: "ALL",
parentPath: "categories",
translationFr: "Toutes les catégories"
},
{
id: 2,
name: "LEISURE",
parentPath: "categories.leisure",
translationFr: "Loisirs"
},
{
id: 35,
name: "AUTO",
parentPath: "categories.services.auto",
translationFr: "Auto-écoles"
},
{
id: 3,
name: "MUSEUMS",
parentPath: "categories.leisure.museums",
translationFr: "Musées"
},
{
id: 4,
name: "MONUMENTS",
parentPath: "categories.leisure.monuments",
translationFr: "Monuments et patrimoine"
},
{
id: 5,
name: "OPERAS",
parentPath: "categories.leisure.operas",
translationFr: "Opéras"
},
{
id: 6,
name: "THEATER",
parentPath: "categories.leisure.theater",
translationFr: "Théâtres"
},
{
id: 7,
name: "CINEMAS",
parentPath: "categories.leisure.cinemas",
translationFr: "Cinémas"
},
{
id: 8,
name: "CONCERT",
parentPath: "categories.leisure.concert",
translationFr: "Salles de concert"
},
{
id: 9,
name: "FITNESS",
parentPath: "categories.leisure.fitness",
translationFr: "Salles de fitness"
},
{
id: 10,
name: "CLUBS",
parentPath: "categories.leisure.clubs",
translationFr: "Clubs de sport"
},
{
id: 11,
name: "SENSATIONAL",
parentPath: "categories.leisure.sensational",
translationFr: "Sensationnel"
},
{
id: 12,
name: "HEALTH",
parentPath: "categories.health",
translationFr: "Bien-être et beauté"
},
{
id: 13,
name: "HAIR",
parentPath: "categories.health.hair",
translationFr: "Salons de coiffure"
},
{
id: 14,
name: "CARE",
parentPath: "categories.health.care",
translationFr: "Soins"
},
{
id: 15,
name: "SPAS",
parentPath: "categories.health.spas",
translationFr: "Spas"
},
{
id: 16,
name: "MASSAGE",
parentPath: "categories.health.massage",
translationFr: "Massage"
},
{
id: 17,
name: "FOOD",
parentPath: "categories.food",
translationFr: "Restaurants et Bars"
},
{
id: 18,
name: "NIGHTCLUB",
parentPath: "categories.food.nightclub",
translationFr: "Boîtes de nuit"
},
{
id: 19,
name: "RESTAURANTS",
parentPath: "categories.food.restaurants",
translationFr: "Restaurants"
},
{
id: 20,
name: "BARS",
parentPath: "categories.food.bars",
translationFr: "Bars"
},
{
id: 21,
name: "FASTFOOD",
parentPath: "categories.food.fastfood",
translationFr: "Sur le pouce"
}
];
var root = categories.reduce((children, item) => {
children(item.parentPath.replace(/(^|\.)\w+$/g, "")).push({
...item,
children: children(item.parentPath)
});
return children;
}, function(key){
return this[key] || (this[key]=[])
}.bind({}) )("");
console.log(root);