Checking if Ticker is in another list, then adding a new key [RamdaJS]

43 views Asked by At

I'm trying to use Ramda to compare 2 lists, find which tickers in the tickersList are also in the portfolioTickers. If one is in the portfolio list as well then add a key portfolio:true.

Example data:

tickers = [
    { ticker: aa },
    { ticker: bb },
    { ticker: cc }
]

portfolio = [
    { ticker: aa }
]

Expected result:

tickers = [
    { ticker: aa, portfolio: true },
    { ticker: bb },
    { ticker: cc }
]

Sounds simple, but stuck here so far:

const checkTicker = _.curry((ticker, portTicker) => {
    if (ticker.ticker === portTicker.ticker) {
        ticker.portfolio = true;
    }
    return ticker;
});

const matched = R.anyPass([checkTicker]);

const setPortfolioFlag = _.curry((portfolioTickers, ticker) => {
    // const matched = R.filter(checkTicker(ticker), portfolioTickers);
    // console.log('matched', matched)
    const each = matched(R.forEach(checkTicker(ticker), portfolioTickers));
    console.log('each', each)

    return each;
});

const inPortfolioCheck = (tickersList) => {
    const portfolioTickers = R.filter(TickersFactory.isTickerInPortfolio, tickersList);
    const tickers = R.map(setPortfolioFlag(portfolioTickers), tickersList);
    console.log('tickers', tickers)
};

const displayTickers = (tickers) => {
    this.tickersList = tickers; 
    const checkedTickers = inPortfolioCheck(this.tickersList);
    console.log('checkedTickers', checkedTickers)
};

Right now each is always true, and the list that all this logic returns is just a list the same length as my tickerList, but just true'.

A problem I keep running into is I feel that I need to run another 2nd R.map to do the check, however the result of that returned map is the Portfolio tickers, and not the original list.


Working code, but with an ugly for loop:

This obviously works because I'm using a for loop, but I'm trying to remove all object oriented code and replace with functional code.

const setPortfolioFlag = _.curry((portfolioTickers, ticker) => {
    for (var i=0; i<portfolioTickers.length; i++) {
        if (portfolioTickers[i].ticker === ticker.ticker) {
            ticker.portfolio = true;
        }
    }

    return ticker;
});

const inPortfolioCheck = (tickersList) => {
    const portfolioTickers = R.filter(TickersFactory.isTickerInPortfolio, tickersList);
    return R.map(setPortfolioFlag(portfolioTickers), tickersList);
};

const displayTickers = (tickers) => {
    this.tickersList = inPortfolioCheck(tickers);
    console.log('this.tickersList', this.tickersList)
};

LoDash version of the forLoop:

_.each(portfolio, (port) => {
    if (port.ticker === ticker.ticker) {
        ticker.portfolio = true;
    }
});
2

There are 2 answers

6
Scott Christopher On BEST ANSWER

So the first thing you might notice is that the expected resulting tickers array is the same "shape" as the input tickers array, suggesting that we should be able to make use of R.map for the expression. Knowing that, we can then focus on what just has to happen to the individual elements of the array.

So for each ticker object, when found in the portfolio array, we would like to attach the portfolio: true property.

const updateTicker =
  R.when(R.flip(R.contains)(portfolio), R.assoc('portfolio', true))

updateTicker({ ticker: 'aa' })
//=> {"portfolio": true, "ticker": "aa"}

updateTicker({ ticker: 'bb' })
//=> {"ticker": "bb"}

Then we can just map over the tickers list to update each ticker.

R.map(updateTicker, tickers)
//=> [{"portfolio": true, "ticker": "aa"}, {"ticker": "bb"}, {"ticker": "cc"}]
2
Scott Sauyet On

The answer from @scott-christopher is, as always, excellent. But subsequent comments make me think that it doesn't quite cover your use case. If the data is more complex, and it's possible that items in the portfolio and the tickers might have distinct properties, then contains won't be strong enough. So if your data looks more like this:

const tickers = [
    { ticker: 'aa', size: 1 },
    { ticker: 'bb', size: 2 },
    { ticker: 'cc', size: 3 },
    { ticker: 'dd', size: 4 }
]

const portfolio = [
    { ticker: 'aa', id: 'abc' },
    { ticker: 'dd', id: 'xyz' }
]

Then this version might work better:

const updateTicker =
  when(pipe(eqProps('ticker'), any(__, portfolio)), assoc('portfolio', true))

This nearly points-free version might be a little obscure. It's equivalent to this one:

const updateTicker =
    when(
        (ticker) => any(eqProps('ticker', ticker), portfolio), 
        assoc('portfolio', true)
    )

Also, if the any(__, portfolio) notation is unclear, it's equivalent to flip(any)(portfolio).

You can see this on the Ramda REPL.