Reputation: 27713
Given a state like this:
state = {
things: [
{ id: 'a1', name: 'thing 1' },
{ id: 'a2', name: 'thing 2' },
],
};
How can I create a new state where ID "a1" is removed? It's easy enough to push new items:
return state.set(state.get('things').push(newThing));
But I can't figure out how to search for and remove an object by its id
property. I tried this:
return state.set('tracks',
state.get('tracks').delete(
state.get('tracks').findIndex(x => x.get('id') === 'a2')
)
)
But it seems messy, plus it only works if the item is found, because if findIndex
returns -1
, that's a valid value for delete
.
Upvotes: 26
Views: 32412
Reputation: 26076
Immutablejs is great but at the same time makes things more complicated in some edge cases, particularly when working with nested arrays.
Sometimes it is easier to take it back to JS in a general sense for this particular issue.
// 1. get a copy of the list into normal JavaScript
const myList = state.getIn(['root', 'someMap', 'myList']).toJS()
// 2. remove item in list using normal JavaScript and/or anything else
myList.splice(deleteIndex, 1)
// 3. return the new state based on mutated myList
return state
.mergeDeep({ root: { someMap: { myList: undefined } }})
.mergeDeep({ root: { someMap: { myList } }})
Unfortunately, step 3 is necessary to specifically set to undefined
because if you simply set myList
directly as an array value, ImmutableJS will do a comparison of values between the current list and only modify them creating strange behavior.
The justification for this is to simplify the mental overhead. I do not recommend doing this in a loop, rather manipulate the pure JS array in a loop if you must but should be prior to step 3.
Upvotes: 0
Reputation: 2969
You can do that even without immutable.js with following function.
function arrayFilter(array, filter) {
let ret = array
let removed = 0
for (let index = 0; index < array.length; index++) {
const passed = filter(array[index], index, array)
if (!passed) {
ret = [...ret.slice(0, index - removed), ...ret.slice(index - removed + 1)]
removed++
}
}
return ret
}
Upvotes: 0
Reputation: 2662
When you are using filter it iterates all cycle -> one effective way is finding index => slice and using splitter ...
const index = state.findIndex(data => data.id === action.id);
return [...state.slice(0, index), ...state.slice(index + 1)];
Upvotes: 15
Reputation: 111
Found this thread while looking for a solution to a similar task. Solved it with update method:
return state.update('things', (things) => things.filter((t) => t.id !== action.things.id))
any idea/comment which one is better/preferred?
Upvotes: 1
Reputation: 10837
Alternatively, as you are "searching and then deleting"...
var itemIndex = this.state.get("tracks").findIndex(x => x.get('id') === 'a2');
return itemIndex > -1 ? this.state.deleteIn(["tracks", itemIndex]) : this.state;
This will ensure the state is not mutated when there are no changes.
Upvotes: 5
Reputation: 87203
You can use Array#filter
.
return state.set('things', state.get('things').filter(o => o.get('id') !== 'a1'));
Upvotes: 42