Reputation: 975
I have a collection that I’d like to grab some values from, and merge them together to form a new value. So far I’ve looked at R.evolve
, but with no luck (because medias
is also a collection.
This is what I’m starting with:
[{
"permalink": "http://example.com/1",
"medias": [{
"filename": "image_1.png",
}]
}, {
"permalink": "http://example.com/3",
"medias": [{
"filename": "image_3.png",
}]
}]
And this is what I’d like to end with:
[{
"permalink": "http://example.com/1",
"medias": [{
"filename": "image_1.png",
"image_url": "http://example.com/1/image_1.png"
}]
}, {
"permalink": "http://example.com/3",
"medias": [{
"filename": "image_3.png",
"image_url": "http://example.com/3/image_3.png"
}]
}]
Essentially what we’re doing is prepending each filename
with a permalink
.
This is the kind of thing I’ve been going for so far, unfortunately when running the transform on medias
, I can’t get access to the permalink
property.
const mapMedia = R.map(
// can’t get access to the `permalink` in here?
)
const transformations = {
medias: mapMedia
}
const transform = R.compose(
R.map(R.evolve(transformations)),
)(data)
Upvotes: 2
Views: 579
Reputation: 50807
So the problem is that the data you need is not in scope. In my opinion, the easiest way to fix that is to add some scope!
First off, you don't need that compose
call, since you're only supplying it one function. So we can replace
const transform = R.compose(
R.map(R.evolve(transformations)),
)(data)
with just
const transform = R.map(R.evolve(transformations)),
(I also removed the call with (data)
, since I'm trying to build this up as a reusable function. We would then call it later with transform(data)
.
Now we have to add context so that transformations
has the object in scope. We can do that like this:
const transform = R.map(image => R.evolve(transformations(image), image));
Of course that means transformations
needs to change to take this into account. We can do that like this:
const transformations = image => ({
medias: mapMedia(image.permalink)
})
... and this in turn requires a change to mapMedia
. I thought that it should only need to know about the permalink and not the entire image, so we can pass only that as above. And mapMedia
would now look like this:
const mapMedia = permalink => R.map(
medium => // do something with `medium` and `permalink`
);
There are many ways that you could do this last step. One of them, fairly aligned to your use of evolve
would be this:
const mapMedia = permalink => R.map(
medium => assoc('image_url', permalink + '/' + medium.filename, medium)
);
Another alternative to both evolve
and assoc
is to investigate the various lens
-related functions in Ramda, such as lensProp
, over
, and set
.
Now, if those helper functions are only used for this purpose, you might not want them cluttering things up, and you could choose to inline them in your main function. If you liked, you might combine all the above into
const addUrls = map(
image => evolve({
medias: map(medium => assoc('image_url', image.permalink + '/' + medium.filename, medium)),
}, image)
);
You can see the separate functions approach or the combined one in the Ramda REPL. For good measure, there's also a version using lenses.
(And on a separate note, the word "media" is already plural. The singular is "medium". There's no good reason for "medias".)
Upvotes: 3