I have an array of objects and I want to add an element to specific index when a certain attribute changes compared to the previous one.
We have:
const arr = [
{ num: 1 },
{ num: 1 },
{ num: 1 },
{ num: 3 },
{ num: 3 },
{ num: 4 },
{ num: 5 },
];
I want it to become
const arr = [
{ separator:true }
{ num: 1 },
{ num: 1 },
{ num: 1 },
{ separator:true }
{ num: 3 },
{ num: 3 },
{ separator:true }
{ num: 4 },
{ separator:true }
{ num: 5 },
];
I did this:
const getIndexes = (myArr) => {
let indexes = [];
let previousValue = null;
myArr.forEach((el, idx) => {
if (el.num !== previousValue) {
indexes.push(idx);
previousValue = el.num;
}
});
return indexes;
};
const insertSeparator = (arr) => {
let result = arr;
getIndexes(arr).forEach((position) => result.splice(position, 0, { separator: true }));
return result
};
and it returns:
[
{ separator: true },
{ num: 1 },
{ num: 1 },
{ separator: true },
{ num: 1 },
{ separator: true },
{ separator: true },
{ num: 3 },
{ num: 3 },
{ num: 4 },
{ num: 5 }
]
Maybe because of the "new" size of the array, because it is getting bigger and changes its dimension.
What do you think is the best way to solve this?
I propose this solution which would consume only one iteration with a reduce :
const arr = [{
num: 1
},
{
num: 1
},
{
num: 1
},
{
num: 3
},
{
num: 3
},
{
num: 4
},
{
num: 5
},
];
let prev_value = arr[0];
const result = arr.reduce((acc, val) => {
const insert = (val.num !== prev_value.num) ? [{
separator: true
}, val] : [val];
prev_value = val;
return acc.concat(insert)
}, [{
separator: true
}, ])
console.log(result)
Run it through .flatMap()
const result = arr.flatMap((obj, idx, arr) => {...
.flatMap() is .map() and .flat() combined, so it transforms the contents of a copy of the given array and removes the brackets []. Next, we return the first object with a separator:
if (idx == 0) {
// returns are wrapped in brackets because they'll be removed before being returned
return [{separator: true}, obj];
}
The next step is to compare the current value with the previous value:
obj.num == arr[idx - 1].num ? // current value vs previous value
[arr[idx - 1]] : // if they are the same value return previous value
[{separator: true}, obj]; /* if they are not the same then return that separator
and current */
const arr = [
{ num: 1 },
{ num: 1 },
{ num: 1 },
{ num: 3 },
{ num: 3 },
{ num: 4 },
{ num: 5 },
];
const result = arr.flatMap((obj, idx, arr) => {
if (idx == 0) {
return [{
separator: true
}, obj];
}
return obj.num == arr[idx - 1].num ? [arr[idx - 1]] : [{
separator: true
}, obj];
});
console.log(JSON.stringify(result, null, 2));
There must be other ways to do it too. But with a simple modification to your code it can be done. You just need to keep track of the offset with a new variable, incrementing it in the loop:
const arr = [
{ num: 1 },
{ num: 1 },
{ num: 1 },
{ num: 3 },
{ num: 3 },
{ num: 4 },
{ num: 5 },
];
const getIndexes = (myArr) => {
let indexes = [];
let previousValue = null;
myArr.forEach((el, idx) => {
if (el.num !== previousValue) {
indexes.push(idx);
previousValue = el.num;
}
});
return indexes;
};
const insertSeparator = (arr) => {
let result = [...arr];
let offset = -1;
getIndexes(arr).forEach((position) => {
offset++;
return result.splice(position+offset, 0, { separator: true });
});
return result
};
console.log(insertSeparator(arr));
Note: If you want to start with 0 you can do the increment in the .splice() itself : result.splice(position+(offset++),