Update array item order without moving it - javascript

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.

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

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

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

Related

How to get unique element from two arrays in js?

I'm trying to get unique (by id) values from two arrays.
But it returns whole array instead of { id: 3 }
const a = [{ id: 1 }, { id: 2 }];
const b = [{ id: 1 }, { id: 2 }, { id: 3 }];
const array3 = b.filter((obj) => a.indexOf(obj) == -1);
console.log(array3);
What's wrong here?
You cannot compare objects you should check that an element with that id doesn't exists in the other array
here I used some that returns a boolean if he can find a match
const a = [{
id: 1
}, {
id: 2
}];
const b = [{
id: 1
}, {
id: 2
}, {
id: 3
}];
const array3 = b.filter(obj => !a.some(({id}) => obj.id === id));
console.log(array3)
In your case, the following code gives all unique objects as an array, based on the id.
const a = [{
id: 1
}, {
id: 2
}];
const b = [{
id: 1
}, {
id: 2
}, {
id: 3
}];
const array3 = b.filter(objB => a.some((objA) => objB.id !== objA.id));
console.log(array3)
A different approach with a symmetrically result.
const
take = m => d => o => m.set(o.id, (m.get(o.id) || 0) + d),
a = [{ id: 1 }, { id: 2 }],
b = [{ id: 1 }, { id: 2 }, { id: 3 }],
map = new Map(),
add = take(map),
result = [];
a.forEach(add(1));
b.forEach(add(-1));
map.forEach((v, id) => v && result.push({ id }));
console.log(result);

Return similar values ​from multiple array of objects in Javascript

Guys I made a simple example to illustrate my problem. I have 3 object arrays, datasOne, datasTwo and datasThree and what I want is to return a new array only with the objects that are in the 3 arrays. For example, if there is only Gustavo in the 3 arrays, then he will be returned. But there is a detail that if the datasThree is an empty array, then it will bring the data in common only from datasOne and datasTwo and if only the datasTwo which has data and the other two arrays have empty, then it will return data only from datasTwo. In other words it is to return similar data only from arrays that have data. I managed to do this algorithm and it works the way I want, but I would like to know another way to make it less verbose and maybe simpler and also work in case I add more arrays to compare like a dataFour for example. I appreciate anyone who can help me.
My code below:
let datasOne = [
{ id: 1, name: 'Gustavo' },
{ id: 2, name: 'Ana' },
{ id: 3, name: 'Luiz' },
{ id: 8, name: 'Alice' }
]
let datasTwo = [
{ id: 1, name: 'Gustavo' },
{ id: 3, name: 'Luiz' },
{ id: 8, name: 'Alice' }
]
let datasThree = [
{ id: 1, name: 'Gustavo' },
{ id: 3, name: 'Luiz' },
{ id: 2, name: 'Ana' },
{ id: 5, name: 'Kelly' },
{ id: 4, name: 'David' }
]
let filtered
if (datasOne.length > 0 && datasTwo.length > 0 && datasThree.length > 0) {
filtered = datasOne.filter(firstData => {
let f1 = datasThree.filter(
secondData => firstData.id === secondData.id
).length
let f2 = datasTwo.filter(
secondData => firstData.id === secondData.id
).length
if (f1 && f2) {
return true
}
})
} else if (datasOne.length > 0 && datasTwo.length > 0) {
filtered = datasOne.filter(firstData => {
return datasTwo.filter(secondData => firstData.id === secondData.id).length
})
} else if (datasOne.length > 0 && datasThree.length > 0) {
filtered = datasOne.filter(firstData => {
return datasThree.filter(secondData => firstData.id === secondData.id)
.length
})
} else if (datasTwo.length > 0 && datasThree.length > 0) {
filtered = datasTwo.filter(firstData => {
return datasThree.filter(secondData => firstData.id === secondData.id)
.length
})
} else if (datasThree.length > 0) {
filtered = datasThree
} else if (datasTwo.length > 0) {
filtered = datasTwo
} else if (datasOne.length) {
filtered = datasOne
}
console.log(filtered)
1) You can first filter the array which is not empty in arrs.
const arrs = [datasOne, datasTwo, datasThree].filter((a) => a.length);
2) Flatten the arrs array using flat().
arrs.flat()
3) Loop over the flatten array and count the occurrence of all objects using Map
const map = new Map();
for (let o of arrs.flat()) {
map.has(o.id)
? (map.get(o.id).count += 1)
: map.set(o.id, { ...o, count: 1 });
}
4) Loop over the map and collect the result only if it is equal to arrs.length
if (count === arrs.length) result.push(rest);
let datasOne = [
{ id: 1, name: "Gustavo" },
{ id: 2, name: "Ana" },
{ id: 3, name: "Luiz" },
{ id: 8, name: "Alice" },
];
let datasTwo = [
{ id: 1, name: "Gustavo" },
{ id: 3, name: "Luiz" },
{ id: 8, name: "Alice" },
];
let datasThree = [
{ id: 1, name: "Gustavo" },
{ id: 3, name: "Luiz" },
{ id: 2, name: "Ana" },
{ id: 5, name: "Kelly" },
{ id: 4, name: "David" },
];
const arrs = [datasOne, datasTwo, datasThree].filter((a) => a.length);
const map = new Map();
for (let o of arrs.flat()) {
map.has(o.id)
? (map.get(o.id).count += 1)
: map.set(o.id, { ...o, count: 1 });
}
const result = [];
for (let [, obj] of map) {
const { count, ...rest } = obj;
if (count === arrs.length) result.push(rest);
}
console.log(result);
/* This is not a part of answer. It is just to give the output fill height. So IGNORE IT */
.as-console-wrapper { max-height: 100% !important; top: 0; }
Not 100% sure it cover all edge cases, but this might get you on the right track:
function filterArrays(...args) {
const arraysWithData = args.filter((array) => array.length > 0);
const [firstArray, ...otherArrays] = arraysWithData;
return firstArray.filter((item) => {
for (const array of otherArrays) {
if (!array.some((itemTwo) => itemTwo.id === item.id)) {
return false;
}
}
return true;
});
}
Usage:
const filtered = filterArrays(datasOne, datasTwo, datasThree);
console.log(filtered)
I believe the code is fairly readable, but if something is not clear I'm glad to clarify.
function merge(arr){
arr = arr.filter(item=>item.length>0)
const map = {};
arr.forEach(item=>{
item.forEach(obj=>{
if(!map[obj.id]){
map[obj.id]=[0,obj];
}
map[obj.id][0]++;
})
})
const len = arr.length;
const ret = [];
Object.keys(map).forEach(item=>{
if(map[item][0]===len){
ret.push(map[item][1])
}
})
return ret;
}
merge([datasOne,datasTwo,datasThree])

