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; }
Related
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
I have two array as below
const l1 = [ {"a": 1, "a1": 2}, {"b": 3, "b1": 4}, {"c": 5, "c1": 6} ];
const l2 = [ {"d": 2}, {"e": 2}, {"f": 2} ];
How can I use ramda library to combine these two and get result like
[ {"a": 1, "a1": 2, "d": 2}, {"a": 1, "a1": 2, "e": 2}, {"a": 1, "a1": 2, "f": 2},
{"b": 3, "b1": 4, "d": 2}, {"b": 3, "b1": 4, "e": 2}, {"b": 3, "b1": 4, "f": 2},
{"c": 5, "c1": 6, "d": 2}, {"c": 5, "c1": 6, "e": 2}, {"c": 5, "c1": 6, "f": 2} ]
I used
R.map(R.xprod(__, l2, l1)
But not working since i got object inside array.
Thanks in advance.
To get a Cartesian product of two arrays of objects you can create a function by lifting merge:
const fn = R.lift(R.merge)
const l1 = [ {"a": 1, "a1": 2}, {"b": 3, "b1": 4}, {"c": 5, "c1": 6} ];
const l2 = [ {"d": 2}, {"e": 2}, {"f": 2} ];
const result = fn(l1, l2)
console.log(result)
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.0/ramda.js" integrity="sha256-buL0byPvI/XRDFscnSc/e0q+sLA65O9y+rbF+0O/4FE=" crossorigin="anonymous"></script>
If not very particular about ramda, you can do as below with one line using flatMap and map.
const l1 = [
{ a: 1, a1: 2 },
{ b: 3, b1: 4 },
{ c: 5, c1: 6 }
];
const l2 = [{ d: 2 }, { e: 2 }, { f: 2 }];
const merged = l2.flatMap(item2 => l1.map(item1 => ({ ...item1, ...item2 })));
console.log(JSON.stringify(merged));
With plain Javascript, you could take cartesian product with a double nested reduce by mapping new objects.
This approach works for an arbirary count of arrays (sort of ...).
const
l1 = [{ a: 1, a1: 2 }, { b: 3, b1: 4 }, { c: 5, c1: 6 }],
l2 = [{ d: 2 }, { e: 2 }, { f: 2 }],
result = [l1, l2].reduce((a, b) => a.reduce((r, v) => r.concat(b.map(w => ({ ...v, ...w }))), []));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
There is another option to merge arrays using R.reduce function
const input = {
arr1: ['a', 'b'],
arr2: ['c', 'd']
}
R.compose(
R.reduce((acc, cur) => {
return [...acc, ...cur]
}, []),
R.map(([key, value]) => value),
R.toPairs
)(input)
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)
}
})
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)))
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.