Reputation: 24679
I am new to Ramda and I am trying to achieve the following:
I have an array of objects (i.e. messages).
I want to group the messages by counter party ID (either the sender or the recipient ID, whichever is not 1, see my groupBy
lambda below).
I am going to obtain an object whose keys will be the counter party IDs and values an array of the messages exchanged with that counter party.
I then want to sort by date descending those arrays of messages.
And finally keep the most recent message and thus obtain an array containing the most recent message exchanged with each of the counter parties.
Because I have two counter parties above, I should have an array of two messages.
Here is what I have attempted:
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);
Here is the corresponding codepen:
https://codepen.io/balteo/pen/JWOWRb
Can someone please help?
edit: Here is a link to the uncurried version: here. The behavior is identical without the currying. See my comment below with my question as to the necessity of currying.
Upvotes: 4
Views: 7615
Reputation: 50797
While I think the solution from Scott Christopher is fine, there are two more steps that I might take with it myself.
Noting that one of the important rules about map is that
map(compose(f, g)) ≍ compose(map(f), map(g))
when we're already inside a composition pipeline, we can choose to unnest this step:
R.map(R.compose(R.head, R.sort(R.descend(R.prop('sendDate'))))),
and turn the overall solution into
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)
)
Doing this, is, of course, a matter of taste. But I find it cleaner. The next step is also a matter of taste. I choose to use compose
for things that can be listed on a single line, and hence make some obvious connection between the formats compose(f, g, h)(x)
and f(g(h(x)))
. If it spans multiple lines, I prefer to use pipe
, which behaves the same way, but runs it's functions from first to last. So I would change this a bit further to look like this:
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)
)
I find this top-down reading easier than the bottom up needed with longer compose
versions.
But, as I said, these are matters of taste.
You can see these examples on the Ramda REPL.
Upvotes: 4
Reputation: 6516
Your example was close to what you wanted, though you need just needed to move the composition of head
and sort
to the function argument given to map
and then call values
on the final result to convert the object to an array of 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)
Upvotes: 2