Usually when I need to find the max value of an array I use this very simple code:
var max = Math.max.apply(Math, array);
However, now I have a multidimensional array in which for each line I have an array with 5 columns. Is there a similar way to find the max value for a certain column?
Right now I'm doing:
var maxFunc = function(data){
var max = 0;
data.forEach(function(value){
max = Math.max(max, value[0]);
});
return max;
};
I was curious if there was a prettier/simpler way of doing this?
I would write it as such:
Math.max.apply(Math, array.map(v => v[0]));
The array.map will transform the original array based on your picking logic, returning the first item in this case. The transformed array is then fed into Math.max()
To avoid creating a new array, you can also reduce the array to a single value:
array.reduce((max, current) => Math.max(max, current[0]), -Infinity)
As you can see, we need to add the initial value of -Infinity, which is returned if the array is empty.
This is a great application for Array.prototype.reduce:
max = data.reduce(function(previousVal, currentItem, i, arr) {
return Math.max(previousVal, currentItem[0]);
}, Number.NEGATIVE_INFINITY);
This also avoids the bug in your code that would happen if all the values in data are less than 0. You should be comparing against Number.NEGATIVE_INFINITY rather than 0.
Alternatively, you could normalize the data before reducing to the max value:
max = data.map(function (d) {
return d[0];
}).reduce(function (p, c, i, arr) {
return Math.max(p, c);
});
Related
I'm trying to solve some 'hackerrank.com' coding challenges.
I'm stuck on this one:
You will be given an array of integers arr and a single integer k. You must create an array arr' of length k from elements of arr such that its unfairness is minimized.
Unfairness is defined as max(arr') - min(arr')
The function should return the minimum possible unfairness
My code works fine for the majority of test cases. However, in three of those test cases - the ones where the size of arr and k is particularly huge - fail due to an excession of the given time limit.
How can I optimize the performance of my code?
Thanks in advance
function maxMin(k, arr) {
// create array to push unfairness values to
var unfairnesses = [];
// sort given array in ascending order
arr.sort(function(a, b) {
return a - b;
});
// loop over sorted array
for(var i = 0; i < arr.length - k + 1; i++) {
// get array with the length of k at position i
var tempArr = arr.slice(i, i + k);
// determine Max and Min of the sliced array
var tempArrMax = Math.max(...tempArr);
var tempArrMin = Math.min(...tempArr);
// get unfairness of the sliced array
var thisUnfairness = tempArrMax - tempArrMin;
// push sliced-array-unfairness to unfairnesses array
unfairnesses.push(thisUnfairness);
}
// return minimal value of unfairnesses array
return Math.min(...unfairnesses);
}
The two first steps could be:
Your array is sorted. Thus there's no need to use Math.max and Math.min - the first element of a slice is the smallest, the last is the largest.
When you eliminate Math.max and Math.min calls, you can remove Array.prototype.slice call. Then you're left with a sort and a single pass over the array.
To sort the array you're already looping one time over the whole thing.
Then you're looping another time to figure out which one is max and which one is the minimum.
You're looping twice as you can only loop once if you do:
function minMax(array) {
const safeArray = array ?? []
// No max or min as array is empty
if(safeArray.length === 0)
return [undefined, undefined]
let max: number = Number.MIN_SAFE_INTEGER
let min: number = Number.MAX_SAFE_INTEGER
for(let item of safeArray) {
max = Math.max(item, max)
min = Math.min(item, min)
}
return [max, min]
}
So, I'm new to programming and I am having a bit of a trouble here. See, I wanted to write a small range function, like range(a,b) that would return an array with all numbers between a and b. So I googled and found this one:
const range = (min, max) => [...Array(max - min + 1).keys()].map(i => i + min);
This works perfectly fine, but I'm having a bit of trouble undertanding it, especially with the .keys() part. I thought .keys() was an Object function, that would return the key of a key/value pair of an object, but here it seems to me that it's been used in an Array.
What am I understanding wrong here?
Appreciate the help!
Arrays also have a keys method and it's central to this range function working as it should.
Here's how that works:
It creates an empty (sparse) array of the appropriate length (Array(max - min + 1)).
It gets an iterator for the valid indexes of that array (.keys()). Valid indexes in an arrays are the numbers 0 through the length of the array minus one. (Technically, in specification terms, the indexes of arrays are string property names like "0", "1", etc., but the keys iterator returns numbers because we typically use numbers to index into arrays and that's the more useful value to provide.)
It spreads the elements from that iterator out into an array ([...]).
It creates a new array by mapping each value in the array from #3, adding the min value to them (.map(i => i + min)).
Note that this creates multiple intermediate arrays and other objects. It's unlikely to matter, but it can be done much more efficiently. For instance, with Array.from as shown by Nina, or even just a simple loop:
const range = (min, max) => {
const result = [];
for (let n = min; n < max; ++n) {
result.push(n); // Or: `result[result.length] = n;` if you want to
// avoid the function call
}
return result;
};
const range = (min, max) => {
const result = [];
for (let n = min; n < max; ++n) {
result.push(n);
}
return result;
};
console.log(range(5, 10));
That's not nearly as cool-looking, though. :-D
But, I probably wouldn't have range return an array at all unless I know for sure that's the end product I'm always going to need. Instead, I'd have it return an iterator:
const range = function*(min, max) {
for (let n = min; n < max; ++n) {
yield n;
}
};
for (const value of range(0, 10)) {
console.log(value);
}
.as-console-wrapper {
max-height: 100% !important;
}
You can always spread it out into an array (or use it with Array.from) if you need an array.
Arrays have keys as well and returns an iterator for returning keys. This requires a spreading into an array or use a function which takes iterables and returns an array.
For this purpose Array.from is made - and this has a mapping function as well.
Instead of builing more than one array, you could take Array.from directly with an object with a length property and a mapping function.
const range = (min, max) => Array.from(
{ length: max - min + 1 },
(_, i) => i + min
);
console.log(...range(7, 9));
I am trying to sum up the numbers inside an array e.g. from here: https://jsfiddle.net/4r8dtxhz/12/
here is the Code:
var someObj = [{name:"hi", series: [1,2,10,4,5,6]},{name:"ho",series:[3,7,6,9,12,1,3,20,3,1]}]
for (var doc of someObj) {
this.min = doc.series.reduce((agg,val) => val < agg? val:agg, doc.series[0]);
this.max = doc.series.reduce((agg,val) => val > agg? val:agg, doc.series[0]);
}
console.log(max)
var test = Array.from(someObj.map((doc)=>doc.series)).reduce((accumulator, currentValue) => accumulator + currentValue);
console.log(typeof test)
console.log(test)
I was expecting the reduce function to sum up the numbers in the object series array... so I am wondering what goes wrong here?
Your map function is producing an two dimensional array of [someObj[0].series, someObj[1].series].
When you add two arrays together using the + operator in your reducer, it converts them to a string and concatenates the string.
If you want to create an array of the sum of each series, introduce another map function which has a reduce inside it.
You are missing a step to flatten the result of your map step. Your code
someObj.map((doc) => doc.series)
will return an array of arrays (or 2D array) rather than a flat array.
If you add a step to flatten the 2D array after your map step—for example by
.reduce((flattened, series) => flattened.concat(series))
using Array.reduce again—you will get the expected result.
Note that you should always provide an initial value for the accumulator of reduce (in your case 0 was missing for the summation) to assure that + is getting resolved correctly to number addition (otherwise it will be string concatenation, e.g. [1]+[2] === '12').
Also, Array.from wasn't necessary since someObj already is an array (Array.from converts from an Iterable object to Array).
var someObj = [{name:"hi", series: [1,2,10,4,5,6]},{name:"ho",series:[3,7,6,9,12,1,3,20,3,1]}]
for (var doc of someObj) {
this.min = doc.series.reduce((agg,val) => val < agg? val:agg, doc.series[0]);
this.max = doc.series.reduce((agg,val) => val > agg? val:agg, doc.series[0]);
}
console.log(max)
var test = someObj.map((doc)=>doc.series)
.reduce((flattened, series) => flattened.concat(series))
.reduce((accumulator, currentValue) => accumulator + currentValue, 0);
console.log(typeof test)
console.log(test)
I have a very simple question. Say I have an array
a = [10,40,30,20,60,50]
After sorting, it would be (assuming I use sort_a = a.sort())
sort_a = [60,50,40,30,20,10]
I want to create an array of indices from a which specify which location in the sorted array that element WILL BE after sorting. From the above example, the result would be
a_sortedindices = [6, 3, 4, 5, 1, 2]
..meaning 10 is in the 6th position when sorted, 40 is in the 3rd position... etc
Pair the values with their current indices
Sort the array of pairs based on the original values
Combine the pairs with their new indices
Sort the new array based on the original indices
Obtain the new indices from the sorted array
let values = [10,40,30,20,60,50];
let indices = values
.map((v, i) => ({ v, i }))
.sort((l, r) => r.v - l.v)
.map(({v, i}, i2) => ({ v, i, i2 }))
.sort((l, r) => l.i - r.i)
.map(p => p.i2);
console.log(indices);
This results in an array of 0-based indices because JavaScript uses 0-based indices. If you want 1-based indices like in your question, you can change p.i2 to p.i2 + 1 in the second to last line.
One of the ways, apart from many to achieve this:
1) Transform the array into another with old indices
2) Sort the array in descending order
3) Create an answer array since you now know the old and new indices.
let a = [10,40,30,20,60,50];
let transformed = a.map((v,i)=> {
return {num:v,old:i};
});
transformed.sort((a,b)=> {
return b.num - a.num;
});
let ans = [];
transformed.forEach((v,i) => {
ans[v.old] = i+1;
});
console.log(ans);
Not sure if this is a trick question or if you're trying to find the most minimal method for achieving this, but you basically already have it. This is what I came up with:
var a = [10,40,30,20,60,50];
var sort_a = a.slice(0).sort((a1,a2) => a2 - a1);
var a_sortedindices = a.map( a1 => sort_a.indexOf(a1) + 1 );
console.log(a_sortedindices);
Walking through it, I'll explain each part.
First, off you have to sort it. Looks like you need reverse sorting, so we'll add an arrow function describing a reverse sort, but before we do that, we'll also clone the array, otherwise we'll lose the original indexes of the values. .slice(0) is a nice way to return a clone of an array
var sort_a = a.slice(0).sort((a1,a2) => a2 - a1);
Then we'll map each value of the origin array. .map() is nice and easy to quickly manipulate each element in an array. We use .indexOf() to figure out where it was at in the original array. We add one to that value because you're not using zero-based indexing.
var a_sortedindices = a.map( a1 => sort_a.indexOf(a1) + 1 );
And voila. You have the sorted indexes.
A naive way of doing this job could be;
var arr = [10,40,30,20,60,50],
idx = arr.map(function(n){return this.indexOf(n)+1;}, arr.slice().sort((a,b) => b-a));
console.log(idx);
where the this argument for the .map() function callback is arr.slice().sort((a,b) => b-a)
// the array to be sorted
var list = [10,20,30,40];
// temporary array holds objects with position and sort-value
var mapped = list.map(function(el, i) {
return { index: i, value: el };
})
// sorting the mapped array
mapped.sort(function(a, b) {
return b.value - a.value;
});
// you can then remap the sorted mapped array to hold just the indices
P.S.: For future reference MDN is your friend
I am playing with reduce method provided by mozilla. Underscore provides its own version which seems very similar. Reducing an array is very simple.
ArrayName.reduce(function(a,b) {
return a +b;
});
I use a very similar approach when sorting arrays as well. However, what I do not understand is how to return a list of calls. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce Provides a really cool table that shows how their method works. I set my array pretty much the same
var array = [1,2,3,100,55,88,2];
var sortArray= function(a,b) {
return a -b;
}
var sorted = array.sort(sortArray);
var reduced = sorted.reduce(function(previousValue,currentValue, index,array){
return previousValue + currentValue;
});
What I wanted to set up though was each call that was made. I figured that I could simply reference the index in the return value with a , at the end.
return previousValue + currentValue, index;
However, that only returns the index. I am trying to figure a way to get the value of each call. Does this approach allow you to get each call?
You don't need to use reduce for what you are doing. Try
function running_total(array) {
var sum = 0;
return array.map(function(elt) {
return sum += elt;
};
}
Reduce is about "boiling down" an array to a "single thing". In contrast, map is about "transforming" elements of the array. Your problem (as I understand it) is to transform each element of the array into the sum of itself and all the preceding elements. Therefore, conceptually, it's a map, not a reduce.
Using the , in the return statement will only return index due to the comma operator rule
[0, 1, 2, 3, 4].reduce(function(previousValue, currentValue, index, array) {
console.log(previousValue+"--"+currentValue)
return previousValue + currentValue;
});
this will do want you want
You can create another array and store its value for each progress.
var array = [1,2,3,100,55,88,2];
var sortArray= function(a,b) {
return a -b;
}
var sorted = array.sort(sortArray);
var progress = [];
var reduced = sorted.reduce(function(previousValue,currentValue, index,array){
progress[index] = previousValue + currentValue;
return progress[index];
}, 0);
// ^^^^^
// give it a init value so it starts at first element.
console.log(progress);
However, as torazaburo says, as you expect to get an array, you should use .map which returns an array instead of .reduce.