I need to perform a cumulative sum of an array. Under certain conditions, I need it to be straight forward, and found this snippet to work great:
cumul = sum.reduce(function (a, n) {
a.push((a.length > 0 ? a[a.length-1] : 0) + n);
return a;
}, [initial]);
cumul.shift();
Logger.log(cumul);
When I log 'cumul' I get the result I need. However, under other IF() conditions, I need to perform the cumulative sum providing a certain condition in another array containing dates is met - if the date is <= X, don't add to the cumulative sum in this iteration (show previous cumulative value).
Any ideas how to implement this? It seems that using this version of a cumulative sum won't work, but I'm not sure what would be other ways to go about this.
Thanks in advance!
G
You're making life far too hard on yourself with all that pushing and reducing and shifting.
function running_total(array, init) {
return array
.map(function(v, i) { return v ===3 ? 0 : v; }) // filter out 3's
.map(function(v) { return init += v; })
;
}
document.writeln(running_total([1,2,3,4,5], 0));
To make the filter pertain to another, parallel array of dates, change the first map function to something such as
function(v, i) { return dates[i] < cutoff ? 0 : v; }
For loops are very fast in Javascript, and can create all kinds
of conditional sums.
The function ccSum() below takes 2 parameters, a data array and a condition array.
The data is summed if the condition is true.
function ccSum(data, condition){
var r = [];
r[0] = (condition[0])? data[0]: 0;
for(i=1,l=data.length; i<l; ++i) r[i] = r[i-1] + ( (condition[i])? data[i]: 0 );
return r
}
It would be easy to rewrite this where condition is a function to be called, by changing the square bracket condition[i] for a round one condition(i). Alternatively, we could check typeof(condition)==='function' and branch these two cases.
Related
I got the following string-array:
cohortsDates =
[
'2020-11', '2021-01',
'2021-02', '2021-03',
'2021-04', '2020-10',
'2021-05', '2020-12',
'2021-07'
]
Now I try to sort it that the dates are in an ascending order from 2020-10 to 2021-07 with this code:
cohortsDates.forEach((month) => {
for(var i = 0; i < cohortsDates.length; i++ ) {
if(moment(cohortsDates[i+1]) < moment(cohortsDates[i])) {
var swap = cohortsDates[i]
cohortsDates[i] = cohortsDates[i+1]
cohortsDates[i+1] = swap
}
}
})
console.log(cohortsDates)
But all I get is an endless loop and the sorted array never prints out. Does somebody know, what can I do to fix it?
When i === cohortsDates.length-1 (i.e. you are looking at the last item in your for loop) you test:
if(moment(cohortsDates[i+1]) < moment(cohortsDates[i])) {
Where cohortsDates[i+1] will always be undefined and thus less than the previous value.
So you swap them and assign cohortsDates[i] to cohortsDates[i+1].
This increases cohortsDates.length by 1 so the end condition of the for loop doesn't apply.
You now loop again and cohortsDates[i+1] is still undefined so it goes infinite.
JS has a built-in sort method to do this. Don't reinvent the wheel.
You don't exactly have the most efficient sorting algorithm. When sorting, you should use javascript's array.sort:
cohortsDates.sort((a, b) => moment(a) < moment(b) ? -1 : 1);
The sort method will loop over the array and call the function you passed as an argument with two of the values as parameters. If the function returns a negative number, that means a is less than b, a positive number means a is greater than b.
I'm new to coding, still learning. My friend gave me a task to write a function that does return the 2nd highest number from an array, I've managed to do it using array.prototype.sort(). He said to replace "-" with a "<" or ">" to make the code more clear, that's where the problem started.
I'm using VCS on windows, and it's not working properly.
My friend uses a mac, everything works fine.
Tried it on jsfiddle, everything works fine.
const secondMax = (arr) => {
return arr.sort((a, b) => b - a)[1]; //does return the correct number after console.log()
};
const secondMax = (arr) => {
return arr.sort((a, b) => a < b)[1]; //does not
};
"a < b" should be sorting descending
"a > b" should be sorting ascending
But no matter which operator I use, the sorting fails and just returns the second number from the array
You're supposed to return a number, not a boolean. So the first is correct. The latter might work by chance on some javascript engines, but it's not guaranteed to.
sort sorts the array as String by default. If you pass a comparator, then it's a function which will depend on two parameters and return:
negative, if the first parameter is smaller than the second
0 if they are equal
positive, if the first parameter is greater than the second
Using a logical operator instead of the above is mistaken.
However, if you are interested in finding the second largest number, then it's better to do it using a cycle:
var largestNumbers = [];
var firstIndex = (arr[0] < arr[1]) ? 1 : 0;
largestNumbers.push(arr[firstIndex]);
largestNumbers.push(arr[1 - firstIndex]);
for (var i = 2; i < arr.length; i++) {
if (largestNumbers[1] < arr[i]) {
if (largestNumbers[0] < arr[i]) {
largestNumbers[1] = largestNumbers[0];
largestNumbers[0] = arr[i];
}
}
}
This is quicker than sorting an array and more importantly, it does not destroy your initial order just to find the second largest number.
I am trying to find a way to write a function that sums up all the elements within an array. I have been trying to implement this using JavaScripts reduce function. The problem I am having is that i want this function to work on both an empty array and an array with elements as separate scenarios. The following example passes the test case for an empty array but then not when the array has elements and vice versa.
function sum (numbers) {
numbers = ['a', 'b', 'c'];
return numbers.reduce(function (x, y, i) {
return x + y + i;
}), 0 };
I was looking at the signature of the reduce function and trying to implement this on the basis of that but something seems to be missing in my knowledge here.
function (previousValue, currentElement, currentIndex, array)
the following works with both an array of numbers, and an empty array (where the result will obviously be zero)
var sum = numbers.reduce(function(prev,curr){
return curr + prev;
},0);
Below is a demo of both your scenarios
function sum(numbers){
var x = numbers.reduce(function(prev,curr){
return curr + prev;
},0);
return x;
}
alert(sum([1,2,3]));
alert(sum([]));
I am working on this problem from coderbytes:
Using the JavaScript language, have the function SecondGreatLow(arr) take the array of numbers stored in arr and return the second lowest and second greatest numbers, respectively, separated by a space. For example: if arr contains [7, 7, 12, 98, 106] the output should be 12 98. The array will not be empty and will contain at least 2 numbers. It can get tricky if there's just two numbers!
My solution works by removing the greatest and lowest values from the array and then using Math methods to return the second highest and lowest values.
However, when there are two or more instances of the greatest or lowest elements of the array, and their index positions are adjacent to each other, I believe only the first instance of this value is removed and the flow skips over the second instance.
Is there any way to have the loop run through the same index value twice in order to process adjacent greatest or lowest values?
Here are the two iterations of my solution which I've tested.. my original attempt using .forEach and my second using a for loop.. I've console.logged a situation in which the code works and in which it doesn't for each attempt.
I'm really new to all this, almost a month of learning in my free time so explaining yourself as if I'm really dumb is appreciated. Thanks!!!
// * First attempt - using .forEach method *
// outputs the second lowest value in the array
function secondLowest (arr) {
var g = function () {
return Math.min.apply(null, arr);
}
arr.forEach(function (val, indx, arr) {
if (val === g()) {
arr.splice(indx, 1);
}
});
lowestVal = g(); // store this value to be added back in for the secondGreatest function (in case there were only two digits in the arr argument)
return Math.min.apply(null, arr);
}
// number trimmed from the array in the function secondLowest..
// to be added back in for the function secondGreatest
var lowestVal = 0
// adds back the lowest value which was trimmed..
// outputs the second greatest value
function secondGreatest (arr){
arr.splice(0,0,lowestVal);
var g = function () {
return Math.max.apply(null, arr);
}
arr.forEach(function (val, indx, arr) {
if (val === g()) {
arr.splice(indx, 1);
}
});
return Math.max.apply(null, arr);
}
// putting together the output
function SecondGreatLow (arr) {
return secondLowest(arr) + " " + secondGreatest(arr);
}
console.log(SecondGreatLow([1,2,3,4,5]));
console.log(SecondGreatLow([1,1,2,2,3,3,4,4,5,5]));
// * Second attempt - using for loops *
// outputs the second lowest value in the array
function secondLowest (arr) {
var g = function () {
return Math.min.apply(null, arr);
}
lowestVal = g();
for (var i = 0; i < arr.length; i++) {
if (arr[i] === g()) {
arr.splice(i, 1);
}
}
return Math.min.apply(null, arr);
}
// number trimmed from the array in the function secondLowest..
// to be added back in for the function secondGreatest
var lowestVal = 0
// adds back the lowest value which was trimmed..
// outputs the second greatest value
function secondGreatest (arr){
arr.splice(0,0,lowestVal);
var g = function () {
return Math.max.apply(null, arr);
}
for (var i = 0; i < arr.length; i++) {
if (arr[i] === g()) {
arr.splice(i, 1);
}
}
return Math.max.apply(null, arr);
}
// putting together the output
function SecondGreatLow (arr) {
return secondLowest(arr) + " " + secondGreatest(arr);
}
console.log(SecondGreatLow([1,2,3,4,5]));
console.log(SecondGreatLow([1,1,2,2,3,3,4,4,5,5]));
I tried using the delete operator in order to keep the argument array length consistent (rather than shortening it with splice which I think allows the adjacent value to pass into the removed element's index position and not be processed in the next runthrough of the for loop or forEach method) but the Math.min/max.apply methods don't like having 'undefined' in the array argument.
Also if my code is looking ugly/annoying and makes you cringe then please take this opportunity to vent.. helps me learn to write code that doesn't piss people off ;)
** Solution Found **
Thank you for reminding me of the sort method!(function?) Here's what I ended up with:
function SecondGreatLow (arr) {
var secondLow = 0,
secondHigh = 0;
arr.sort(function(a,b){
return a-b;
});
for (var i = 1; i < arr.length; i++) {
if (arr[i] !== arr[i-1]) {
secondLow = arr[i];
break;
}
}
for (var j = (arr.length-2); j >= 0; j--) {
if (arr[j] !== arr[j+1]) {
secondHigh = arr[j];
break;
}
}
return secondLow + " " + secondHigh;
}
console.log(SecondGreatLow([1,1,2,2,3,3,4,4,5,5]));
What an awesome community.. I'll be back with more questions and hopefully I'll feel confident enough to even answer some questions in the near future. Thanks!
I feel like perhaps I'm missing something, but the challenge doesn't seem to include a requirement for removing items from the original array, so I don't see why you're modifying it in such a way. The requirements you provided simply state to return 'a b' where a is the second lowest, and b the second highest.
So, I would first recommend sorting the list. Since you know you're working at the upper and lower bounds, you don't have to iterate over anything (nor should you). Your test arrays are already sorted, but ensuring order will make your code more robust and able to handle other inputs. Check out the Arrays API for more details.
While it seems it may be beyond the scope of your problem, you may also want to look into sorting algorithms to learn more about how that all works, rather than relying solely on the API.
Once sorted, you should be able to easily compare inwards from the boundaries to get your second lowest and second highest values.
Also, you shouldn't need to utilize the Math API, simple inequality operators should do the trick (< and >).
EDIT: While I recommend working on the problem yourself, here is a simple solution to the problem. I place it here so if you get stuck you can reference this (and the associated comments) for guidance.
function SecondGreatLow(arr) {
var i;
var j;
var lowest;
var highest;
var secondLowest;
var secondHighest;
//Sort Array
arr.sort(function (a, b) {
return a - b;
});
//Get Bounds
//Since we sorted the array, and the default sort is in
//ascending lexicographical order, then we're guaranteed that
//our 'lowest' value is at index 0 and our 'highest' value is
//at index arr.length -1. Note that these values may be
//equal.
lowest = arr[0];
highest = arr[arr.length - 1];
//Search for second lowest.
for (i = 0; i < arr.length; i++) {
if (arr[i] > lowest) {
secondLowest = arr[i];
break;
}
}
//If we reach the end of the array, but didn't
//find a greater value, then, since the array is sorted,
//we're guaranteed that all values in the array are equal.
//Therefore, the required value comparisons have no meaning,
//and we return 'undefined'.
if (secondLowest === 'undefined') {
return 'undefined';
}
//Search for second highest, working backwards from the
//high end of the array until we reach our crossover point
//with the previous search. Either some value > arr[i] is the
//second highest, or arr[i] is, so there's no point in looking
//at values in the indices lower than i.
for (j = arr.length - 1; j >= i; j--) {
if (arr[j] < highest) {
secondHighest = arr[j];
break;
}
}
return secondLowest + ' ' + secondHighest;
}
var result = SecondGreatLow([3,3,4,5,4,6]);
console.log(result);
JSFiddle
You may create a priority queue limited by 2 elements, then feed it with all the array and pop the value, which would be the answer.
The trivial implementation would look like:
function UniqueNElementSortedQueue(length, comparison) {
this.length = length;
this.data = [];
this.comparison = comparison;
}
UniqueNElementSortedQueue.prototype.push = function(v) {
if (this.data.indexOf(v) > -1) {
return;
}
this.data.push(v);
this.data.sort(this.comparison);
this.data.length = this.length;
};
UniqueNElementSortedQueue.prototype.popIfN = function() {
if (this.data.length == this.length) {
return this.data[this.length - 1];
}
};
JSFiddle: http://jsfiddle.net/fmfv67xy/
The solution is O(N) (one might argue that I have sorting internally and they would be right :-)) by number of operations and O(N) by additional memory (where N is linear to the "next-lowest/greatest" index value)
As the description does not define what to return if it was not sufficient data fed - my implementation returns undefined.
Actually, let me turn my comment into an answer, since I think it always helps to also worry about performance:
Create 4 local variables:
largest and second_largest initialized to a number smaller than anything you'd expect in your array, or to the smallest possible value that your data-type can take on (-2^31 - 1)
smallest and second_smallest initialized to a number larger than anything you'd expect in your array, or the largest possible value for your data-type (2^31)
Loop over your array once:
If you find a number larger than largest, set second_largest to largest and largest to that number
If you find something smaller than largest but larger than second_largest, set second_largest to that number
If you find a number smaller than smallest, set second_smallest to smallest and smallest to that number
If you find something larger than smallest but smaller than second_smallest, set second_smallest to that number
When you're done with your loop, your answer is contained in second_largest and second_smallest
Given how small your arrays seem to be, you might not notice much of a performance difference between this answer and the other suggested ones, but I think it's a good habit to get into to always keep this concern in the back of your head for every line of code you write. In this answer, you process every array element exactly once (i.e. the algorithm runs in O(n)), whereas adding a sorting step leads to every element being processed multiple times in a general case (the best sorting algorithms (Timsort, for example) have an expected runtime of O(n log n)).
One thing to note:
#elclanrs mentioned a special case in his comment ([1, 1]), which according to the definition of the problem does not have a defined solution (multiple 1s all would be considered the largest number, so there is no second-largest). In this case, the algorithm above will still have second_largest and second_smallest set to their initial values.
I have array with decimal nos such as
var idArray = ["98.40", "111.46", "144.47", "180.48", "217.49", "284.50", "424.51", "571.52", "1887.53", "1960.54", "1972.55", "2118.56", "2167.57", "2467.58", "2480.59", "2488.60", "2662.61", "2671.62", "2767.63", "2982.64", "3168.65", "3263.66", "3295.67", "3369.68", "3579.69", "3592.70", "3600.71", "3605.72", "3620.73", "3646.74", "3852.75", "3857.76", "4031.77", "4489.78", "4975.79"]
I found the minimum value in the array as below
var result = Math.min.apply(null, idArray );
I got result as 98.4
Is there a way to return actual value in the array as 98.40
You could code your own:
minInArr = function(arr) {
var smallest = arr[0];
for(var i=1; i<arr.length; i++){
if(parseInt(arr[i],10) < smallest){
smallest = arr[i];
}
}
return smallest
}
Made this code based on this one:
Return index of greatest value in an array
There are a couple of methods in addition to those already here (though one is pretty similar to adeneo's). One is to copy the array, sort it, then get the 0 index value:
var min = idArray.slice().sort(function(a,b){return a - b})[0];
If you don't care about sorting the original array, drop the .slice() part.
Another way is to use Math.min to get the value, then use some to find it in the original array. The benefit of some is that it will stop at the first match:
var min, temp = Math.min.apply(Math, idArray);
idArray.some(function(v){ return temp == v? min = v : false});
console.log(min);
There are pros and cons to each, choose whatever is easiest to maintain.
If it really is an array, you can do it the old fashion way with iteration instead, and return the actual string instead of the parsed number, that way number of decimals is not important.
var idArray = ["98.40", "111.46", "144.47", "180.48", "217.49", "284.50", "424.51", "571.52", "1887.53", "1960.54", "1972.55", "2118.56", "2167.57", "2467.58", "2480.59", "2488.60", "2662.61", "2671.62", "2767.63", "2982.64", "3168.65", "3263.66", "3295.67", "3369.68", "3579.69", "3592.70", "3600.71", "3605.72", "3620.73", "3646.74", "3852.75", "3857.76", "4031.77", "4489.78", "4975.79"];
var result = idArray[0];
idArray.forEach(function(x) {
if (parseFloat(x) < result) result = x; // find smallest number as string instead
});
document.body.innerHTML = result;
or, you could just sort the array and get the first item (I sliced it to not modify the original)
var result = idArray.slice().sort(function(a,b) {
return a - b;
}).shift();
or, use Array.reduce
var result = idArray.reduce(function (a,b) {
return parseFloat(a) < parseFloat(b) ? a : b;
});
Try:
var roundedResult = parseFloat(result).toFixed(2);
The trailing zero has no importance and hence it is truncated. So you have no other go other than storing it as a string.
var result = Math.min.apply(null, idArray);
result = (result+"").test(/\.\d\d$/) ? result : result + "0"
Applying Math.min will always coerce your answer to a number, if you coerce it back to a string you loose any trailing zeros. As others have suggested if you know you will always have a fixed number of digits after the decimal you could use .toFixed.
A better solution that doesn't rely on having a fixed number of decimal points would be to use .reduce:
var result,
idArray = ["98.40", "111.46", "144.47", "180.48", "217.49", "284.50", "424.51", "571.52", "1887.53", "1960.54", "1972.55", "2118.56", "2167.57", "2467.58", "2480.59", "2488.60", "2662.61", "2671.62", "2767.63", "2982.64", "3168.65", "3263.66", "3295.67", "3369.68", "3579.69", "3592.70", "3600.71", "3605.72", "3620.73", "3646.74", "3852.75", "3857.76", "4031.77", "4489.78", "4975.79"];
result = idArray.reduce(function (prev, cur) {
if (+prev < +cur) {
return prev;
} else {
return cur;
}
});
console.log(result); // "98.40"
A quick explanation of what this does:
.reduce iterates over the array and calls the provided function once for each item in the array.
This code just uses the first two parameters available in the function, but there are a couple of others available too. The first parameter is the value returned from the previous call (prev, which will be undefined on the first call). The second parameter will be the value of the current item in the array (cur).
Before comparing the the two they are each coerced from strings to numbers using the Unary plus operator.
If prev is smaller it is returned and the next time the function runs prev will be the same, otherwise cur is returned and become the new value of prev on the next call. It is important to note that when the variables were coerced to compare them that just changed the values being compared in the conditional statement, it did not change the actual value stored in the variable, it remains a string.
After the function has been called on the last item in the array the final value of prev is returned and stored in result.
You could shorten it a little using a ternary statement:
result = idArray.reduce(function (prev, cur) {
return +prev < +cur ? prev : cur;
});
If you aren't afraid to use ES6 syntax (not all browsers currently support it) you could make it even shorter with a arrow function:
result = idArray.reduce((prev, cur) => +prev < +cur ? prev : cur);
The one potential (but unlikely) problem with this approach is that it coerces prev every time it makes a comparison. This adds a tiny bit of overhead to each step in the loop. If performance is a concern it would be better to get away from trying to do it with a one-liner and write a function to do it:
var arrayMin = function (arr) {
var i,
len,
prev, // prev and cur will hold numbers that are coerced from strings
cur, // once when they are first encountered
minIndex; // keep track of the index of the smallest item rather
// than copying a string every time we find a smaller number
prev = +arr[0];
minIndex = 0;
for (i = 1, len = arr.length; i < len; i += 1) {
cur = +arr[i];
if (cur < prev) {
prev = cur;
minIndex = i;
}
}
return arr[minIndex];
};
var result = arrayMin(idArray);