1

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.

9
  • 1
    What is the rule on the new order? Commented Jul 14, 2020 at 16:31
  • well what does the part of the code that calls 'reorder' look like? the example is incomplete Commented Jul 14, 2020 at 16:31
  • @epascarello I just want to update the order based on recent update array. There is no specific rule. Commented Jul 14, 2020 at 16:32
  • "based on recent update array" still no clue what that means. Commented Jul 14, 2020 at 16:33
  • I tried this code using just the code you provided, and it worked: arr = reorder(arr, 1, 2, 2); arr = reorder(arr, 2, 0, 1); arr = reorder(arr, 3, 1, 0); Commented Jul 14, 2020 at 16:34

3 Answers 3

2

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;
}
Sign up to request clarification or add additional context in comments.

Comments

1

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);

Comments

0

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;
};

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.