Reputation:
I've recently started using Ramda to work with responses from JSONAPI. I am a bit confused as to how I should be merging two objects by a deeply nested key.
For example:
Take these two sets of data,
const users = [
{ id: 1,
attributes: {
firstName: "Bob",
lastName: "Lee"
},
relationships: {
phone: {
data: {
id: 2,
type: "phone"
}
}
},
type: "users"
},
{ id: 2,
attributes: {
firstName: "Kevin",
lastName: "Smith"
},
relationships: {
phone: {
data: {
id: 5,
type: "phone"
}
}
},
type: "users"
},
];
const phones= [
{ id: 2,
attributes: {
phoneNumber: "123-345-6789"
},
type: "phones"
},
{ id: 5,
attributes: {
phoneNumber: "987-654-4321"
},
type: "phones"
},
];
What I want to create is a new array with the related phone added to the user array with a new key holding all related objects, like this:
const newUser =
[
{ id: 1,
attributes: {
firstName: "Bob",
lastName: "Lee"
},
relationships: {
phone: {
data: {
id: 2,
type: "phones"
}
}
},
included: {
phoneNumber: "123-345-6789"
}
},
{ id: 2,
attributes: {
firstName: "Kevin",
lastName: "Smith"
},
relationships: {
phone: {
data: {
id: 5,
type: "phones"
}
}
},
type: "users",
included: {
phoneNumber: "987-654-4321"
}
}
]
I've tried multiple methods like map, pick, and join but the objects just don't seem to want to merge the way I want them to. The following code puts both of the objects into the same array, but I can't seem to wrap my head around where to go next.
const data = R.pipe(
R.juxt([
R.pipe(R.path(['users'])),
R.pipe(R.path(['phones']))
]),
)
}),
Upvotes: 0
Views: 1596
Reputation: 50797
Here's my first approach:
const {map, path, find, propEq, assoc} = R
const addPhones = (phones, users) => map(user => {
const phoneId = path(['relationships', 'phone', 'data', 'id'], user)
const phone = find(propEq('id', phoneId), phones)
return phone
? assoc('included', phone.attributes, user)
: user
}, users)
const users = [{"attributes": {"firstName": "Bob", "lastName": "Lee"}, "id": 1, "relationships": {"phone": {"data": {"id": 2, "type": "phone"}}}, "type": "users"}, {"attributes": {"firstName": "Kevin", "lastName": "Smith"}, "id": 2, "relationships": {"phone": {"data": {"id": 5, "type": "phone"}}}, "type": "users"}, {"attributes": {"firstName": "Nancy", "lastName": "Johnson"}, "id": 3, "relationships": {"phone": {"data": {"id": 6, "type": "phone"}}}, "type": "users"}]
const phones= [{"attributes": {"phoneNumber": "123-345-6789"}, "id": 2, "type": "phones"}, {"attributes": {"phoneNumber": "987-654-4321"}, "id": 5, "type": "phones"}, {"attributes": {"phoneNumber": "212-867-5309"}, "id": 7, "type": "phones"}]
console.log(addPhones(phones, users))
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.js"></script>
This version deals appropriately with missing values in either list. If there is a user with no matching phone, the user is returned as-is, with no included
property. And if there is a phone with no matching user, it is simply ignored.
This makes the assumption that you can include the entire phone.attributes
object in your user. If you need to include only the phoneNumber
, it's only slightly more complex, replacing the obvious line with
? assocPath(['included', 'phoneNumber'], phone.attributes.phoneNumber, user)
Upvotes: 1