Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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]

like image 933
user2386092 Avatar asked Jul 15 '26 19:07

user2386092


2 Answers

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.

like image 122
Scott Sauyet Avatar answered Jul 17 '26 16:07

Scott Sauyet


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.

like image 34
aaron.xiao Avatar answered Jul 17 '26 14:07

aaron.xiao



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!