Reputation: 1349
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
Reputation: 2624
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;
};
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;
};
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;
};
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
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
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