Mapping an ImmutableJS collection

Viewed 57

I have an ImmutableJS structure that looks like this:

let state = fromJS({
  // ...
  foo: [{
    bar: ['id1', 'id2']
  },{
    bar: ['id2', 'id3']
  }]
});

I want to map over foo and for each item, filter the associated bar against another array of ids to exclude (idsToExclude).

I have come up with this:

const idsToExclude = ['id3', 'id4'];
state = { 
    ...state, 
    foo: state.get('foo').map((i) => 
        i.set(['bar'], i.get(['bar']
            .filterNot(b => idsToExclude.some(id => id === b)))) 
};

Is there a better, more idiomatic or terser way?

I ask because this seems quite verbose.

2 Answers

I think you can get this a little cleaner by using .update, though this is still fairly long. You can also simplify .some(id => id === b) to .contains(b).

state = state.update('foo', foo => 
    foo.map(item => 
        item.update('bar', 
            bar => bar.filterNot(
                id => idsToExclude.contains(id)))));

There are several problems with your existing code:

  • state = {...state} is creating a plain JS object, not an Immutable.js Map, and it won't merge the new foo into the fields from state correctly.
  • I'm pretty sure get and set only accept a single key, not an array. To use an array of keys as a path, use getIn or setIn.

If you can use immutable Sets in your data structure instead of Lists, then you can use set operations like subtract to simplify your code:

import {fromJS, Set} from 'immutable'

const initState = fromJS({
  // ...
  foo: [
    {bar: Set(['id1', 'id2'])},
    {bar: Set(['id2', 'id3'])}
  ]
})
const idsToExclude = fromJS(['id3', 'id4'])
const finalState = initState.update('foo', foo =>
  foo.map(element =>
    element.update('bar', set =>
      set.subtract(idsToExclude)
    )
  )
)

Even if not storing Sets in your data structure, you could use them temporarily during the mutation:

const initState = fromJS({
  // ...
  foo: [
    {bar: ['id1', 'id2']},
    {bar: ['id2', 'id3']}
  ]
})
const idsToExclude = fromJS(['id3', 'id4'])
const finalState = initState.update('foo', foo =>
  foo.map(element =>
    element.update('bar', list =>
      list.toSet().subtract(idsToExclude).toList()
    )
  )
)
Related