简体   繁体   中英

Filter & map an array with Ramda

I am using Ramda to get the teams with 'Prem League' in their categories array. My code looks like the following and it works.

import { pipe, map, filter } from 'ramda'   

const teams = [
  {name: 'Liverpool', id: '1', categories: ['Prem League']},
  {name: 'Man Utd', id: '2', categories: ['Blue Square']},
  {name: 'Sheff Utd', id: '2', categories: ['Prem League']},
]

const getTeamOptions = pipe(
    filter((team) => team.categories.includes('Prem League')),
    map((team) => ({ label: team.name, value: team.id }))
);

getTeamOptions(teams)

However I want to remove team as the argument for filter and map .

I tried the following but got prop(...).includes is not a function

filter(prop('categories').includes('Prem League')),

Ideally I would try and remove team from map too but maybe this is not necessary.

The reason for these changes is that I have been following this course and it advises prop etc as best practices.

You can use R.includes to check for the existence of the value. You can generate the new object using R.applySpec:

 const { pipe, filter, prop, includes, map, applySpec } = R; const getTeamOptions = val => pipe( filter(pipe(prop('categories'), includes(val))), map(applySpec({ label: prop('name'), value: prop('id') })) ); const teams = [{"name":"Liverpool","id":"1","categories":["Prem League"]},{"name":"Man Utd","id":"2","categories":["Blue Square"]},{"name":"Sheff Utd","id":"2","categories":["Prem League"]}]; const result = getTeamOptions('Prem League')(teams); console.log(result);
 <script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.0/ramda.js"></script>

this would also help you solve it with ramda in a point free fashion...

 const hasPremLeague = R.where({ categories: R.includes('Prem League') }); const toOption = R.applySpec({ label: R.prop('name'), value: R.prop('id') }); const getTeamOptions = R.into([], R.compose( R.filter(hasPremLeague), R.map(toOption), )); // --- const teams = [ {name: 'Liverpool', id: '1', categories: ['Prem League']}, {name: 'Man Utd', id: '2', categories: ['Blue Square']}, {name: 'Sheff Utd', id: '2', categories: ['Prem League']}, ]; console.log( getTeamOptions(teams), );
 <script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.0/ramda.js"></script>

const premLeague = R.equals('Prem League');
const premLeagueInArray = R.any(premLeague);
const categories = R.path(['categories']);
const isPremLeagueInArray = R.pipe(
  categories,
  premLeagueInArray,
);

const teams = [
  { name: "Liverpool", id: "1", categories: ["Prem League"] },
  { name: "Man Utd", id: "2", categories: ["Blue Square"] },
  { name: "Sheff Utd", id: "2", categories: ["Prem League"] },
];
const premLeagueTeam = [
  { name: "Liverpool", id: "1", categories: ["Prem League"] },
  { name: "Sheff Utd", id: "2", categories: ["Prem League"] },
];

const transducer = R.compose(R.filter(isPremLeagueInArray));
const getPremLeagueTeam = R.transduce(transducer, R.flip(R.append), []);

R.equals(getPremLeagueTeam(teams), premLeagueTeam);

You should consider a non-Ramda option too.

This may or may not be an abuse of Array#flatMap but I find it acceptable: filter + map = flatMap

Let's say you want to add 10 to even numbers and exclude odd numbers:

[1, 2, 3, 4].flatMap(n => n % 2 === 0 ? n + 10 : []);
//=> [12, 14]

There is also a point to be made about pointfree style. It's nice, but sometimes it gets in the way. For example it doesn't allow you to leverage some nice ES6 constructs. For example destructuring:

const getTeamOptions =
  teams =>
    teams.flatMap
      ( ({name: label, id: value, categories}) =>
          categories.includes('Prem League')
            ? { label, value }
            : []
      );

getTeamOptions
  ( [ {name: 'Liverpool', id: '1', categories: ['Prem League']}
    , {name: 'Man Utd', id: '2', categories: ['Blue Square']}
    , {name: 'Sheff Utd', id: '2', categories: ['Prem League']}
    ]
  );

//=> [ {label: "Liverpool", value: "1"}
//=> , {label: "Sheff Utd", value: "2"} ]

For completeness, here's a variant using Array#reduce :


const getTeamOptions =
  teams =>
    teams.reduce
      ( (acc, {name: label, id: value, categories}) =>
          categories.includes('Prem League')
            ? (acc.push({ label, value }), acc)
            : acc
      , []
      );

Don't get me wrong! Ramda is absolutely amazing . When I first met this library I wanted to rewrite all my code with it, then I discovered pointfree style and rewrote everything again. In the end I completely lost mental control of my code and that's a problem. You should only use Ramda when it serves you well. In this case you could do without to be honest.

I love to do this in composable way, but currently you are using pipe so I do this with pipe for consitency. Below is a worked solution

 const teams = [ { name: 'Liverpool', id: '1', categories: ['Prem League'] }, { name: 'Man Utd', id: '2', categories: ['Blue Square'] }, { name: 'Sheff Utd', id: '2', categories: ['Prem League'] } ] const getTeamOptions = pipe( filter( pipe( prop('categories'), includes('Prem League') ) ), map( pipe( props(['name', 'id']), zipObj(['label', 'value']) ) ) ) console.log(getTeamOptions(teams))
 <script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.0/ramda.js"></script> <script>const { pipe, map, filter, prop, includes, zipObj, props } = R</script>

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