2

How can I map an array of ids:

['a', 'b', 'c']

to their corresponding objects in another array:

[
  {
    id: 'a',
    label: 'Letter A'
  },
  {
    id: 'b',
    label: 'Letter B'
  },
  ...
  ...
]

The other array is likely to have more objects than the first array has ids.

The challenging part is that I want to do this the "Ramda way".

If I were not using RamdaJS, it would look like this:

const ids = ['a', 'b']
const objects = [{ id: 'a', label: 'Letter A' }, { id: 'b', label: 'Letter B' }, { id: 'c', label: 'Letter C' }]
const whatIWant = objects.filter(obj => ids.includes(obj.id))

There must be a way to do this with a combination of map, filter, prop, propEq, isNil, reject, etc. I just can't seem to figure it out.

"Keep/filter the objects who have ids included in the ids array"

I know the following code doesn't work, but this is sort of how far I got:

const doFilter = (ids, objects) => (
  map(
    find(
      // ??? Where the object.id is included in ids[]
    )(objects)
  )(ids)
)

Thanks

Captainlonate
  • 4,878
  • 4
  • 25
  • 35

3 Answers3

1

ref

const ids = ['a', 'b'];
const objects = [{ id: 'a', label: 'Letter A' }, { id: 'b', label: 'Letter B' }, { id: 'c', label: 'Letter C' }];
console.log(R.flip(R.contains)(ids)('a'));
console.log(R.flip(R.contains)(ids)('c'));
console.log(R.filter(R.compose(R.flip(R.contains)(ids), R.prop('id')), objects));
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>

ref2

const ids = ['a', 'b'];
const objects = [{ id: 'a', label: 'Letter A' }, { id: 'b', label: 'Letter B' }, { id: 'c', label: 'Letter C' }];

const joinById = R.innerJoin(
  (o, id) => o.id === id
);

const result = joinById(objects, ids);

console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>
shrys
  • 5,860
  • 2
  • 21
  • 36
1

Using map to exclude items from a list was probably a bad start. You typically use map to transform items (not exclude them):

map(add(1), [10,20,30]);
//=> [11,21,31]

To exclude items from a list you have two options: filter or reject

filter keeps items that passed a predicate:

filter(equals('foo'), ['foo', 'bar', 'baz']);
//=> ['foo']

Whereas reject keeps the items that didn't:

reject(equals('foo'), ['foo', 'bar', 'baz']);
//=>  ['bar', 'baz']

To answer your question I'd use flip+includes with where.

Use flip to reverse the order of the arguments to includes. Why? You already know the list of whitelisted ids:

const inWhitelist = flip(includes)(['a', 'b', 'c']);
inWhitelist('a'); // true
inWhitelist('z'); // false

Then use where to query each object. With just one property to check, you could use propSatisfies but in general where reads better. (YMMV of course.)

filter(where({id: inWhitelist}), objects);

const ids = ['a', 'b'];

const objects = [
  { id: 'a',
    label: 'Letter A' },
  { id: 'b',
    label: 'Letter B' },
  { id: 'c',
    label: 'Letter C' }];
    
const inWhitelist = flip(includes)(ids);

console.log(

  filter(where({id: inWhitelist}), objects)

)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>
<script>const {filter, where, flip, includes} = R;</script>
customcommander
  • 17,580
  • 5
  • 58
  • 84
0

Ramda way (if you think it's more readable than vanilla js):

const ids = ['a', 'b']
const objects = [
  { id: 'a', label: 'Letter A' },
  { id: 'b', label: 'Letter B' },
  { id: 'c', label: 'Letter C' },
];

const isInArray = R.flip (R.includes);

const filterByIds = ids => R.filter(R.propSatisfies(isInArray(ids), 'id'));

const results = filterByIds (ids) (objects);

console.log(results):
// [ { id: 'a', label: 'Letter A' },
//   { id: 'b', label: 'Letter B' } ];

diccccn
  • 156
  • 1
  • 2