使用 Ramda compose、groupBy 和 sort 来处理对象数组
Using Ramda compose, groupBy and sort in order to process an array of objects
我是 Ramda 的新手,我正在努力实现以下目标:
我有一组对象(即消息)。
我想按对方 ID(发件人或收件人 ID,以非 1 为准,请参阅下面的 groupBy
lambda)对消息进行分组。
我要获取一个对象,其键是对方 ID,值是与对方交换的消息数组。
然后我想按日期降序排列这些消息数组。
最后保留最新的消息,从而得到一个数组,其中包含与每个对方交换的最新消息。
因为我上面有两个对方,所以我应该有两个消息的数组。
这是我尝试过的:
const rawMessages = [
{
"sender": {
"id": 1,
"firstName": "JuliettP"
},
"recipient": {
"id": 2,
"firstName": "Julien"
},
"sendDate": "2017-01-28T19:21:15.863",
"messageRead": true,
"text": "ssssssss"
},
{
"sender": {
"id": 3,
"firstName": "Juliani"
},
"recipient": {
"id": 1,
"firstName": "JuliettP"
},
"sendDate": "2017-02-01T18:08:12.894",
"messageRead": true,
"text": "sss"
},
{
"sender": {
"id": 2,
"firstName": "Julien"
},
"recipient": {
"id": 1,
"firstName": "JuliettP"
},
"sendDate": "2017-02-07T22:19:51.649",
"messageRead": true,
"text": "I love redux!!"
},
{
"sender": {
"id": 1,
"firstName": "JuliettP"
},
"recipient": {
"id": 3,
"firstName": "Juliani"
},
"sendDate": "2017-03-13T20:57:52.253",
"messageRead": false,
"text": "hello Juliani"
},
{
"sender": {
"id": 1,
"firstName": "JuliettP"
},
"recipient": {
"id": 3,
"firstName": "Juliani"
},
"sendDate": "2017-03-13T20:56:52.253",
"messageRead": false,
"text": "hello Julianito"
}
];
const currentUserId = 1;
const groupBy = (m: Message) => m.sender.id !== currentUserId ? m.sender.id : m.recipient.id;
const byDate = R.descend(R.prop('sendDate'));
const sort = (value, key) => R.sort(byDate, value);
const composition = R.compose(R.map, R.head, sort, R.groupBy(groupBy));
const latestByCounterParty = composition(rawMessages);
console.log(latestByCounterParty);
对应的代码笔如下:
https://codepen.io/balteo/pen/JWOWRb
有人可以帮忙吗?
编辑:这里是 link 到非柯里化版本:here。没有柯里化,行为是相同的。关于柯里化的必要性,请参阅下面我的评论。
你的例子接近你想要的,尽管你只需要将 head
和 sort
的组合移动到给定 map
的函数参数,然后调用 values
在最终结果上将对象转换为值数组。
const currentMessagesForId = R.curry((id, msgs) =>
R.compose(
R.values,
R.map(R.compose(R.head, R.sort(R.descend(R.prop('sendDate'))))),
R.groupBy(m => m.sender.id !== id ? m.sender.id : m.recipient.id)
)(msgs))
currentMessagesForId(currentUserId, rawMessages)
虽然我认为 没问题,但我自己还可以采取另外两个步骤。
注意到关于地图的 important rules 之一是
map(compose(f, g)) ≍ compose(map(f), map(g))
当我们已经在组合管道中时,我们可以选择取消嵌套这一步:
R.map(R.compose(R.head, R.sort(R.descend(R.prop('sendDate'))))),
并把整体方案变成
const currentMessagesForId = R.curry((id, msgs) =>
R.compose(
R.values,
R.map(R.head),
R.map(R.sort(R.descend(R.prop('sendDate')))),
R.groupBy(m => m.sender.id !== id ? m.sender.id : m.recipient.id)
)(msgs)
)
这样做当然是个人喜好问题。但我觉得它更干净。下一步也是一个品味问题。对于可以列在一行中的内容,我选择使用 compose
,因此在格式 compose(f, g, h)(x)
和 f(g(h(x)))
之间建立了一些明显的联系。如果它跨越多行,我更喜欢使用 pipe
,它的行为方式相同,但从头到尾运行它的函数。所以我会进一步改变它看起来像这样:
const currentMessagesForId = R.curry((id, msgs) =>
R.pipe(
R.groupBy(m => m.sender.id !== id ? m.sender.id : m.recipient.id),
R.map(R.sort(R.descend(R.prop('sendDate')))),
R.map(R.head),
R.values
)(msgs)
)
我发现这种自上而下的阅读比较长 compose
版本所需的自下而上的阅读更容易。
但是,正如我所说,这些都是品味问题。
您可以在 Ramda REPL.
上查看这些示例
我是 Ramda 的新手,我正在努力实现以下目标:
我有一组对象(即消息)。
我想按对方 ID(发件人或收件人 ID,以非 1 为准,请参阅下面的
groupBy
lambda)对消息进行分组。我要获取一个对象,其键是对方 ID,值是与对方交换的消息数组。
然后我想按日期降序排列这些消息数组。
最后保留最新的消息,从而得到一个数组,其中包含与每个对方交换的最新消息。
因为我上面有两个对方,所以我应该有两个消息的数组。
这是我尝试过的:
const rawMessages = [
{
"sender": {
"id": 1,
"firstName": "JuliettP"
},
"recipient": {
"id": 2,
"firstName": "Julien"
},
"sendDate": "2017-01-28T19:21:15.863",
"messageRead": true,
"text": "ssssssss"
},
{
"sender": {
"id": 3,
"firstName": "Juliani"
},
"recipient": {
"id": 1,
"firstName": "JuliettP"
},
"sendDate": "2017-02-01T18:08:12.894",
"messageRead": true,
"text": "sss"
},
{
"sender": {
"id": 2,
"firstName": "Julien"
},
"recipient": {
"id": 1,
"firstName": "JuliettP"
},
"sendDate": "2017-02-07T22:19:51.649",
"messageRead": true,
"text": "I love redux!!"
},
{
"sender": {
"id": 1,
"firstName": "JuliettP"
},
"recipient": {
"id": 3,
"firstName": "Juliani"
},
"sendDate": "2017-03-13T20:57:52.253",
"messageRead": false,
"text": "hello Juliani"
},
{
"sender": {
"id": 1,
"firstName": "JuliettP"
},
"recipient": {
"id": 3,
"firstName": "Juliani"
},
"sendDate": "2017-03-13T20:56:52.253",
"messageRead": false,
"text": "hello Julianito"
}
];
const currentUserId = 1;
const groupBy = (m: Message) => m.sender.id !== currentUserId ? m.sender.id : m.recipient.id;
const byDate = R.descend(R.prop('sendDate'));
const sort = (value, key) => R.sort(byDate, value);
const composition = R.compose(R.map, R.head, sort, R.groupBy(groupBy));
const latestByCounterParty = composition(rawMessages);
console.log(latestByCounterParty);
对应的代码笔如下:
https://codepen.io/balteo/pen/JWOWRb
有人可以帮忙吗?
编辑:这里是 link 到非柯里化版本:here。没有柯里化,行为是相同的。关于柯里化的必要性,请参阅下面我的评论。
你的例子接近你想要的,尽管你只需要将 head
和 sort
的组合移动到给定 map
的函数参数,然后调用 values
在最终结果上将对象转换为值数组。
const currentMessagesForId = R.curry((id, msgs) =>
R.compose(
R.values,
R.map(R.compose(R.head, R.sort(R.descend(R.prop('sendDate'))))),
R.groupBy(m => m.sender.id !== id ? m.sender.id : m.recipient.id)
)(msgs))
currentMessagesForId(currentUserId, rawMessages)
虽然我认为
注意到关于地图的 important rules 之一是
map(compose(f, g)) ≍ compose(map(f), map(g))
当我们已经在组合管道中时,我们可以选择取消嵌套这一步:
R.map(R.compose(R.head, R.sort(R.descend(R.prop('sendDate'))))),
并把整体方案变成
const currentMessagesForId = R.curry((id, msgs) =>
R.compose(
R.values,
R.map(R.head),
R.map(R.sort(R.descend(R.prop('sendDate')))),
R.groupBy(m => m.sender.id !== id ? m.sender.id : m.recipient.id)
)(msgs)
)
这样做当然是个人喜好问题。但我觉得它更干净。下一步也是一个品味问题。对于可以列在一行中的内容,我选择使用 compose
,因此在格式 compose(f, g, h)(x)
和 f(g(h(x)))
之间建立了一些明显的联系。如果它跨越多行,我更喜欢使用 pipe
,它的行为方式相同,但从头到尾运行它的函数。所以我会进一步改变它看起来像这样:
const currentMessagesForId = R.curry((id, msgs) =>
R.pipe(
R.groupBy(m => m.sender.id !== id ? m.sender.id : m.recipient.id),
R.map(R.sort(R.descend(R.prop('sendDate')))),
R.map(R.head),
R.values
)(msgs)
)
我发现这种自上而下的阅读比较长 compose
版本所需的自下而上的阅读更容易。
但是,正如我所说,这些都是品味问题。
您可以在 Ramda REPL.
上查看这些示例