Shifting array of elements into another index of the array - javascript

Not sure if this may be a duplicate, but I am having some troubles trying to think of the best way of shifting an element in an array filled of arrays of elements.
Such as:
var foo = [
[ {obj: 1}, {obj: 2}, {obj: 3}, {obj: 4} ],
[ {obj: 5}, {obj: 6}, {obj: 7}, {obj: 8} ],
[ {obj: 9}, {obj: 10}, {obj: 11}, {obj: 12} ]
];
If I remove one element given an arrayIndex, it would remove that element then shift all of the proceeding elements down to the appropriate array. Such as if I remove obj 3 the result would be:
var arrayIndex = 0;
var objIndex = 2;
var bar = foo[arrayIndex].splice(objIndex, 1);
Result:
bar = [
[ {obj: 1}, {obj: 2}, {obj: 4}, {obj: 5} ],
[ {obj: 6}, {obj: 7}, {obj: 8}, {obj: 9} ],
[ {obj: 10}, {obj: 11}, {obj: 12} ]
];
Another example would be as shown removing obj 8:
var arrayIndex = 1;
var objIndex = 3;
var bar = foo[arrayIndex].splice(objIndex, 1);
Result:
bar = [
[ {obj: 1}, {obj: 2}, {obj: 3}, {obj: 4} ],
[ {obj: 5}, {obj: 6}, {obj: 7}, {obj: 9} ],
[ {obj: 10}, {obj: 11}, {obj: 12} ]
];
The issue for me is shifting all of the proceeding elements into the correct array position. Additionally, I would like the empty array to be removed. Where foo's length would decrease. foo will also be mutated.
Here was my attempted jsfiddle: https://jsfiddle.net/mLw8kncn/1/
Any help would be appreciated.

A simple way is to store your items in 1D array instead of 2D. Then manipulate the indexes.
var foo = [ {obj: 1}, {obj: 2}, {obj: 3}, {obj: 4},
{obj: 5}, {obj: 6}, {obj: 7}, {obj: 8},
{obj: 9}, {obj: 10}, {obj: 11}, {obj: 12} ];
function remove(arrayIndex, objIndex) {
var realIndex = arrayIndex * 4 + objIndex;
foo.splice(realIndex, 1);
}
Otherwise, you have to rearrange items after every splice.
function remove(arrayIndex, objIndex) {
foo[arrayIndex].splice(objIndex, 1);
for (var i = arrayIndex + 1; i < foo.length; i++) {
var obj = foo[i].shift();
foo[i - 1].push(obj);
}
if (foo[foo.length - 1].length <= 0) {
foo.pop();
}
}
And this is much complicated.

You could use a function that flattens the array temporarily to 1 dimension (keeping record of the original sizes of the sub arrays), then applies the standard splice on that, and finally reconstructs the 2D array based on the recorded size information.
This will have as benefit you can use all the power of splice to delete more than one element at a time and/or insert other elements in the same operation.
The given index must therefore be the index as if the input array were 1-dimensional:
function splice2d(a, start, deleteCount /*, item1, item2, ...*/){
var flat = [], sizes = [];
// Make a flat array, keeping record of subarray sizes
while (a.length) {
sizes.push(a[0].length);
flat = flat.concat(a.shift());
};
// Apply original splice to flat array
[].splice.apply(flat, [].slice.call(arguments, 1));
// Reconstruct 2D array again based on sizes
while (sizes.length) {
a.push(flat.splice(0, sizes.shift()));
}
return a;
}
// Sample data
var foo =
[[{obj: 1}, {obj: 2}, {obj: 3}, {obj: 4}],
[{obj: 5}, {obj: 6}, {obj: 7}, {obj: 8}],
[{obj: 9}, {obj: 10}, {obj: 11}, {obj: 12}]]
// Mutate
splice2d(foo, 2, 1);
// Output result
console.log(foo);

I guess Array.prototype.reduce() is ideal for this job. You may do it like
var foo = [[{obj: 1}, {obj: 2}, {obj: 3}, {obj: 4}],
[{obj: 5}, {obj: 6}, {obj: 7}, {obj: 8}],
[{obj: 9}, {obj: 10}, {obj: 11}, {obj: 12}]
],
removeAndShift = (a,ai,oi) => ai == a.length-1 ? a[ai].splice(oi,1)
: a.reduce((p,c,i,a) => { if (i == ai+1) {
p.splice(oi,1);
p.push(c.shift());
}
i > ai+1 && p.push(c.shift());
return c;
});
removeAndShift(foo,1,3);
console.log(foo);
Note that we are not doing anything but simply splicing out the item to delete if the array item to remove an item from is at the very end.

