How to distribute routes by day of the month? - javascript
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);
Related
Why reduce() is returning NaN when multiplying big array
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));
What's wrong with my code for leetcode 2246?
I am looking at LeetCode problem 2246. Longest Path With Different Adjacent Characters: You are given a tree (i.e. a connected, undirected graph that has no cycles) rooted at node 0 consisting of n nodes numbered from 0 to n - 1. The tree is represented by a 0-indexed array parent of size n, where parent[i] is the parent of node i. Since node 0 is the root, parent[0] == -1. You are also given a string s of length n, where s[i] is the character assigned to node i. Return the length of the longest path in the tree such that no pair of adjacent nodes on the path have the same character assigned to them. This is my code: function longestPath(parent: number[], s: string): number { const childrenMap: Record<number, number[]> = {} for (let node = 0; node < parent.length; node++) { const parentNode = parent[node] childrenMap[parentNode] = childrenMap[parentNode] ?? [] childrenMap[parentNode].push(node) } let max = 0 const dfs = (node = 0, parentNode = -1): number => { if (s[node] === s[parentNode]) { max = Math.max(max, dfs(node, -1)) return 0 } const [a = 0, b = 0] = (childrenMap[node] ?? []).map((child) => dfs(child, node)) .sort((la, lb) => lb - la) max = Math.max(max, 1 + a + b) return a + 1 } dfs() return max } console.log( longestPath( [-1, 56, 10, 79, 52, 0, 37, 39, 127, 125, 116, 52, 95, 131, 105, 55, 55, 52, 87, 35, 43, 130, 87, 103, 8, 73, 8, 116, 4, 43, 60, 104, 116, 118, 78, 9, 133, 139, 7, 127, 96, 28, 52, 79, 78, 36, 102, 134, 100, 104, 47, 127, 129, 77, 121, 133, 10, 58, 104, 55, 69, 73, 107, 9, 139, 79, 52, 72, 130, 78, 112, 43, 14, 4, 120, 9, 139, 118, 52, 52, 73, 82, 79, 58, 121, 80, 139, 10, 25, 74, 10, 123, 134, 112, 40, 80, 108, 128, 5, 52, 43, 31, 10, 42, 79, 139, 86, 58, 3, 118, 117, 21, 4, 79, 45, 26, 5, 122, 102, 13, 88, 139, 108, 118, 116, 10, 58, 32, 80, 125, 121, 105, 116, 104, 82, 131, 39, 10, 126, 125], 'vodpyvpjmogqvwnibqasbulkfbfugvtlpdtsmydrbrekavkhifoypepbcnzpmasbnlrfqgdhnmvhldsrogjsntummchcftzrnycichziopmphfqwqdsihoywdpqqkyvzrhbqorwrkmns', ), ) It outputs 13, but the expected output is 17. When I replace max = Math.max(max, dfs(node, -1)) with max = Math.max(dfs(node, -1), max) the answer will be right (17). I can't tell the difference... What's wrong?
Your dfs changes max in two places: max = Math.max( max, dfs(node, -1), ) ... max = Math.max(max, 1 + a + b) return a + 1 So the order in which you call dfs as an argument to Math.max can affect things: If you call dfs first, it will change max and the second argument passed to Math.max will be the changed value, so it will compare with the correct value that was changed by the recursive dfs calls. If you call dfs second, it will copy max, call dfs then compare it with the old max, with the value it had before the recursive call. Solution: The best thing you can do is not mutate state external to dfs: make dfs return what you need and use that returned value, don't rely on an external variable. You seem to already be returning something from dfs, so it shouldn't be too hard to get rid of mutating max. That can be messy in some cases (but not yours), in those cases at least don't put the recursive call as a parameter. Do it like this: const dfsResult = dfs(node, -1) max = Math.max(max, dfsResult)
reduce the size of a javascript array while keeping members that are evenly distributed in the original
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.
Longest Increasing Subsequence (Javascript) Facebook Algo
I was trying to solve this problem with backtracking instead of DP...and ran into a problem that doesn't make sense to me. here is my code function solve(A) { let lp = 0; const path = []; const traverse = function (arr, i = 0) { if (i >= arr.length) return; for (j = i; j < arr.length; j++) { if (path.length === 0 || arr[j] > path[path.length - 1]) { path.push(arr[j]); // here is the problem I don't understand, I am calling the recursive function with( j + 1) // but somehow that reads it as j = j + 1; why is this happening? // I would understand if it was an Array where the j would pointing to memory but it shouldn't // happen to a regular number, right? traverse(arr, j + 1); // this causes the j variable in the for loop to increment by 1 so I can't get back track properly. if (path.length > lp) lp = path.length; path.pop(); } } } traverse(A); return lp; } const A = [69, 54, 19, 51, 16, 54, 64, 89, 72, 40, 31, 43, 1, 11, 82, 65, 75, 67, 25, 98, 31, 77, 55, 88, 85, 76, 35, 101, 44, 74, 29, 94, 72, 39, 20, 24, 23, 66, 16, 95, 5, 17, 54, 89, 93, 10, 7, 88, 68, 10, 11, 22, 25, 50, 18, 59, 79, 87, 7, 49, 26, 96, 27, 19, 67, 35, 50, 10, 6, 48, 38, 28, 66, 94, 60, 27, 76, 4, 43, 66, 14, 8, 78, 72, 21, 56, 34, 90, 89] const B = [1,3,4,2,5,3] console.log(solve(A)) console.log(solve(B)) an explanation would be appreciated, thanks in advance!!!
Weird jsPerf behavior for recursive function
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.