我正在尝试将Google product taxonomy 加载到 Firestore 文档中,我认为这主要意味着将其转换为 JSON。
我建议第一次创建嵌套对象。
(您始终可以重新处理它们以获得最终所需的结构。)
{
"Animals & Pet Supplies": {
"id": "1",
"Live Animals": {
"id": "3237"
},
"Pet Supplies": {
"id": "2",
"Bird Supplies": {
"id": "3",
"Bird Cage Accessories": {
"id": "7385"
}
}
}
}
}
为什么?在我看来,这些行可以很容易地转换为嵌套对象,然后您可以合并在一起。
以下几行:
[ "1 - Animals & Pet Supplies",
"3237 - Animals & Pet Supplies > Live Animals" ]
可以转化为:
[ {"Animals & Pet Supplies": {"id": 1}}
{"Animals & Pet Supplies": {"Live Animals": {"id": 3237}}}]
然后合并到:
{
"Animals & Pet Supplies": {
"id": 1,
"Live Animals": {
"id": 3237
}
}
}
如何?
首先让我们创建两个函数来获取 id 和每个类别
- 要获取 id,我们必须拆分“-”字符。
- 要获得必须在“>”字符上拆分的类别。
- 在这两种情况下,我们都希望修剪结果。
让我们首先创建一个通用的curried函数:
const splitBy = sep => str =>
str.split(sep).map(x => x.trim());
因为它是柯里化的,我们可以在它之上构建两个专门的函数:
const splitLine = splitBy('-');
const splitCategories = splitBy('>');
splitLine('1 - Animals & Pet Supplies');
//=> [ '1', 'Animals & Pet Supplies' ]
splitCategories('Animals & Pet Supplies > Live Animals');
//=> [ 'Animals & Pet Supplies', 'Live Animals' ]
然后让我们将每一行转换成一个数据结构,让我们可以创建嵌套对象:
以下几行:
[ "1 - Animals & Pet Supplies",
"3237 - Animals & Pet Supplies > Live Animals" ]
可以转换成对,其中每对代表一个对象,一对可以包含在另一个对象中:
[ ["Animals & Pet Supplies", 1]
["Animals & Pet Supplies", ["Live Animals", 3237]]]
此函数将在将平面数组转换为对象之前将其转换为嵌套对:
const nest = xs =>
xs.length === 2
? typeof xs[1] === 'string'
? {[xs[0]]: {id: xs[1]}}
: {[xs[0]]: nest(xs[1])}
: nest([xs[0], xs.slice(1)]);
nest(["Animals & Pet Supplies", "Live Animals", 3237]);
// (internally) => ["Animals & Pet Supplies", ["Live Animals", 3237]]
// (final output) => {"Animals & Pet Supplies": {"Live Animals": {"id": 3237}}}
要合并这个对象数组,我将使用deepmerge。 (但您可以使用其他任何东西,只要它允许 deep 合并而不是 shallow 合并,就像您使用扩展 ... 运算符或 Object.assign 获得的那样)
deepmerge.all(
[ {"Animals & Pet Supplies": {"id": 1}}
{"Animals & Pet Supplies": {"Live Animals": {"id": 3237}}}]);
//=> {
//=> "Animals & Pet Supplies": {
//=> "id": "1",
//=> "Live Animals": {
//=> "id": "3237"
//=> }
//=> }
//=> }
这是一个函数,它将你的行作为一个数组并返回一个嵌套类别的对象:
const load = lines =>
// put all lines into a "container"
// we want to process all lines all the time as opposed to each line individually
[lines]
// separate id and categories
// e.g ['3237', 'Animals & Pet Supplies > Live Animals']
.map(lines => lines.map(splitLine))
// split categories and put id last
// e.g. ['Animals & Pet Supplies', 'Live Animals', 3237]
.map(lines => lines.map(([id, cats]) => splitCategories(cats).concat(id)))
// created nested objects
// e.g. {"Animals & Pet Supplies": {"Live Animals": {"id": 3237}}}
.map(lines => lines.map(nest))
// merge all objects into one
.map(lines => deepmerge.all(lines))
// pop the result out of the container
.pop();
load(
[ "1 - Animals & Pet Supplies",
"3237 - Animals & Pet Supplies > Live Animals" ]);
//=> {
//=> "Animals & Pet Supplies": {
//=> "id": "1",
//=> "Live Animals": {
//=> "id": "3237"
//=> }
//=> }
//=> }
总而言之:
const splitBy = sep => str =>
str.split(sep).map(x => x.trim());
const splitLine = splitBy('-');
const splitCategories = splitBy('>');
const nest = xs =>
xs.length === 2
? typeof xs[1] === 'string'
? {[xs[0]]: {id: xs[1]}}
: {[xs[0]]: nest(xs[1])}
: nest([xs[0], xs.slice(1)]);
const load = lines =>
// put all lines into a "container"
// we want to process all lines all the time as opposed to each line individually
[lines]
// separate id and categories
// e.g ['3237', 'Animals & Pet Supplies > Live Animals']
.map(lines => lines.map(splitLine))
// split categories and put id last
// e.g. ['Animals & Pet Supplies', 'Live Animals', 3237]
.map(lines => lines.map(([id, cats]) => splitCategories(cats).concat(id)))
// created nested objects
// e.g. {"Animals & Pet Supplies": {"Live Animals": {"id": 3237}}}
.map(lines => lines.map(nest))
// merge all objects into one
.map(lines => deepmerge.all(lines))
// pop the result out of the container
.pop();
console.log(
JSON.stringify(
load(file_content),
null,
2
)
)
<script src="https://unpkg.com/deepmerge@3.0.0/dist/umd.js"></script>
<script>
const file_content = [
'1 - Animals & Pet Supplies',
'3237 - Animals & Pet Supplies > Live Animals',
'2 - Animals & Pet Supplies > Pet Supplies',
'3 - Animals & Pet Supplies > Pet Supplies > Bird Supplies',
'7385 - Animals & Pet Supplies > Pet Supplies > Bird Supplies > Bird Cage Accessories',
];
</script>
附录:访问我们的数据
现在我们已经将数据加载到这个结构中,看起来遍历它会很尴尬。
const data = {
"Animals & Pet Supplies": {
"id": "1",
"Live Animals": {
"id": "3237"
},
"Pet Supplies": {
"id": "2",
"Bird Supplies": {
"id": "3",
"Bird Cage Accessories": {
"id": "7385"
}
}
}
}
}
这很可能不是我们能想到的最好的数据结构,如果我们需要,还可以选择重新处理它。
不过,感谢Iterator protocol,我们现在可以塑造我们的数据,而无需(过多地)考虑如何访问它。
根据 Iterator 协议使我们的数据“可迭代”很容易,并且允许我们使用 JavaScript 构造,例如 ... 扩展运算符或 for...of 循环:
const iterate = o => (
{ ...o
, [Symbol.iterator]() {
const entries = Object.entries(o).filter(([k, v]) => k !== 'id');
return {
next() {
if (entries.length === 0) return {done: true};
const [name, {id}] = entries.pop();
return {done: false, value: {id, name}};
}
};
}
}
);
在这个实现中,我们将在每次迭代时返回一个对象{id, name}。
让我们进入第一层:
for (let obj of iterate(data)) {
console.log(obj)
}
//=> { id: '1', name: 'Animals & Pet Supplies' }
让我们进入第二层:
for (let obj of iterate(data['Animals & Pet Supplies'])) {
console.log(obj)
}
// { id: '2', name: 'Pet Supplies' }
// { id: '3237', name: 'Live Animals' }
或者我们可以使用...扩展运算符直接存储到数组中:
const level2 = [...iterate(data['Animals & Pet Supplies'])];
// [ { id: '2', name: 'Pet Supplies' }
// { id: '3237', name: 'Live Animals' } ]