使用 RxJS 展平 objects 的嵌套数组
flatten a nested array of objects using RxJS
我们的数据结构是一个嵌套数组 objects
interface dtModel {
id: number;
permission: number;
childs: dtModel[];
}
const data: dtModel[] = [
{
id: 1,
permission: 2,
childs: [
{
id: 2,
permission: 1,
childs: [
{
id: 3,
permission: 3,
childs: [],
},
],
},
],
},
{
id: 4,
permission: 1,
childs: [
{
id: 5,
permission: 2,
childs: [
{
id: 6,
permission: 3,
childs: [
{
id: 7,
permission: 1,
childs: [],
},
],
},
],
},
],
},
];
我尝试在 RxJs 的帮助下创建一个像这样的扁平数组,但我还没有成功
[
{id:1,permission:2},
{id:2,permission:1},
{id:3,permission:3},
{id:4,permission:1},
{id:5,permission:2},
{id:6,permission:3},
{id:7,permission:1}
]
我试图在 'reduce' 的帮助下做到这一点,但因为我孩子的 属性 并不总是具有相同的长度,所以我无法用这种方法做到这一点。
of(data)
.pipe(
map((items: dtModel[]) => {
return items.reduce((res, curr) => {
res.push({ id: curr.id, permission: curr.permission });
return res;
}, []);
})
)
.subscribe(console.log);
您需要进行树(=您的数据结构)遍历,并且对于每个节点,您都必须将 {id://some id,permission://some permission}
压入结果数组。
你不需要 rxjs。
需要递归
const source = [
{
id: 1,
permission: 2,
childs: [
{
id: 2,
permission: 1,
childs: [
{
id: 3,
permission: 3,
childs: [],
},
],
},
],
},
{
id: 4,
permission: 1,
childs: [
{
id: 5,
permission: 2,
childs: [
{
id: 6,
permission: 3,
childs: [
{
id: 7,
permission: 1,
childs: [],
},
],
},
],
},
],
},
];
const result = [];
const getAllItemsPerChildren = (item) => {
result.push({ id: item.id, permission: item.permission });
if (item.childs) {
return item.childs.map((i) => getAllItemsPerChildren(i));
}
};
source.forEach((i) => getAllItemsPerChildren(i));
console.log(result)
RXJS 解决方案
演示:https://stackblitz.com/edit/typescript-dlva1z?file=index.ts
const source$ = of({
data: [],
last: false,
check: source,
}).pipe(
expand((data) =>
data.last
? EMPTY
: of(data.check).pipe(
map((currentItem) => {
const childs = (currentItem as any).flatMap((i) => i?.childs || []);
return {
data: currentItem.map((i) => ({
id: i.id,
permissions: i.permission,
})),
last: !childs?.length,
check: childs,
};
})
)
),
reduce((acc, items) => [...acc, ...(items as any).data], [])
);
source$.subscribe((data) => console.log('result ', data));
解释:
- 使用
expand
运算符的主要思想 - 它允许我们创建递归。
- 要停止递归需要return
EMPTY
of({data: [],last: false, check: source})
- 这是我们的商店
data
被分配以保留已解析的项目 - 将在 reduce 运算符中使用 - 连接整个数据
- 一个 return 来自 expand 等于一个 emit
last
允许控制回溯,我们正在检查是否所有项目都已解析
check
- 要解析的元素,初始值设置为整个数据
一个简化的 expand
版本,只需要确保每个项目都被发出并在最后用 map
做一些清理
工作示例:https://codesandbox.io/s/rxjs-playground-forked-vr0l6s?file=/src/index.js
from(data)
.pipe(
expand((obj) => {
return obj.childs && obj.childs.length
? from(obj.childs)
: EMPTY;
}),
map(obj=>({ id: obj.id, permission: obj.permission })),
toArray()
)
.subscribe(console.log);
很好的文章解释了expand
用法https://ncjamieson.com/understanding-expand/
我们的数据结构是一个嵌套数组 objects
interface dtModel {
id: number;
permission: number;
childs: dtModel[];
}
const data: dtModel[] = [
{
id: 1,
permission: 2,
childs: [
{
id: 2,
permission: 1,
childs: [
{
id: 3,
permission: 3,
childs: [],
},
],
},
],
},
{
id: 4,
permission: 1,
childs: [
{
id: 5,
permission: 2,
childs: [
{
id: 6,
permission: 3,
childs: [
{
id: 7,
permission: 1,
childs: [],
},
],
},
],
},
],
},
];
我尝试在 RxJs 的帮助下创建一个像这样的扁平数组,但我还没有成功
[
{id:1,permission:2},
{id:2,permission:1},
{id:3,permission:3},
{id:4,permission:1},
{id:5,permission:2},
{id:6,permission:3},
{id:7,permission:1}
]
我试图在 'reduce' 的帮助下做到这一点,但因为我孩子的 属性 并不总是具有相同的长度,所以我无法用这种方法做到这一点。
of(data)
.pipe(
map((items: dtModel[]) => {
return items.reduce((res, curr) => {
res.push({ id: curr.id, permission: curr.permission });
return res;
}, []);
})
)
.subscribe(console.log);
您需要进行树(=您的数据结构)遍历,并且对于每个节点,您都必须将 {id://some id,permission://some permission}
压入结果数组。
你不需要 rxjs。
需要递归
const source = [
{
id: 1,
permission: 2,
childs: [
{
id: 2,
permission: 1,
childs: [
{
id: 3,
permission: 3,
childs: [],
},
],
},
],
},
{
id: 4,
permission: 1,
childs: [
{
id: 5,
permission: 2,
childs: [
{
id: 6,
permission: 3,
childs: [
{
id: 7,
permission: 1,
childs: [],
},
],
},
],
},
],
},
];
const result = [];
const getAllItemsPerChildren = (item) => {
result.push({ id: item.id, permission: item.permission });
if (item.childs) {
return item.childs.map((i) => getAllItemsPerChildren(i));
}
};
source.forEach((i) => getAllItemsPerChildren(i));
console.log(result)
RXJS 解决方案
演示:https://stackblitz.com/edit/typescript-dlva1z?file=index.ts
const source$ = of({
data: [],
last: false,
check: source,
}).pipe(
expand((data) =>
data.last
? EMPTY
: of(data.check).pipe(
map((currentItem) => {
const childs = (currentItem as any).flatMap((i) => i?.childs || []);
return {
data: currentItem.map((i) => ({
id: i.id,
permissions: i.permission,
})),
last: !childs?.length,
check: childs,
};
})
)
),
reduce((acc, items) => [...acc, ...(items as any).data], [])
);
source$.subscribe((data) => console.log('result ', data));
解释:
- 使用
expand
运算符的主要思想 - 它允许我们创建递归。 - 要停止递归需要return
EMPTY
of({data: [],last: false, check: source})
- 这是我们的商店data
被分配以保留已解析的项目 - 将在 reduce 运算符中使用 - 连接整个数据- 一个 return 来自 expand 等于一个 emit
last
允许控制回溯,我们正在检查是否所有项目都已解析check
- 要解析的元素,初始值设置为整个数据
一个简化的 expand
版本,只需要确保每个项目都被发出并在最后用 map
工作示例:https://codesandbox.io/s/rxjs-playground-forked-vr0l6s?file=/src/index.js
from(data)
.pipe(
expand((obj) => {
return obj.childs && obj.childs.length
? from(obj.childs)
: EMPTY;
}),
map(obj=>({ id: obj.id, permission: obj.permission })),
toArray()
)
.subscribe(console.log);
很好的文章解释了expand
用法https://ncjamieson.com/understanding-expand/