Missing data should be on the next array

I am making a matchmaking system where 2 players with the same level will be joined in 1 array. However, when there are 4 players with the same level, the other 2 players are disappearing. My target is to show those 2 players with the same level in another array. I provided an image below and my codes. Any help will be appreciated.
const source = [{
id: 1,
name: 'player1',
level: 1
},
{
id: 2,
name: 'player2',
level: 1
},
{
id: 3,
name: 'player3',
level: 2
},
{
id: 4,
name: 'player4',
level: 2
},
{
id: 5,
name: 'player5',
level: 3
},
{
id: 6,
name: 'player6',
level: 3
},
{ // this data is missing
id: 7,
name: 'player7',
level: 1
},
{ // this data is missing
id: 8,
name: 'player8',
level: 1
},
]
const combine = (source) => {
return source.reduce((acc, curr) => {
if (acc[curr.level] && acc[curr.level].length > 1)
return acc;
if (acc[curr.level])
acc[curr.level].push(curr);
else
acc[curr.level] = [curr];
return acc;
}, {})
}
var result = combine(source)
var html = ""
var keys = Object.keys(result) //if there more then one keys i.e : 2..
for (var i = 0; i < keys.length; i++) {
console.log("Keys " + keys[i])
//loop through json array
result[keys[i]].forEach(function (val, index) {
//check if index value is `0`..change name.
var ids = index == 0 ? "id[]" : "idside[]"
var name = index == 0 ? "name[]" : "nameside[]"
var levels = index == 0 ? "level[]" : "levelside[]"
html += `<input type="text" name="${ids}" value="${val.id}">
<input type="text" name="${name}" value="${val.name}">
<input type="text" name="${levels}" value="${val.level}">`
})
}
document.getElementById("result").innerHTML = html //add html to div
console.log(result);
<div id="result">
</div>
Problem is on these two lines
if (acc[curr.level] && acc[curr.level].length > 1)
return acc;
If there already is level 1 in acc and has two or more players, you just skip all other level 1 players.
My solution, even though probably bit slower, would be to sort and group the input data based on level and then push pairs into an array.
Target achieved. Super thanks to: Swati. Also, thank you for your suggestions.
const source = [{
id: 1,
name: 'player1',
level: 1
},
{
id: 2,
name: 'player2',
level: 1
},
{
id: 3,
name: 'player3',
level: 2
},
{
id: 4,
name: 'player4',
level: 2
},
{
id: 5,
name: 'player5',
level: 3
},
{
id: 6,
name: 'player6',
level: 3
},
{ // this data is missing
id: 7,
name: 'player7',
level: 1
},
{ // this data is missing
id: 8,
name: 'player8',
level: 1
},
]
const combine = (source) => {
return source.reduce((acc, curr) => {
if (acc[curr.level]) {
const levelArr = acc[curr.level];
const last = levelArr[levelArr.length - 1];
if (last.length === 2) {
levelArr.push([curr])
} else {
last.push(curr);
}
} else {
acc[curr.level] = [
[curr]
];
}
return acc;
}, {})
};
var result = combine(source)
var html = ""
var keys = Object.keys(result) //if there more then one keys i.e : 2..
for (var i = 0; i < keys.length; i++) {
result[keys[i]].forEach(function(val) {
val.forEach(function(value, index) {
var ids = index == 0 ? "id[]" : "idside[]"
var name = index == 0 ? "name[]" : "nameside[]"
var levels = index == 0 ? "level[]" : "levelside[]"
html += `<input type="text" name="${ids}" value="${value.id}"> <input type="text" name="${name}" value="${value.name}">
<input type="text" name="${levels}" value="${value.level}">`
})
})
}
document.getElementById("result").innerHTML = html //add html to div
<div id="result">
</div>

