

使用 normalizr 进行复杂数据转换
source link: https://www.fly63.com/article/detial/11931
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

扫一扫分享
笔者曾经开发过一个数据分享类的小程序,分享逻辑上类似于百度网盘。当前数据可以由被分享者加工然后继续分享(可以控制数据的过期时间、是否可以加工数据以及继续分享)。
分享的数据是一个深度嵌套的 json 对象。在用户读取分享数据时存入小程序云数据库中(分享的数据和业务数据有差异,没使用业务服务器进行维护)。如果拿到数据就直接存储的话,很快云数据库就会变得很大,其次我们也没办法分析各项和检索各项子数据给予分享者。
这时候需要进行数据转换以便拆分和维护。我们可以使用 redux 作者 Dan Abramov 编写的 normalizr 来处理数据。
normalizr 创立的初衷是处理深层,复杂的嵌套的对象。
稍微修改一下官方的例子,假定获取到如下书籍的数据:
{
id: "1",
title: "JavaScript 从入门到放弃",
// 作者
author: {
id: "1",
name: "chc"
},
// 评论
comments: [
{
id: "1",
content: "作者写的太好了",
commenter: {
id: "1",
name: "chc"
}
},
{
id: "2",
content: "楼上造假数据哈",
commenter: {
id: "2",
name: "dcd"
}
},
]
}
这时候我们可以写出 3 个主体: 书籍信息、评论以及用户。我们先从基础的数据来构造模式:
import { normalize, schema } from 'normalizr';
// 构造第一个实体 用户信息
const user = new schema.Entity('users');
// 构造第二个实体 评论
const comment = new schema.Entity('comments', {
// 评价者是用户
commenter: user
});
// 构造第三个实体 书籍
const book = new schema.Entity('books', {
// 作者
author: user,
// 评论
comments: [comment]
});
// 传入数据以及当前最大的 schema 信息
const normalizedData = normalize(originalData, book);
先来看一下最终数据。
{
"entities": {
"users": {
"1": {
"id": "1",
"name": "chc"
},
"2": {
"id": "2",
"name": "dcd"
}
},
"comments": {
"1": {
"id": "1",
"content": "作者写的太好了",
"commenter": "1"
},
"2": {
"id": "2",
"content": "楼上造假数据哈",
"commenter": "2"
}
},
"books": {
"1": {
"id": "1",
"title": "JavaScript 从入门到放弃",
"author": "1",
"comments": [
"1",
"2"
]
}
}
},
"result": "1"
}
去除其他信息,我们可以看到获取了 3 个不同的实体对象, users,comments,books。对象的键为当前 id,值为当前平铺的数据结构。这时候我们就可以使用对象或者数组(Object.values) 来新增和更新数据。
看到这里,大家可能是很懵的。先不管代码实现,这里先分析一下库是如何解析我们编写的 schema 的,以便大家可以在实际场景中使用,再看一遍数据和 schema 定义:
{
id: "1",
title: "JavaScript 从入门到放弃",
// 作者
author: {
id: "1",
name: "chc"
},
// 评论
comments: [
{
id: "1",
content: "作者写的太好了",
commenter: {
id: "1",
name: "chc"
}
},
{
id: "2",
content: "楼上造假数据哈",
commenter: {
id: "2",
name: "dcd"
}
},
]
}
书籍信息是第一层对象,数据中有 id, title, author, comments,对应 schema 如下
const book = new schema.Entity('books', {
// 作者
author: user,
// 一本书对应多个评论,所以这里使用数组
comments: [comment]
});
其中 id ,title 是 book 本身的属性,无需关注,把需要解析的数据结构写出来。books 字符串与解析无关,对应 entities 对象的 key。
再看 user
const user = new schema.Entity('users');
user 没有需要解析的信息,直接定义实体即可。
最后是评论信息
const comment = new schema.Entity('comments', {
// 评价者是用户
commenter: user
});
{
id: "1",
content: "作者写的太好了",
commenter: {
id: "1",
name: "chc"
}
}
把 comments 从原本的数据结构中拿出来,实际也就很清晰了。
normalizr 可以解析单个对象,那么如果当前业务传递数组呢?类似于 comment 直接这样使用即可:
[
{
id: '1',
title: "JavaScript 从入门到放弃"
// ...
},
{
id: '2',
// ...
}
]
const normalizedData = normalize(originalData, [book]);
我们只需要拿到刚才的 normalizedData 中的 result 以及 entities 就可以获取之前的信息了。
import { denormalize, schema } from 'normalizr';
//...
denormalize(normalizedData.result, book, normalizedData.entities);
Entity 配置
开发中可以根据配置信息重新解析实体数据。
const book = new schema.Entity('books', {
// 作者
author: user,
// 一本书对应多个评论,所以这里使用数组
comments: [comment]
}, {
// 默认主键为 id,否则使用 idAttribute 中的数据,如 cid,key 等
idAttribute: 'id',
// 预处理策略, 参数分别为 实体的输入值, 父对象
processStrategy: (value, parent, key) => value,
// 遇到两个id 相同数据的合并策略,默认如下所示,我们还可以继续修改
mergeStrategy: (prev, prev) => ({
...prev,
...next,
// 是否合并过,如果遇到相同的,就会添加该属性
isMerge: true
}),
});
// 看一下比较复杂的例子,以 user 为例子
const user = new schema.Entity('users', {
}, {
processStrategy: (value, parent, key) => {
// 增加父对象的属性
// 例如 commenter: "1" => commenterId: "1" 或者 author: "2" => "authorId": "2"
// 但是目前还无法通过 delete 删除 commenter 或者 author 属性
parent[`${key}Id`] = value.id
// 如果是从评论中获取的用户信息就增加 commentIds 属性
if (key === 'commenter') {
return {
...value,
commentIds: [parent.id]
}
}
// 不要忘记返回 value, 否则不会生成 user 数据
return {
...value,
bookIds: [parent.id]
};
}
mergeStrategy: (prev, prev) => ({
...prev,
...next,
// 该用户所有的评论归并到一起去
commentIds: [...prev.commentIds, ...next.commentIds],
// 该用户所有的书本归并到一起去
bookIds: [...prev.bookIds, ...next.bookIds],
isMerge: true
}),
})
// 最终获取的用户信息为
{
"1": {
"id": "1",
"name": "chc"
// 用户 chc 写了评论和书籍,但是没有进行过合并
"commentIds": ["1"],
"bookIds": ["1"],
},
"2": {
"id": "2",
"name": "dcd",
// 用户 dcd 写了 2 个评论,同时进行了合并处理
"commentIds": [
"2",
"3"
],
"isMerge": true
}
}
当然了,该库也可以进行更加复杂的数据格式化,大家可以通过 api 文档 来进一步学习和使用。
当然了,normalizr 使用场景毕竟有限,开源负责人也早已换人。目前主库已经无人维护了(issue 也也已经关闭)。当然了,normalizr 代码本身也是足够稳定。
笔者也在考虑一些新的场景使用并尝试为 normalizr 添加一些新的功能(如 id 转换)和优化(ts 重构),如果您在使用 normalizr 的过程中遇到什么问题,也可以联系我,存储库目前在 normalizr-helper 中。
如果你觉得这篇文章不错,希望可以给与我一些鼓励,在我的 github 博客下帮忙 star 一下。博客地址
来自:https://segmentfault.com/a/1190000042216483
链接: https://www.fly63.com/article/detial/11931
</div
Recommend
-
25
README.md normalizr
-
15
近日在公司遇到一个需求,因为准备要推出海外版产品,所以需要将所有的简体文字转换为繁体文字。一开始是改了表面的文字,但是后面发现很多提示语也需要去改,所以找了一个工具去对所有 .m 文件进行批量文字转换。 OpenCC OpenCC 正好符合我的...
-
12
扫码领取奖励更多详情链小象(CFOR)未来可兑换比特币、以太坊、瑞波、EOS等区块链资产;链向财经合作区块链项目资产;链向财经应用内的增值产品和服务、链向财...
-
20
使用 Java 转换 Apache Avro 为 Parquet 数据格式 2021-02-23 — Yanbin Avro 和 Parquet 是处理数据时常用的两种编码格式,它们同为 Hadoop 大家庭中的成员。这两种格式都是自我描述的,即在数据文件中带有 Schema。Avro 广泛的应用于数...
-
10
使用 Java 转换 Apache Avro 为 Parquet 数据格式(依赖更新) 2021-02-25 — Yanbin 在上篇 使用 Java 转换 Apache Avro 为 Parquet 数据格式
-
6
iOS中如何对具有复杂依赖的SDK在真机上进行单元测试 17 Feb 2016 • 7 min. read • 0 Comments
-
32
怎么用Lambda求和并且进行类型转换 ...
-
6
CameraX 是一个旨在帮助开发者简化相机应用开发工作的 Jetpack 支持库。它...
-
9
使用js进行string和json之间转换的方法 | Lenix Blog 一、string类型转换成Json对象方法 1、Javascript支持的转换方式:eval
-
13
很多复杂的矩阵计算可以使用 einsum 来表示,方便 PoC,性能也还过得去。 Einstein notation 你没有看错,Einstein notation 是那位著名的...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK