简体   繁体   中英

How to preload a Ramda curried function with item of an Array?

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...

Goal:

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.


What I tried first:

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);

在此处输入图片说明

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); 

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 .

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;
};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM