使用Ramda compose,groupBy和sort来处理对象数组

时间:2017-03-16 21:25:46

标签: ramda.js

我是 Ramda 的新手,我正在努力实现以下目标:

我有一个对象数组(即消息)。

  • 我想按照对方ID(发件人或收件人ID,不论是哪一个,请参阅下面的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);

这是相应的codepen:

https://codepen.io/balteo/pen/JWOWRb

有人可以帮忙吗?

修改:以下是未公开版本的链接:here。没有currying,行为是相同的。请参阅下面的评论,我的问题是关于currying的必要性。

2 个答案:

答案 0 :(得分:2)

你的例子接近你想要的,尽管你只需要将headsort的组合移动到给予map的函数参数然后调用{{1在最终结果上将对象转换为值数组。

values

答案 1 :(得分:2)

虽然我认为solution from Scott Christopher很好,但我自己可以采取两个步骤。

注意到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

上看到这些示例