user2386092
user2386092

Reputation: 186

Ramda map and filter in one loop

const R = require('rambda')

export const test = () => {

  const data = [1, 2, 3, 4, 5, 6, 7, 8]
  const filter = no => no > 5
  const map = no => no * 100

  // looping 2 times
  return data
    .filter(filter)
    .map(map)

  // wird 1 loop
  return data.reduce((data, no) => {
    if (!filter(no)) return data
    data.push(map(no))
    return data
  }, [])

  // I want to do something like this.
  return data.map(R.pipe(map, filter))

}

Hello, is it possible to map and filter in one loop using ramda or rambda?

My example:

return data.map(R.pipe(map, filter))

is returning

Array [
  600,
  700,
  800,
  true,
  true,
  true,
  true,
  true,
  true,
  true,
  true,
]

and I need it to be [600, 700, 800]

Upvotes: 3

Views: 4349

Answers (2)

aaron.xiao
aaron.xiao

Reputation: 198

Of course it can be.

const expr = R.compose(
    R.map(x => x * 100),
    R.filter(x => x > 5)
);
const result = expr([1, 2, 3, 4, 5, 6, 7, 8]);
console.log(result);

updated: a one-loop way:

const iter = (acc, x) => (x > 5 && acc.push(x * 100), acc)
const result = R.reduce(iter, [], [1, 2, 3, 4, 5, 6, 7, 8])

Both one-loop or two-loop, execute the same logic, should no difference.

Upvotes: 1

Scott Sauyet
Scott Sauyet

Reputation: 50797

You can do this with Ramda's transducers.

(Disclaimer: I'm a Ramda author)

While a normal composition of regular Ramda functions will loop for every list transformation, a transduced one will run each function in the composition on each iteration. Note that for technical reasons, the composition runs in the reverse order when using transducers.

Not every function is a transducer. The documentation uses this note to indicate one: "Acts as a transducer if a transformer is given in list position." This is included in both map and filter. There are several functions that make it easier to work with transducers. into is one of them.

Still, it might be easier to write your own filterMap function. Is isn't hard:

const filterMap = curry((f, m, data) => reduce(
  (a, i) => f(i) ? append(m(i), a) : a, 
[], data))

const {compose, map, filter, into, curry, reduce, append} = R;

const data = [1, 2, 3, 4, 5, 6, 7, 8]
const gt5 = no => {console.log(`filter: ${no}`); return no > 5;}
const times100 = no => {console.log(`map ${no}`); return no * 100;}

console.log('========= Plain composition =========')
const transform1 = compose(map(times100), filter(gt5))
console.log(transform1(data))

console.log('========== With transducers =========')
const transform2 = into([], compose(filter(gt5), map(times100)));
console.log(transform2(data))

console.log('=========== With filterMap ==========')
const filterMap = curry((f, m, data) => reduce((a, i) => f(i) ? append(m(i), a) : a, [], data))
console.log(filterMap(gt5, times100)(data))
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.js"></script>

If you want to see how the transducers shine, add a take(2) to the end of the composition.

These add a great deal of complexity to Ramda's implementations, and there has been some talk of removing them, or trying to find a way to make them optional. But not much has been done toward this.

Upvotes: 15

Related Questions