Как удалить объект из массива в Immutable?

Учитывая такое состояние:

state = {
  things: [
    { id: 'a1', name: 'thing 1' },
    { id: 'a2', name: 'thing 2' },
  ],
};

Как создать новое состояние, в котором удаляется идентификатор "a1"? Достаточно легко выдвинуть новые предметы:

return state.set(state.get('things').push(newThing));

Но я не могу понять, как искать и удалять объект по свойству id. Я попробовал это:

return state.set('tracks',
  state.get('tracks').delete(
    state.get('tracks').findIndex(x => x.get('id') === 'a2')
  )
)

Но это кажется беспорядочным, плюс он работает только, если элемент найден, потому что если findIndex возвращает -1, это допустимое значение для delete.

Ответ 1

Вы можете использовать Array#filter.

return state.set('things', state.get('things').filter(o => o.get('id') !== 'a1'));

Ответ 2

Когда вы используете фильтр, он выполняет итерацию всего цикла → один эффективный способ находит index => slice и использует сплиттер...

const index = state.findIndex(data => data.id === action.id);

return [...state.slice(0, index), ...state.slice(index + 1)];

Ответ 3

Кроме того, поскольку вы "выполняете поиск, а затем удаляете"...

var itemIndex = this.state.get("tracks").findIndex(x => x.get('id') === 'a2');

return itemIndex > -1 ? this.state.deleteIn(["tracks", itemIndex]) : this.state;

Это гарантирует, что состояние не будет изменено, если изменений не будет.

Ответ 4

Нашел эту тему, ища решение аналогичной задачи. Решил его с помощью метода обновления:

return state.update('things', (things) => things.filter((t) => t.id !== action.things.id))

любая идея/комментарий, какой из них лучше/предпочтительнее?

Ответ 5

Вы можете сделать это даже без immutable.js со следующей функцией.

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
}

Ответ 6

ImmutableJS, работающий с вложенными массивами

Immutablejs отлично, но в то же время усложняет работу в некоторых случаях, особенно при работе с вложенными массивами.

Иногда легче вернуть его в JS в общем смысле для этой конкретной проблемы.

// 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 } }})

К сожалению, шаг 3 необходим для того, чтобы специально установить значение undefined потому что если вы просто установите myList непосредственно как значение массива, ImmutableJS проведет сравнение значений между текущим списком и только изменит их, создавая странное поведение.

Обоснованием этого является упрощение умственных накладных расходов. Я не рекомендую делать это в цикле, а манипулировать чистым массивом JS в цикле, если нужно, но должен быть до шага 3.