Skip to content

Instantly share code, notes, and snippets.

@royteusink
Last active October 7, 2020 06:32
Show Gist options
  • Save royteusink/ecae35628d609e8a6572789877bbf1b8 to your computer and use it in GitHub Desktop.
Save royteusink/ecae35628d609e8a6572789877bbf1b8 to your computer and use it in GitHub Desktop.
immutable.js - Move item in List to a specific location (change sequence, reorder)
if (action.toindex < 0) return state;
var olditem = state.get(action.index);
var newlist = state.delete(action.index).insert(action.toindex, olditem);
return newlist;
@philraj
Copy link

philraj commented Nov 29, 2017

Be careful – this code will change behavior depending on whether you're moving an item up or down in the list. When moving an item to a lower index, it will appear before the item previously at the target index. When moving an item to a higher index,
it will appear after the item previously at the target index.

To avoid this you can do the following:

if (action.toIndex < 0) return state;
const sourceItem = state.get(action.index);
const targetItem = state.get(action.toIndex);
state = state.delete(action.index);
const targetIndex = state.indexOf(targetItem);
return state.insert(targetIndex, sourceItem);

If the list is huge, you may want to just compute whether to alter the insertion index based on if action.index < action.toIndex for performance reasons.

@bogdosarov
Copy link

BTW, how can I move item inside of nested list?
For example:
move item with id 2 in path [2, 1]

const list = [
  { id: 1 },
  { id: 2 },
  { id: 3, children: [{ id: 5 }, { id: 6 }] },
  { id: 4 },
]

@elbakerino
Copy link

Don't know what i had copied wrong, but also #comment-2271960 didn't worked for me with v4 immutable.

This is what i now got, also in the need to move items in nested map/array a lot:

CodeSandbox

import { List, Map } from "immutable";

/**
 * Move item inside a list to new index
 * @param {List} value
 * @param {int} oldI
 * @param {int} newI
 * @return {List}
 */
const moveItem = (value, oldI, newI) => {
  if (!value || 0 > newI || value.size < newI) return value;

  const srcItem = value.get(oldI);
  return value.splice(oldI, 1).splice(newI, 0, srcItem);
};

/**
 * Moves an item in a list which is in a Map/OrderedMap
 *
 * @param {Map|OrderedMap} state
 * @param {List} stateKeys
 * @param {int} go if positive will move the number further e.g.: `2` positions further: old is 1, new is 3, go is `2`; when negative will move back: old is 3, new is 1, go is `-2`
 * @return {*}
 */
const stateMoveItem = (state, stateKeys, go) => {
  const valueStateKeys = stateKeys.slice(0, -1);
  const index = stateKeys.slice(-1).get(0);
  return state.setIn(
    valueStateKeys,
    moveItem(state.getIn(valueStateKeys), index, index + go)
  );
};

const state = List([
  Map({ id: 1 }),
  Map({ id: 2 }),
  Map({
    id: 3,
    children: List([Map({ id: 5 }), Map({ id: 6 }), Map({ id: 7 })])
  }),
  Map({ id: 4 })
]);

let movedFirstLevel1 = moveItem(state, 0, 1); // switch `id:1` and `id:2`
console.log(movedFirstLevel1.toJS());
let movedFirstLevel2 = moveItem(state, 1, 2); // move `id:2` BEFORE `id:4`
console.log(movedFirstLevel2.toJS());

let movedChildren1 = stateMoveItem(state, List([2, "children", 1]), 1); // move `id:6` AFTER `id:7`
console.log(movedChildren1.toJS());
let movedChildren2 = stateMoveItem(state, List([2, "children", 2]), -1); // move `id:7` before `id:6`
console.log(movedChildren2.toJS());
let movedChildren3 = stateMoveItem(state, List([2, "children", 2]), -2); // move `id:7` before `id:5`
console.log(movedChildren3.toJS());

// `go` is not `newIndex`, when using `newIndex`, substract `oldIndex` from `newIndex` and you got `go`
const oldIndex = 1;
const newIndex = 2;
let movedChildren4 = stateMoveItem(state, List([2, "children", oldIndex]), newIndex - oldIndex); // move `id:6` AFTER `id:7`
console.log(movedChildren4.toJS());

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment