Reputation: 39018
I have tagsList
which has about 20 tags, and termIds
which is an array of up to 3 tag ids.
I'm trying to find the tags that match the ids in termIds in the tagsList, then set their borders. Looking to avoid for loops and object-oriented programming in favor of a functional programming solution using Ramda curry.
A tag in tagsList looks like :
{
term: 'hi',
id: 123
}
And termIds could look like [123, 345, 678]
When I find an id that matches, I give that tag a new key border1:true
, border2:true
etc...
There is a list of tags, I have another array of termIds, goal is to see if any of the tags in the tagsList have an id that matches the termIds. If so give it a border1, if there are 2, then the 2nd gets border2 and finally 3 gets border 3.
const checkId = _.curry((term_id, tag) => {
if (tag.id === term_id) {
console.log('match found!', tag)
}
});
const matchId = checkId(termIds);
const coloredTags = R.map(matchId, tagsList);
console.log('coloredTags', coloredTags)
return tagsList;
However this did not work because I am preloading the entire termIds array into the checkId
function. When instead I want to preload it with the individual items.
Next I tried this which I thought would work but getting a strange error:
const matchId = R.forEach(checkId, termIds);
Upvotes: 0
Views: 101
Reputation: 50787
This seems a reasonable approach:
R.map(tag => {
const index = R.indexOf(tag.id, termIds);
return (index > -1) ? R.assoc('border' + (index + 1), true, tag) : tag
})(tagsList);
//=> [
// {id: 123, term: "hi", border1: true},
// {id: 152, term: "ho"},
// {id: 345, term: "hu", border2: true},
// {id: 72, term: "ha"}
// ]
Although it could probably be made points-free with enough effort, it would likely be much less readable.
You can see this in action on the Ramda REPL.
If you want to make this into a reusable function, you can do it like this:
const addBorders = R.curry((terms, tags) => R.map(tag => {
const index = R.indexOf(tag.id, terms);
return (index > -1) ? R.assoc('border' + (index + 1), true, tag) : tag
})(tags))
addBorders(termIds, tagsList)
(The call to curry
is a Ramda habit. It means you can call addBorders(termIds)
and get back a reusable function that is looking for the tags. If you don't need that, you can skip the curry
wrapper.)
This version is also on the Ramda REPL.
Upvotes: 1
Reputation: 4612
I think pure JS is enough to do it without Ramda. You just need a map :
var tagsList = [{term: 'hi', id: 123}, {term: 'ho', id: 152}, {term: 'hu', id: 345}, {term: 'ha', id: 72}];
var termIds = [123, 345, 678];
var i = 1;
var results = tagsList.map(x => {
if (termIds.indexOf(x.id) !== -1) x["border"+ (i++)] = true;
return x;
});
console.log(results);
Upvotes: 1
Reputation: 39018
Ah just figured it out, I had to curry the logic a 2nd time:
const matchId = R.curry((tag, term_id) => {
if (tag.id === Number(term_id)) {
console.log('match found!', tag)
}
});
const curried = R.curry((termIds, tag) => {
return R.map(matchId(tag), termIds);
});
const coloredTags = R.map(curried(termIds), tagsList);
console.log('coloredTags', coloredTags)
return tagsList;
So at the coloredTags
line, a tag from tagsLists goes into the curried(termIds). Ramda functions accept params from right to left.
curried(termIds)
is already preloaded with the termIds array. So next in the const curried =
line, the termIds array and single tag make it in and the tag gets sent along into the next curried function matchId
, also the termIds
array is placed in the R.map
. Ramda list functions accept the Array of data as the right param.
Finally in matchId
I can make my check!
UPDATE
So the above answers the question I asked, about how to curry an item from an Array. However it caused a bug in my app. Since the termIds
array could hold up to 3 items, the coloredTags R.map
will run up to 3 times and create duplicate tags in my tagsList.
So just for completeness this is how I solved my in problem, much simpler and didn't need to use a double curried function.
const setTagColors = (tagsList, state) => {
const setBorder = (tag) => {
if (tag.id === Number(state.term_id_1)) {
tag.border1 = true;
} else if (tag.id === Number(state.term_id_2)) {
tag.border2 = true;
} else if (tag.id === Number(state.term_id_3)) {
tag.border3 = true;
}
return tag;
};
const coloredTags = R.map(setBorder, tagsList);
return state.term_id_1 ? coloredTags : tagsList;
};
Upvotes: -1