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

Using Ramda compose, groupBy and sort in order to process an array of objects

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

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

因为我上面有两个对方,所以我应该有两个消息的数组。

这是我尝试过的:

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。没有柯里化,行为是相同的。关于柯里化的必要性,请参阅下面我的评论。

你的例子接近你想要的,尽管你只需要将 headsort 的组合移动到给定 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.

上查看这些示例