sadrzadehsina
sadrzadehsina

Reputation: 1349

Update array item order without moving it

I have following array of objects.

[
  { id: 1, title: 't1', order: 0 },
  { id: 2, title: 't1', order: 1 },
  { id: 3, title: 't1', order: 2 },
]

I want to reorder items several times.
In the first try.

// move id: 1, fromOrder: 0, toOrder: 2
[
  { id: 1, title: 't1', order: 2 },
  { id: 2, title: 't2', order: 0 },
  { id: 3, title: 't3', order: 1 },
]

In the second try.

// move id: 3, fromOrder: 1, toOrder: 0
[
  { id: 1, title: 't1', order: 2 },
  { id: 2, title: 't2', order: 1 },
  { id: 3, title: 't3', order: 0 },
]

As you can see the point is that I am not going to move the item, I just want to update the order attribute.

I did something like below but it does not work as expected.

const reorder = (array, id, oldIndex, newIndex) => {
  const ordered = array
    .map(item => item.order === newIndex ? { ...item, order: oldIndex } : item)
    .map(item => item.id === id ? { ...item, order: newIndex } : item);
  
  return ordered;
};

Post Answer Third Party Clarification Edit
The user wanted to shift all item's orders around (including wrapping around), rather than just swapping two values, preserving the relative orders.

Upvotes: 1

Views: 168

Answers (3)

dwb
dwb

Reputation: 2624

Most true to your code option

All you have to do is calculate the difference between the start and end index, and shift all item's order by that value.

const reorder = (array, id, oldIndex, newIndex) => {
  orderShift = newIndex-oldIndex;
  const ordered = array.map(item => {
    item.order = mod(item.order + orderShift, array.length);
    return item;
  });
  return ordered;
};

Most efficient option

The below code is an optimised function, since you don't need to specify the item's id or any specific indexes, only how much to shift by.

const reorder = (array, shift) => {
  for (let i=0, len=array.length; i<len; i++) {
    array[i].order = mod(array[i].order + shift, len);
  }
  return array;
};

Most useful option

If you don't know its current location, and want to specify the newIndex, then you can alternatively use the function below.

const reorder = (array, id, newIndex) => {
  let shift = newIndex - array.find(x => x.id === id).order;
  for (let i=0, len=array.length; i<len; i++) {
    array[i].order = mod(array[i].order + shift, len);
  }
  return array;
};

Extra needed function

Since JavaScript doesn't have a modulo operator (only the % "remainder" operator), I use this function as a quick shortcut.

// you'll need this mod function in all of the above options
function mod(n, m) {
  return ((n % m) + m) % m;
}

Upvotes: 2

Naeio
Naeio

Reputation: 1181

As said in a deleted post, you don't need to ask oldIndex.

As I understand your code, you switch places the arrays items instead of shifting them around. What you have to do is decrement or increment the index of all the elements between the old and the new index :

const reorder = (array, id, newIndex) => {
  const oldIndex = array.findIndex(item => item.id == id);
  const ordered = array
    .map(item =>
      (item.order > oldIndex && item.order <= newIndex) ? // implicit check of oldIndex < newIndex
         { id: 1, title: 't1', order: item.order-1 } :
      (item.order < oldIndex && item.order >= newIndex) ? // implicit check of oldIndex > newIndex
         { id: 1, title: 't1', order: item.order+1 } :
      (item.id == id) ?
         { id: 1, title: 't1', order: newIndex } :
         item
    );
  
  return ordered;
};

Upvotes: 0

epascarello
epascarello

Reputation: 207501

I would just use a for loop and increment the array, when you get to the max or end of array jump it back to zero.

var data = [
  { id: 1, title: 't1', order: 0 },
  { id: 2, title: 't2', order: 1 },
  { id: 3, title: 't3', order: 2 },
  { id: 4, title: 't4', order: 3 },
  { id: 5, title: 't5', order: 4 },
  { id: 6, title: 't6', order: 5 },
  { id: 7, title: 't7', order: 6 },
  { id: 8, title: 't8', order: 7 },
];

const debugIt = array => console.log(array.map(x => `${x.id} - ${x.order}`)); 

const reorder = (array, id, newIndex) => {

  let index = array.findIndex(x => x.id === id);
  var max = array.length - 1;
  for (var i=0; i<array.length; i++) {
    array[index].order = newIndex;
    index++
    newIndex++;
    if (index > max) index = 0;
    if (newIndex > max) newIndex = 0;
  }

};

debugIt(data);
reorder(data, 4, 0);
debugIt(data);
reorder(data, 7, 0);
debugIt(data);

Upvotes: 1

Related Questions