Related

How to add recurring object elements in javascript

I had a question, I needed optimal options to solve this problem
Problem:
const laptops = [{acer: 4}, {mac: 5}, {asus: 8}, {acer: 6}, {mac: 3} ];
output: [{acer: 10}, {mac: 8}, {asus: 8}];
the answer must be dynamic for any array

combining two arrays in specific format

I am trying to combine two arrays into one in a specific format. Please guide me on how i can achieve this.
let arr = [{'test' : 1}, {'test1' : 2}, {'test2': 3}, {'test3': 4}];
let arr1 = [{'testdo': 5}, {'testdo1': 6}, {'testdo2': 7}, {'testdo3': 8}];
// the resulted array should look like this.
arr3 = [{'test': 1}, {'testdo': 5}, {'test1': 2}, {'testdo1': 6}, {'test2': 3}, {'testdo2': 7},
{'test3': 4}, {'testdo3': 8}];
I have tried this
for(let i = 0; i<=6; i++){
let arr3: any = [arr[i], arr1[i]];
console.log(arr3);
}
Should work with arrays of any length.
let arr = [{'test' : 1}, {'test1' : 2}, {'test2': 3}, {'test3': 4}];
let arr1 = [{'testdo': 5}, {'testdo1': 6}, {'testdo2': 7}, {'testdo3': 8}];
let arr3 = [];
let max = Math.max(arr.length, arr1.length);
for (let i=0; i < max; i++) {
if (arr.length > i) {
arr3.push(arr[i]);
}
if (arr1.length > i) {
arr3.push(arr1[i]);
}
}
console.log(arr3);
Probably need something like:
const arr = [{test: 1}, {test1: 2}, {test2: 3}, {test3: 4}];
const arr1 = [{testdo: 5}, {testdo1: 6}, {testdo2: 7}, {testdo3: 8}];
const arr3 = [...arr];
let offset = 0;
arr1.forEach((arr1Item, index) => {
if (index <= arr?.length) {
const targetIndex = index + 1 + offset;
const removeCount = 0;
arr3.splice(targetIndex, removeCount, arr1Item);
offset += 1;
} else {
arr3.push(arr1Item);
}
});
arr3 now has the shape you probably need.
I hope this helps you.

Lodash set every element of an array

