How to add recurring object elements in javascript - 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

Related

Transform array of objects into array of objects with an extra property with map, filter, reduce

I have the following array of objects:
[
{id: 1, updatedVersion: 7},
{id: 2, updatedVersion: 0},
{id: 7, updatedVersion: 9},
{id: 10, updatedVersion: 11},
{id: 9, updatedVersion: 0},
{id: 11, updatedVersion: 0},
]
Those with updatedVersion equal to zero are the latest versions.
From that array I was able to filter the ones that are the latest version and have at least one previous Version,
which are objects with id 9, and 11. (Object with id === 2 is not included since it's the latest version but has no previous versions).
What I need now is to create an extra property in those two objects with id 9 and 11 (again: the objects that are latest version and have at least one previous version),
called versionHistory, which would be an ordered array of previous versions of that object, from most recent to oldest.
So it would look something like that:
[
{ id: 9,
updatedVersion: 0,
versionHistory: [
{id: 7, updatedVersion: 9},
{id: 1, updatedVersion 7}
]
},
{
id: 11,
updatedVersion: 0,
versionHistory: [
{id: 10, updatedVersion: 11}
]
}
]
I came up with an ugly solution that involves nested for loops but I'm looking for something more elegant that uses map, filter and reduce. I would appreciate any hints on how to approach this.
This isn't terribly efficient but hopefully meets the "elegant" requirement ;)
data = [
{id: 1, updatedVersion: 7},
{id: 2, updatedVersion: 0},
{id: 7, updatedVersion: 9},
{id: 10, updatedVersion: 11},
{id: 9, updatedVersion: 0},
{id: 11, updatedVersion: 0},
]
let prev = node => data.find(n => n.updatedVersion === node.id);
let path = node => node ? [node, ...path(prev(node))] : [];
let result = data.filter(n => n.updatedVersion === 0).map(node => ({
versionHistory: path(node).slice(1),
...node,
}));
console.log(result);

Printing objects of one value based on another value

I want to console.log a value within an array of objects based on another specific value. For example, I have a simple array of objects. For all of the objects with v: 1, I want to print the z value.
var array = [
{v:1, z: 4},
{v:3, z: 8},
{v:4, z: 6},
{v:1, z: 4},
{v:2, z: 9},
{v:2, z: 3},
{v:4, z: 7},
{v:1, z: 5},
];
I tried something like for (array.v(1) => { console.log(array.z); }); but the syntax isnt correct. What is the correct syntax here?
Try something like this:
array.forEach( function(a) { if ( a.v == 1 ) console.log(a.z); } );
You need to add a if statement to your for each loop
Also you can use a filter function but this will print the object that meet your filter, not only the z value
var array = [
{v:1, z: 4},
{v:3, z: 8},
{v:4, z: 6},
{v:1, z: 4},
{v:2, z: 9},
{v:2, z: 3},
{v:4, z: 7},
{v:1, z: 5},
];
console.log('For Each')
array.forEach(o=>{ if(o.v == 1)console.log(o.z)})
console.log('Filter')
console.log(JSON.stringify(array.filter(o=>o.v==1)))

Extract values from JavaScript object and push into array in the correct order

I'm trying to fill an empty array with interest rate variables extracted from an object. The solution is incorrect because the order of values is not correct i.e Year 2015: 10th month's value, 11th, 12th, 1st, 2nd, 3rd ..9th . Then 2016: 10, 11, 12, 1,..7,8,9 etc . In other words it always extracts the 10th value first, not the 1st value. Below is sample data and my code.
Sample Data:
2015:
01:{interestrate: 3.67, count: 4}
02:{interestrate: 3.71, count: 4}
03:{interestrate: 3.7699999999999996, count: 4}
04:{interestrate: 3.6720000000000006, count: 5}
05:{interestrate: 3.84, count: 4}
06:{interestrate: 3.9825, count: 4}
07:{interestrate: 4.046, count: 5}
08:{interestrate: 3.905, count: 4}
09:{interestrate: 3.8899999999999997, count: 4}
10:{interestrate: 3.7959999999999994, count: 5}
11:{interestrate: 3.9425, count: 4}
12:{interestrate: 3.964, count: 5}
2016:
01:{interestrate: 3.8725000000000005, count: 4}
02:{interestrate: 3.66, count: 4}
03:{interestrate: 3.6940000000000004, count: 5}
04:{interestrate: 3.605, count: 4}
05:{interestrate: 3.6, count: 4}
06:{interestrate: 3.568, count: 5}
07:{interestrate: 3.4400000000000004, count: 4}
08:{interestrate: 3.435, count: 4}
09:{interestrate: 3.46, count: 5}
10:{interestrate: 3.47, count: 4}
11:{interestrate: 3.7699999999999996, count: 4}
12:{interestrate: 4.198, count: 5}
Code:
let arr = [];
for (x in result) {
for (i in result[x]) {
//console.log(result[x][i]['interestrate']);
arr.push(result[x][i]['interestrate']);
}
}
How can I rewrite code so that I get the interestrate values in this order 2015: 1,2,3,4,5,6,7,8,9,10,11,12. Then 2016: 1,2,3,4,5,6,7,8,9,10,11,12 Then 2017: etc.
Example output: [3.67, 3.71, 3.769, 3.672, etc]
Dictionaries (objects in Javascript works like dictionaries) are not sorted, so you must sort the keys before entering the loop.
Sample data:
let result = {
2015: {
1:{interestrate: 3.67, count: 4},
2:{interestrate: 3.71, count: 4},
3:{interestrate: 3.7699999999999996, count: 4},
4:{interestrate: 3.6720000000000006, count: 5},
5:{interestrate: 3.84, count: 4},
6:{interestrate: 3.9825, count: 4},
7:{interestrate: 4.046, count: 5},
8:{interestrate: 3.905, count: 4},
9:{interestrate: 3.8899999999999997, count: 4},
10:{interestrate: 3.7959999999999994, count: 5},
11:{interestrate: 3.9425, count: 4},
12:{interestrate: 3.964, count: 5}},
2016: {
1:{interestrate: 3.8725000000000005, count: 4},
2:{interestrate: 3.66, count: 4},
3:{interestrate: 3.6940000000000004, count: 5},
4:{interestrate: 3.605, count: 4},
5:{interestrate: 3.6, count: 4},
6:{interestrate: 3.568, count: 5},
7:{interestrate: 3.4400000000000004, count: 4},
8:{interestrate: 3.435, count: 4},
9:{interestrate: 3.46, count: 5},
10:{interestrate: 3.47, count: 4},
11:{interestrate: 3.7699999999999996, count: 4},
12:{interestrate: 4.198, count: 5}
}};
Code:
// If you just use sort(), the order will be wrong, because it will
// be sort alphabetically, and 11 would appear before 2.
function compareNumbers(a, b)
{
return a - b;
}
let arr = [];
Object.keys(result).sort(compareNumbers).forEach(function(year) {
Object.keys(result[year]).sort(compareNumbers).forEach(function(month) {
arr.push(result[year][month]['interestrate']);
});
});
console.log(arr);

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

Shifting array of elements into another index of the array

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.

Categories

Resources