Related
I'm solving a problem 1822 from leetcode and I'm stuck with reduce() returning NaN.
There is a function signFunc(x) that returns:
1 if x is positive.
-1 if x is negative. 0 if x is equal to 0. You are given an integer array nums. Let product be the product of all values in the array
nums.
Return signFunc(product).
My try of solution:
var arraySign = function(nums) {return Math.sign(nums.reduce((acc, curr) => acc * curr));};
console.log(arraySign([1,5,0,2,-3]));
When I pass simple array like [1,5,0,2,-3] everything is okay, but when I pass monster like
[1,28,-91,-62,-36,-1,-84,-90,-92,61,6,-58,-60,2,51,-15,-18,-81,87,84,100,-84,-13,-87,-33,72,-72,-59,-79,28,-69,-97,-93,17,67,11,-12,19,5,42,-85,71,-77,-82,26,-58,-51,-14,63,-85,-86,66,47,57,-86,-25,-75,59,-17,-71,89,-78,-42,30,39,61,-96,-30,-29,-92,-90,69,20,65,32,92,44,-71,-18,87,-86,23,-96,-21,-49,-96,-98,35,-11,-1,81,-48,68,5,75,57,-30,-7,32,86,-29,-86,-61,45,-31,-93,-26,-9,86,57,-52,75,68,-58,14,27,-94,47,-38,-44,75,-83,21,-83,43,62,74,97,78,43,54,28,64,-19,-89,88,68,18,-96,-83,-25,-71,88,-84,-24,-61,72,-90,-56,29,46,56,51,16,66,-2,65,-95,16,51,42,61,99,89,-93,59,-99,69,26,-61,21,41,40,-4,-49,3,-96,57,65,72,-41,-77,-4,61,71,-88,21,-95,38,64,92,0,-63]
var arraySign = function(nums) {return Math.sign(nums.reduce((acc, curr) => acc * curr));};
console.log(arraySign([1,28,-91,-62,-36,-1,-84,-90,-92,61,6,-58,-60,2,51,-15,-18,-81,87,84,100,-84,-13,-87,-33,72,-72,-59,-79,28,-69,-97,-93,17,67,11,-12,19,5,42,-85,71,-77,-82,26,-58,-51,-14,63,-85,-86,66,47,57,-86,-25,-75,59,-17,-71,89,-78,-42,30,39,61,-96,-30,-29,-92,-90,69,20,65,32,92,44,-71,-18,87,-86,23,-96,-21,-49,-96,-98,35,-11,-1,81,-48,68,5,75,57,-30,-7,32,86,-29,-86,-61,45,-31,-93,-26,-9,86,57,-52,75,68,-58,14,27,-94,47,-38,-44,75,-83,21,-83,43,62,74,97,78,43,54,28,64,-19,-89,88,68,18,-96,-83,-25,-71,88,-84,-24,-61,72,-90,-56,29,46,56,51,16,66,-2,65,-95,16,51,42,61,99,89,-93,59,-99,69,26,-61,21,41,40,-4,-49,3,-96,57,65,72,-41,-77,-4,61,71,-88,21,-95,38,64,92,0,-63]));
I got only "NaN" return. At this moment I skipped this bug by if's:
var arraySign = function(nums) {
let product = nums.reduce((acc, curr) => acc * curr);
if(product>0) return 1;
else if (product<0) return -1;
else return 0;};
But i want to know: why reduce is not working in this problem? Is there any limit for reduce()?
The problem is not with reduce. The problem is that you're multiplying a lot of numbers, which amounts to Infinity (or -Infinity), and then you're also multiplying that by 0. But Infinity * 0 gives NaN.
You can solve this by only multiplying the signs of the numbers:
function arraySign(nums) {
return nums.reduce((acc, curr) => acc * Math.sign(curr), 1);
};
Off Topic: But you can solve this by simply counting the number of negative values and looking for zeroes.
Every 2 negative values, the signs will cancel each other out and the result becomes positive.
an even number of negative values will have a positive result
an odd number of negative values will have a negative result
and the positive values in the list have no influence whatsoever on the sign of the result.
const arraySign = function(nums) {
// if the array contains a `0` the result will be `0`.
// 0 * whatever will always stay 0
if (nums.includes(0)) return 0;
let count = 0;
for (const value of nums) count += value < 0;
return count & 1 ? -1 : 1;
}
const nums = [1, 28, -91, -62, -36, -1, -84, -90, -92, 61, 6, -58, -60, 2, 51, -15, -18, -81, 87, 84, 100, -84, -13, -87, -33, 72, -72, -59, -79, 28, -69, -97, -93, 17, 67, 11, -12, 19, 5, 42, -85, 71, -77, -82, 26, -58, -51, -14, 63, -85, -86, 66, 47, 57, -86, -25, -75, 59, -17, -71, 89, -78, -42, 30, 39, 61, -96, -30, -29, -92, -90, 69, 20, 65, 32, 92, 44, -71, -18, 87, -86, 23, -96, -21, -49, -96, -98, 35, -11, -1, 81, -48, 68, 5, 75, 57, -30, -7, 32, 86, -29, -86, -61, 45, -31, -93, -26, -9, 86, 57, -52, 75, 68, -58, 14, 27, -94, 47, -38, -44, 75, -83, 21, -83, 43, 62, 74, 97, 78, 43, 54, 28, 64, -19, -89, 88, 68, 18, -96, -83, -25, -71, 88, -84, -24, -61, 72, -90, -56, 29, 46, 56, 51, 16, 66, -2, 65, -95, 16, 51, 42, 61, 99, 89, -93, 59, -99, 69, 26, -61, 21, 41, 40, -4, -49, 3, -96, 57, 65, 72, -41, -77, -4, 61, 71, -88, 21, -95, 38, 64, 92, 0, -63];
console.log(arraySign([1,5,2,-3]));
console.log(arraySign(nums));
Hard to put into words but I am looking for a function that takes an array and reduces it down to a given size. The use case is specifying ticks for graph in d3.
const availableDatetimes: Date[] = [...]
const numberOfXTicks = chartWidth / 80
const tickValues = reduceArrSize(availableDatetimes, numberOfXTicks)
the result should have evenly distributed dates
I called it shrinkArray(), because reducing an array is the name of a different well-defined operation. Here it is:
const shrinkArray = (array, size) => {
const step = array.length / size
return array.filter((v, i) => Math.floor(i % step) == 0)
}
Let's shrink an array of size 100 to size 33:
const originalArray = (new Array(100).fill(0)).map((v, i) => i)
const shrunkenArray = shrinkArray(originalArray, 33)
console.log(shrunkenArray);
console.log(shrunkenArray.length == 33);
So:
[
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71,
72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83,
84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
96, 97, 98, 99
]
is shrunken to:
[
0, 4, 7, 10, 13, 16, 19, 22, 25,
28, 31, 34, 37, 40, 43, 46, 49, 53,
56, 59, 62, 65, 68, 71, 74, 77, 80,
83, 86, 89, 92, 95, 98
]
that is of size 33.
In this example the array is integers from 0 to 99, but obviously the function works for any data type.
I need help with the distribution of routes by day of the month.
The situation is as follows. There are, for example, 3 zones, each zone has routes (not always the same number).
The task is to distribute routes by weekdays in a month, but there is a condition: routes from two NOT zones can be assigned for one day, zones must not overlap.
Example:
Routes of 1 zone: *1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21 , 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43; *
Routes 2 zones: 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64 , 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79;
Routes 3 zones: 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100 , 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120;
It turns out 120 routes (for example) per month. Let's say there are 20 days in a month, respectively, we divide 120 by 20, we get 6 routes per day.
As a result, we should get the distribution of routes by working days of the week in the following form:
1-6,7-12,13-18,19-24,25-30,31-36,37-43,44-49,50-55,56-61,62-67,68-73,74-79,80-85,86-91,92-97,98-103,104-109,110-115,116-120
As is clear from the number of "number-number" blocks (from which route by which), this is distributed over 20 days.
You can also see that the routes do not intersect zones, i.e. in 1 day there is no such thing:
"43-49", because route 43 is from the 1st zone, and the rest from the 2nd.
As a result, I began to write an algorithm to distribute everything and I could not think of anything better (so far) how to create a two-dimensional array, i.e. an array in which there are arrays with zones, these arrays have route numbers.
Then I wrote a function that distributes these routes without mixing them between zones by recursion and creating a THREE-DIMENSIONAL array.
Then I print the first and last element of each array and get, for example, a string:
'1-6,7-12,13-18,19-24,25-30,31-36,37-43,44-49,50-55,56-61,62-67,68-73,74-79,80-85,86-91,92-97,98-103,104-109,110-115,116-120';
P.S. I had to fix it a little, because it was the case that depending on the number of working days in a week, there could be 1 routes per day, which is bad, so I added a feature that iterates over an array of arrays and if the length of the nested is less than or equal to three (the number of routes is less than or equal to 3m), then we put it in the previous day (as it were)
In theory, everything works, but there is a huge BUT. It does not always work adequately due to the different number of working days in a month. For example: In a month there are 22 working days, and the algorithm divides everything in total into 19 blocks, which corresponds to 19 days, or if 17 working days, then divides into 21 blocks, which is equal to 21 days (BUT THIS IS ONLY AN EXAMPLE, real the numbers can be seen when testing my code).
Can you please tell me how I can correct the code so that the number of allocated blocks is strictly equal to the number of working days (does not depend on the number of working days)?
// Array a - routs
const a = [
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43],
[44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79],
[80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120]
];
const days = 20;
const countRoutsInDay = Math.floor(a.flat().length / days);
const newArr = [];
const resultArr = [];
let str = '';
// Divide into arrays by day
const splitterRouts = (count) => {
let arr = [];
let currentElemOfMainArray = count;
for (let i = 0; i < Math.ceil(a[count].length / countRoutsInDay); i++) {
arr[i] = a[count].slice((i * countRoutsInDay), (i * countRoutsInDay) + countRoutsInDay);
if (i === Math.ceil(a[count].length / countRoutsInDay) - 1) {
currentElemOfMainArray += 1;
newArr.push(arr);
if (currentElemOfMainArray < a.length) {
splitterRouts(currentElemOfMainArray);
}
}
}
}
splitterRouts(0);
// If there are less than or equal to 3 routes, then connect on the previous day
newArr.forEach((el) => {
el.forEach((el2) => {
if (el2.length <= 3) {
const summRoute = el[el.length - 2].concat(el[el.length - 1]);
el.splice(el.indexOf(el[el.length - 2]), el.indexOf(el[el.length - 1]), summRoute);
}
})
});
newArr.forEach((el) => {
el.forEach((el2) => {
str += `${el2[0]}-${el2[el2.length - 1]},`
resultArr.push(str.split(','));
})
})
console.log(str);
console.log(resultArr.length - 1); // this result must be equal to the number of days given above
The answer depends a bit on one decision: what should happen to zone 3 (41 routs) if there are 22 days? Should it be split into 6 days of 5 routs and one day of 11 routs? Or should the routs be evenly distributed with six days of 7 routs and one day of 6 routs? If it's the former, then you'll need to change the bit of code regarding "less than or equal to 3" to append any excess routs to the last day for that zone. Assuming you want the days more distributed more evenly, this solution accomplishes the second case:
First, days are distributed across all the zones (with the biggest zones getting any remainder). Next, within each zone, routs are distributed across days (with the later days getting more routs):
// Array a - routs
const a = [
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43],
[44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79],
[80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120]
];
const days =20;
//evenly distributing the days among the zones, leaving any remaining days on the longest zone(s)
const countRoutsInDay2 = a.map((aInnerArr,i)=>[aInnerArr.length,i]).sort()/*.map(a=>a[1])*/.reverse();
const countRoutsInDay3 = [];
for (let i = 0; i < a.length; i++){
countRoutsInDay3[i] = Math.floor(days/a.length);
if (i >= (a.length - (days % (Math.floor(days/a.length))) )) {countRoutsInDay3[i]++};
}
countRoutsInDay3.reverse();
let countDaysForZone = [];
for (let i = 0; i < a.length; i++){
countDaysForZone[countRoutsInDay2[i][1]] = countRoutsInDay3[i];
}
const newArr = [];
const resultArr = [];
let str = '';
let countRoutsInDay = 5;
// similarly, dividing each zone into days as evenly as possible, with the longer days later
const splitterRouts = (count) => {
let arr = [];
let currentElemOfMainArray = count;
let routsOnThisDay = [];
let daysForThisArray = countDaysForZone[count];
for (let i = 0; i < daysForThisArray; i++){
routsOnThisDay[i] = Math.floor(a[count].length/daysForThisArray);
if (i >= (daysForThisArray - (a[count].length % daysForThisArray) )) {routsOnThisDay[i]++};
}
for (let i = 0; i < countDaysForZone[count]; i++) {
let routsSoFar = 0;
if (i==1){routsSoFar = routsOnThisDay[0];}
if (i>1){
routsSoFar = routsOnThisDay.slice(0,i).reduce((a,b)=>a+b);
}
arr[i] = a[count].slice(routsSoFar, routsSoFar + routsOnThisDay[i]);
if (i === countDaysForZone[count] - 1) {
currentElemOfMainArray += 1;
newArr.push(arr);
if (currentElemOfMainArray < a.length) {
splitterRouts(currentElemOfMainArray);
}
}
}
}
splitterRouts(0);
// If there are less than or equal to 3 routes, then connect on the previous day
newArr.forEach((el) => {
el.forEach((el2) => {
if (el2.length <= 3) {
const summRoute = el[el.length - 2].concat(el[el.length - 1]);
el.splice(el.indexOf(el[el.length - 2]), el.indexOf(el[el.length - 1]), summRoute);
}
})
});
newArr.forEach((el) => {
el.forEach((el2) => {
str += `${el2[0]}-${el2[el2.length - 1]},`
resultArr.push(str.split(','));
})
})
console.log(str);
console.log(resultArr.length - 1);
I am actually working on a 'ground metrics engine' for a game. What I basically do is copying the real map data into another associative array, and map it with some metrics attribute like fertility or humidity, here is a sample code:
this.metricsMap = {};
this.generatedFertilities = {};
for (var i = 0; i < this.groundMap.data.length; i++) {
this.generatedFertilities[i] = this.groundMap.data[i].map(function () {
// TODO
});
}
for (var i = 0; i < this.groundMap.data.length; i++) {
this.metricsMap[i] = this.generatedFertilities[i].map(function (res) {
return {
ownedBy: "",
fertility: res,
humidity: 50
}
});
}
The map data is as an associative array [32][32]. I want to generate the corresponding 'fertility matrix' with consistency and 'number waves', here is a big picture of what I want to try to make on a smaller associative array:
[68, 69, 70, 71, 72, 73, 74, 75, 74, 73],
[69, 70, 71, 72, 73, 74, 75, 74, 73, 72],
[70, 71, 72, 73, 74, 75, 74, 73, 72, 71],
[71, 72, 73, 74, 75, 74, 73, 72, 71, 60],
[72, 73, 74, 75, 74, 73, 72, 71, 60, 59],
[73, 74, 75, 74, 73, 72, 71, 60, 59, 58],
[74, 75, 74, 73, 72, 71, 60, 59, 58; 57],
[75, 74, 73, 72, 71, 60, 59, 58; 57, 56],
[74, 73, 72, 71, 60, 59, 58; 57, 56, 55],
[73, 72, 71, 60, 59, 58; 57, 56, 55, 54]
We can imagine more 'pattern' where there is more concentration in the middle of the matrix for example.
I tried a lot of things things like this :
var baseRand = Math.floor(Math.random() * 100) + 1;
for (var i = 0; i < this.groundMap.data.length; i++) {
var counter = 0;
this.generatedFertilities[i] = this.groundMap.data[i].map(function () {
counter++;
// think of i like the y axis and counter like the x axis
if(i % 8 == 0){
if(counter < 8){
return Math.abs(Math.floor(baseRand++));
} else {
return Math.abs(Math.floor(baseRand--));
}
} else {
if(counter > 8){
return Math.abs(Math.floor(baseRand--));
} else {
return Math.abs(Math.floor(baseRand++));
}
}
});
}
But this doesn't get me somewhere and I am in trouble with the math behind it. How would you guys design such an algorithm ?
I'am not sure if I understand your problem correctly. Are you tied to real map data?
If not maybe Perlin Noise is something that can help you. Such noise functions can easily be used for terrain generation in games. You just have to map the color values to height values.
I have the following code in a test case on jsPerf:
var arr = [0, 45, 96, 8, 69, 62, 80, 91, 89, 24, 6, 23, 49, 88, 26, 40, 87, 61, 83, 2, 60, 53, 43, 82, 67, 3, 65, 37, 42, 77, 73, 38, 9, 46, 75, 10, 63, 15, 47, 28, 79, 55, 59, 95, 11, 93, 70, 98, 25, 48, 30, 5, 72, 12, 84, 1, 29, 13, 50, 33, 19, 7, 31, 57, 32, 44, 74, 51, 35, 90, 86, 54, 4, 64, 92, 71, 22, 41, 16, 17, 27, 76, 39, 18, 99, 94, 36, 66, 85, 20, 21, 56, 34, 81, 14, 78, 68, 58, 97, 52];
Array.prototype.remove = function(from, to) {
var rest = this.slice((to || from) + 1 || this.length);
this.length = from < 0 ? this.length + from : from;
return this.push.apply(this, rest);
};
function quicksort( arr ) {
if ( arr.length <= 1 )
return arr;
var i = 0,
len = arr.length,
less = [],
greater = [],
random = Math.floor( Math.random() * len ),
pivot = arr[ random ];
arr.remove( random );
for ( ; i < len - 1; i++ ){
if ( arr[ i ] <= pivot )
less.push( arr[ i ] );
else
greater.push( arr[ i ] );
}
return quicksort( less ).concat( pivot, quicksort( greater ) );
};
If you copy that into your console and run quicksort( arr ), you'll see that it correctly returns a sorted array.
But for some reason, in this test case on jsPerf, my quicksort function seems to be returning only a single number ( as can be seen in 'Perparation Code Output' ). It also seems to be running way faster than it probably should.
Anybody ideas into what's going on would be greatly appreciated.
I think the problem is that you're calling that .remove() function on the original array, so it quickly strips it down to nothing. In other words, each initial call to the quicksort function removes an element.
When I make it create a copy of the array first, then it seems to work.