Is there a way to use the string notation in the _.set method to match all items in a nested array?
(e.g. hopefully similar to MongoDB's positional all operator)
const doc = {
nested: [{a: 1}, {a: 2}, {a: 3}]
}
_.set(doc, "nested.$[].a", 5)
// console.log(doc)
// {
// nested: [{a : 5}, {a: 5}, {a: 5}]
// }
No, you can't set every element in a nested array with the .set() method, but you can do this instead:
const doc = {
nested: [{a: 1}, {a: 2}, {a: 3}]
}
// Your attempt.
// _.set(doc, "nested.$[].a", 5)
// Just use a .map() with .assign() instead.
const doc2 = _.assign({}, doc, {
nested: _.map(doc.nested, (obj) => _.assign({}, obj, { a: 5 }))
});
console.log(doc2)
// {
// nested: [{a : 5}, {a: 5}, {a: 5}]
// }
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.15/lodash.min.js"></script>

How can i get loop and filter at once in Javascript

const count = [
{number: 1},
{number: 2},
{number: 3},
{number: 4},
{number: 5},
{number: 6},
{number: 7},
{number: 8},
{number: 9},
{number: 10},
{number: 11},
{number: 12},
{number: 13},
{number: 14},
{number: 15}
];
for(let i = 0;i < count.length; i++) {
document.write(count[i].number);
}
If the main number what i want to get out the loop is 5. But i want to write out one above 5 and one under 5. So 456. How can i achieve that?
In order to filter out the number which you want you can use an if statement to check if the current object's number count[i].number is the value you want, and then if it is, print out its associated values.
You can access the object before and after by changing your value of i. Using i-1 you will get the element before your current value. Similarly, using i+1 you will go to the object after your current value.
Lastly, you should check the value of i to make sure that it falls within the array. For instance, if you chose to find the number 1 it has no before object, and so you need to check for this case!
See working example below:
const count = [
{number: 1},
{number: 2},
{number: 3},
{number: 4},
{number: 5},
{number: 6},
{number: 7},
{number: 8},
{number: 9},
{number: 10},
{number: 11},
{number: 12},
{number: 13},
{number: 14},
{number: 15}
];
const toFind = 5;
for(let i = 0; i < count.length; i++) {
if(count[i].number == toFind) {
if(i-1 >= 0) console.log(count[i-1].number);
console.log(count[i].number);
if(i+1 < count.length) console.log(count[i+1].number);
break; // complete loop cycle
}
}
Assuming the needle is unique, you can .filter the numbers you want, then .forEach to print them.
const needle = 5
const haystack = [
{number: 1},
{number: 2},
{number: 3},
{number: 4},
{number: 5},
{number: 6},
{number: 7},
{number: 8},
{number: 9},
{number: 10},
{number: 11},
{number: 12},
{number: 13},
{number: 14},
{number: 15}
]
haystack
.sort((a, b) => a.number - b.number) // skip this if haystack is already sorted.
.filter(el => [needle - 1, needle, needle + 1].includes(el.number))
.forEach(el => {
document.write(el.number)
})
Note that you have to guard against needle = 0 which has no -1 and needle = 15 which has no +1.
const count = [
{ number: 1 },
{ number: 2 },
{ number: 3 },
{ number: 4 },
{ number: 5 },
{ number: 6 },
{ number: 7 },
{ number: 8 },
{ number: 9 },
{ number: 10 },
{ number: 11 },
{ number: 12 },
{ number: 13 },
{ number: 14 },
{ number: 15 }
];
var index = count.findIndex(item => item.number === 5);
document.write(count[index - 1].number);
document.write(count[index].number);
document.write(count[index + 1].number);
You can directly use the forEach and based on range you can write to document.
const count = [{number: 1},{number: 2},{number: 3},{number: 4},{number: 5},{number: 6},{number: 7},{number: 8},{number: 9},{number: 10},{number: 11},{number: 12},{number: 13},{number: 14},{number: 15}];
count.forEach(({number})=> {
if(number>3 && number<7){
document.write(number)
}
})

Finding duplicate max values in a matrix

What I'm working with is a matrix of objects and I'm trying to find the maximum values for each of the objects, including duplicates.
Here's what I have until now:
let findColumnMaxValue = (i) => {
let coord = [];
let maxValue = 0;
for (let j = 0; j < this.field.length; j++) {
if (this.field[i][j].dst > maxValue) {
maxValue = this.field[i][j].dst;
}
}
getMaxValueCoord(maxValue, coord, i);
return coord;
}
Up here I'm finding the maximum value for every row of each column.
let getMaxValueCoord = (max, a, i) => {
for (let j = 0; j < this.field.length; j++) {
if (this.field[i][j].dst === max) {
a.push({x: i, y: j})
}
}
}
and in this function, after finding the max, I'm comparing each row of each column to the max value and pushing the object coordinates into an array if it meets the condition.
findHighestDensityCells() {
let arr = [];
for (let i = 0; i < this.field.length; i++) {
arr.push(findColumnMaxValue(i));
}
return [].concat(...arr);
}
Now that I have an array of all the max object value coordinates for each column, I want this array to contain only the max values, including duplicates, basically repeating much of what I've done above.
What I've written above seems to take up too much code in order to solve this simple problem. Are there other methods I could use to help reduce the amount of code?
EDIT
The data is a simple object options = { dst: 0 } with a value that gets updated by another function. Therefore the rows within the columns all contain the above object, each with different values. So my matrix could look like this:
2 3 4 5 6 6 5 4 3 2
3 4 5 6 7 7 6 5 4 3
4 5 6 7 8 8 7 6 5 4
5 6 3 4 9 9 4 3 2 1
6 7 3 4 9 9 4 3 2 1
6 7 3 4 5 5 4 3 2 1
5 6 3 4 5 5 4 3 2 1
4 6 3 4 5 5 4 3 2 1
3 5 3 4 5 5 4 3 2 1
2 4 3 4 5 5 4 3 2 1
The desired result is getting all the maximum values within the matrix as coordinates including duplicates. In the example above this would be [9,9,9,9].
Check out some magic using Array.prototype.reduce(), arrow function expression, Math.max(), spread operator, Array.prototype.map(), Array.prototype.concat(), Array.prototype.filter():
const maxArray = matrix.reduce((maxArray, row, rowIndex) => {
const max = Math.max(0, ...row.map(e => e.dst));
return maxArray.concat(
row.map(
(e, i) => ({x: i, y: rowIndex, dst: e.dst})
).filter(e => e.dst === max)
);
}, []);
const maxOfAll = Math.max(0, ...maxArray.map(e => e.dst));
const filteredMaxArray = maxArray.filter(
e => e.dst === maxOfAll
).map(e => ({x: e.x, y: e.y}));
const matrix = [
[{dst: 2}, {dst: 3}, {dst: 4}, {dst: 5}, {dst: 6}, {dst: 6}, {dst: 5}, {dst: 4}, {dst: 3}, {dst: 2}],
[{dst: 3}, {dst: 4}, {dst: 5}, {dst: 6}, {dst: 7}, {dst: 7}, {dst: 6}, {dst: 5}, {dst: 4}, {dst: 3}],
[{dst: 4}, {dst: 5}, {dst: 6}, {dst: 7}, {dst: 8}, {dst: 8}, {dst: 7}, {dst: 6}, {dst: 5}, {dst: 4}],
[{dst: 5}, {dst: 6}, {dst: 3}, {dst: 4}, {dst: 9}, {dst: 9}, {dst: 4}, {dst: 3}, {dst: 2}, {dst: 1}],
[{dst: 6}, {dst: 7}, {dst: 3}, {dst: 4}, {dst: 9}, {dst: 9}, {dst: 4}, {dst: 3}, {dst: 2}, {dst: 1}],
[{dst: 6}, {dst: 7}, {dst: 3}, {dst: 4}, {dst: 5}, {dst: 5}, {dst: 4}, {dst: 3}, {dst: 2}, {dst: 1}],
[{dst: 5}, {dst: 6}, {dst: 3}, {dst: 4}, {dst: 5}, {dst: 5}, {dst: 4}, {dst: 3}, {dst: 2}, {dst: 1}],
[{dst: 4}, {dst: 6}, {dst: 3}, {dst: 4}, {dst: 5}, {dst: 5}, {dst: 4}, {dst: 3}, {dst: 2}, {dst: 1}],
[{dst: 3}, {dst: 5}, {dst: 3}, {dst: 4}, {dst: 5}, {dst: 5}, {dst: 4}, {dst: 3}, {dst: 2}, {dst: 1}],
[{dst: 2}, {dst: 4}, {dst: 3}, {dst: 4}, {dst: 5}, {dst: 5}, {dst: 4}, {dst: 3}, {dst: 2}, {dst: 1}],
];
const maxArray = matrix.reduce((maxArray, row, rowIndex) => {
const max = Math.max(0, ...row.map(e => e.dst));
return maxArray.concat(
row.map(
(e, i) => ({x: i, y: rowIndex, dst: e.dst})
).filter(e => e.dst === max)
);
}, []);
const maxOfAll = Math.max(0, ...maxArray.map(e => e.dst));
const filteredMaxArray = maxArray.filter(
e => e.dst === maxOfAll
).map(e => ({x: e.x, y: e.y}));
console.log(filteredMaxArray);
You could use a single function with only one loop over the outer array and inner array with a hash table for temporary max coordinates and with a collecting array for the result.
function getMax(data) {
return data.reduce(function (r, a, x) {
var hash = Object.create(null),
max = 0;
a.forEach(function (o, y) {
if (max <= o.dst) {
max = o.dst;
hash[max] = hash[max] || [];
hash[max].push({ x, y });
}
});
return r.concat(hash[max]);
}, []);
}
var data = [[{ dst: 1 }, { dst: 2 }, { dst: 3 }], [{ dst: 4 }, { dst: 5 }, { dst: 6 }], [{ dst: 7 }, { dst: 8 }, { dst: 9 }]]
console.log(getMax(data));
.as-console-wrapper { max-height: 100% !important; top: 0; }

Categories

Resources