How to remove object in array of objects with matching values

I want to check my array for objects with matching values, if they match remove the object with the lowest index as that will be the one is "older"
I had success using this method for removing duplicate objects in the array, but when i get to specific values of those objects i'm not sure
someFunction() {
let cart = this.state.currentUser.cart
const newCartArray = cart.filter((light, index) => {
return index === cart.findIndex(obj => {
obj.use === light.use
})
})
cart = newCartArray
}
You could take a Map and store the last object with a wanted key and get as result only the last stored objects.
var array = [{ id: 1, index: 0 }, { id: 2, index: 1 }, { id: 3, index: 2 }, { id: 2, index: 3 }, { id: 3, index: 4 }, { id: 1, index: 5 }, { id: 4, index: 6 }, { id: 5, index: 7 }],
result = Array.from(array.reduce((m, o) => m.set(o.id, o), new Map).values());
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
If you like to keep the original order, you could check the same object reference for filtering.
var array = [{ id: 1, index: 0 }, { id: 2, index: 1 }, { id: 3, index: 2 }, { id: 2, index: 3 }, { id: 3, index: 4 }, { id: 1, index: 5 }, { id: 4, index: 6 }, { id: 5, index: 7 }],
map = array.reduce((m, o) => m.set(o.id, o), new Map),
result = array.filter(o => o === map.get(o.id));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
let cart = this.state.currentUser.cart;
let index = cart.indexOf(light);
if( index != -1) {
cart.splice( index,1);
}
or if you need to check the .use
let cart = this.state.currentUser.cart;
for( let i =0; i < cart.length; i++) {
if( cart[i].use === light.use) {
cart.splice(i,1);
break;
}
}
You could filter out all the items that have subsequent items match the relevant property, like so:
const newCartArray = cart.filter((light, i, array) => {
return !array.slice(i + 1).some(obj => obj.use === light.use);
})
This should work:
someFunction() {
let cart = this.state.currentUser.cart
const newCartArray = cart.filter((light, index) => {
return cart.slice(index + 1).findIndex(obj => {
obj.use === light.use
}) === -1;
})
cart = newCartArray
}

How to update Array object index when i delete a array object item from array in angualrjs?

This is my array object
var item = [
{index:1, name: 'miraje'},
{index:2, name: 'alamin'},
{index:3, name: 'behestee'},
{index:4, name: 'arif'},
{index:5, name: 'riad'}
];
when i delete an object like index: 2 , and that time i want to update my index value like ..
var item = [
{ index: 1, name: 'miraje'},
{ index: 2, name: 'behestee'},
{ index: 3, name: 'arif'},
{ index: 4, name: 'riad'}
];
After you remove element you can use forEach() loop to change indexes.
var item = [
{index:1, name: 'miraje'},
{index:2, name: 'alamin'},
{index:3, name: 'behestee'},
{index:4, name: 'arif'},
{index:5, name: 'riad'}
];
item.splice(1, 1)
item.forEach((e, i) => e.index = i + 1)
console.log(item)
Remove the object and alter the index property of each object,
DEMO
i=1;
var item = [
{index:1, name: 'miraje'},
{index:2, name: 'alamin'},
{index:3, name: 'behestee'},
{index:4, name: 'arif'},
{index:5, name: 'riad'}
];
console.log(item);
delete item[ 2 ];
console.log(item);
item.forEach(function(obj) {
obj.index = i;
debugger;
i++;
});
console.log(item);
Basically, you need to find the item with index, delete it, and to update all following items.
function deleteItem(array, index) {
var i = 0, found = false;
while (i < array.length) {
if (found) {
--array[i].index;
++i;
continue;
}
if (found = array[i].index === index) {
array.splice(i, 1);
continue;
}
++i;
}
}
var items = [{ index: 1, name: 'miraje' }, { index: 2, name: 'alamin' }, { index: 3, name: 'behestee' }, { index: 4, name: 'arif' }, { index: 5, name: 'riad' }];
deleteItem(items, 2);
console.log(items);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Just for a variety, without modifying the original array an efficient O(n) approach would also be as follows. We can also provide a range to delete the elements as well...
The range is provided supplied with the array indices, not the object index properties.
var item = [
{index:1, name: 'miraje'},
{index:2, name: 'alamin'},
{index:3, name: 'behestee'},
{index:4, name: 'arif'},
{index:5, name: 'riad'}
];
function deleteObjectsFromArray(a,j,k){
return a.reduceRight((p,c,i) => i >= j && i < k ? p
: i >= k ? (c.index -= k-j, p[c.index-1] = c, p)
: (p[c.index-1] = c, p),[]);
}
console.log(deleteObjectsFromArray(item,2,4));

Categories