Google Apps Script concatenating numbers rather than adding - javascript

I wrote the code below to grab a portion of a column of numbers and add them up. However, the function is concatenating numbers rather than adding.
I'm getting this result:
0AMOUNT120123126129132135138141144147
But if I run it from Dec on the sum should be:
432
My code in Google Scripts:
//add sum and input it into the total column
function sum(startMonth, startColumn, lastRow){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
startColumn = startColumn +1;
var sum = 0;
var currAmount = 0;
var k = 0;
for(k = startMonth; k <= lastRow; k++){
currAmount = sheet.getRange(k, startColumn).getValue();
sum += currAmount; //currAmount + sum;
Logger.log(sum);
}
SpreadsheetApp.getActiveSheet().getRange(k, startColumn).setValue(sum);
SpreadsheetApp.getActiveSheet().getRange(k, startColumn).setBackground("Yellow");
return sum;
}
And a snapshot of my sheet:

Your result is the hint: That's not a number being added. That's strings being concatenated.
currAmount = sheet.getRange(k, startColumn).getValue();
sum += currAmount; //currAmount + sum;
Specifically, this is the main problem. Regardless of whether the number returned by getValue() is a number or not, you add it to sum.
A few ways to fix this would be to adjust the value of k, to check the typeof the value, or coerce the value into a number, depending on exactly what you're trying to do. This is essentially a javascript problem, with the canonical answer here
Also, generally you should batch operations where possible; e.g., instead of running getRange().getValue() every go through a for-loop, you are much better using (as an example, may need tweaking)
var amounts = sheet.getRange(startMonth, startColumn, (lastRow - startMonth), 1);
for(var k = startMonth; k <= lastRow; k++){
sum += amounts[k][0]
Logger.log(sum);
}

Related

Google Apps Scripts - Out of Memory Error

I am writing about a problem my org is having, as a result I have to be a bit vague (though I have permission to post this).
I am writing a script parse a google-sheet that has a column with numerical data in it. I need the script to return a set of ranges (strings in A1 notation for use elsewhere). The ranges have to correspond to real ranges in the script for which the numerical data sums to as close to 0.25 as possible but not more than .25, and return all ranges in the sheet for which this is true. E.g. if my sheet data was
A (COLUMN HEADER)
DataHeader (A1)
.1 (A2)
.01 (A3)
.04 (A4)
.1 (A5)
.03 (A6)
.02 (A7)
.1 (A8)
.05 (A9)
.04 (A10)
.07 (A11)
my script should return the array [A2:A4,A5:A10,A11], because A2:A4 sum to exactly .25, while A5:A10 sum to below (but close to) .25 but including A11 would get us above .25. A11 is included because I still need the data there.
In reality, my script will return larger ranges in A1 notation, but the problem I'm having is occuring in my attempt to properly construct a running sum and return the rows of interest.
My script is broken into 2 functions at the moment, because I was tackling the problems I was having in pieces.
The first script IDs the following info: The data to examine, as well as the number of rows from the top of the sheet that the start of the data
var data
and
var A1offset
respectively
The second function does a running sum of an array of my data and build an array of row number pairs representing the ranges at which the sum is just below .25. I have pasted the portion throwing an error below.
var result = [] //empty array to store row number pairs.
//data is a previously obtained array of my data of interest
//A1offset is the number of rows between the top of the sheet and the start of data
for (var i = 0;i<data.length;i++){
cursum += data[i][0]//add current value to cursum
if( cursum >= .25 && result.length<1){//if we are # the beginning add first rownum (A1 Notation) of data to result
i=i-1 //Go back one so that i++ runs, we don't miss this datapoint.
cursum = 0 //reset
result.push([(A1offset),(i + A1offset )])//
}
else if( cursum >= .25 && result.length>=1){
i=i-1//go back one
cursum = 0//reset
result.push([(result[result.length-1][1]+1),(i+A1offset)]) //Add to result a pair of values, the
//row after the end of the last range and the current row.
}
}
The issue:
Previously, I did not have the
i = i-1
statement but I realized I needed that or
i--
However after adding these two statements, upon running my code I get an 'Out of Memory error' error.
However, this does not happen if I loop over a smaller dataset. Experimentally, I have found that if I use
for (var i=0;i<.85*data.length;i++){
it runs fine.
data has 4978 elements in it.
Question: Is there an upper limit to the number of iterations a google script can run? I was under the impression that if there was, it was much higher than 4978 (or closer to 5050, since I'm using the i=i-1 statement)
Or, did I simply write poor code? Or both?
Thank you for any insights you can provide, I don't have a CS degree so frequently I run into issues where I don't know what I don't know.
Please let me know if I have not provided enough information.'
Edited to add a link to a test version of my sheet as well as the entire script below (overcommented to be certain):
function IDDATA() {
//ID the data to sum
var spsheet = SpreadsheetApp.getActive(); //Get spreadsheet
var tab = spsheet.getSheetByName("Test") //Get data sheet
var Drows = tab.getRange("A:A").getValues() //All values in Column D
var filledvals = Drows.filter(String).length //Length of filled in rows in Column D
//Subtract out the header row from the length of values
// Logger.log(datarows)
var offset = 0 //Initialize at 0. Offset is the distance (ZERO INDEXED <-THIS IS IMPORTANT!) between row 1 and the data
var ct1 = 0//initialize counter 1 at 0
while(Drows[ct1][0].indexOf("DATA")<0){ //Look at the ct1 Row of the 0th column of Column D
ct1++//Count until a the row with DATA in it. indexOf(String) returns -1 if the string is not found.
}
offset = ct1 //set offset to ct1.
var A1offset = ct1 + 2//Assume that all data has a header row, and create an offset variable that points to the first row of NUMERICAL data.
var datarows = filledvals-2 //Gets us the ROW NUMBER of the last filled in row in D:D, which should be the LAST number in the data.
return([datarows,offset,A1offset])
}
function RUNSUM(){
var firstcol = "A"
var lastcol = "A"
var spsheet = SpreadsheetApp.getActive(); //Get spreadsheet
var tab = spsheet.getSheetByName("Test") //Get sheet
var vararray = IDDATA()
var len = vararray[0] //Length of data
var offset = vararray[1] //Unused?
var A1offset = vararray[2]//The offset from 0, but to be used in A1 Notation
// Logger.log(typeof(A1offset))
var startrow = "A"+A1offset//The first row to get data from in the column that has system size data.
var endrow = "A"+(len+A1offset)//The last row to get data from that has system size data.
var cursum = 0 //Initialize # 0, the current sum of system size data.
var range = tab.getRange(startrow+":"+endrow) //Range to examine, in A1 notation Could probably use other forms of getRange()....
var data = range.getValues() //Values in range to exampine
var testmax = Math.floor(.85*data.length)
var result = [] //Positions, initialize as empty array
//var exceptions = [] //Rows that contain non-numbers
for (var i = 0;i<.8*data.length;i++){//.8* because of memory error
/* if(isNaN(data[i][0])){ //if data[i][0] is Not a Number
exceptions.push((i+A1offset).toFixed(0))//push the row number to exclude from eventual csv
continue //skip to next i
}*/
cursum += data[i][0]//add current value to cursum
if( cursum > .25 && result.length<1){//if we are # the beginning, we need to know, see results.push statement below
i=i-1
cursum = 0 //reset
result.push([(A1offset),(i + A1offset )])//
}
else if( cursum > .25 && result.length>=1){
i=i-1//go back one
cursum = 0//reset
result.push([(result[result.length-1][1]+1),(i+A1offset)])
}
}
var rangearray = []
var intarray = [] //declare an empty array to intermediate array vals todo: remove in favor of single loop
for(var k = 0; k < result.length; k++){
intarray.push([result[k][0].toFixed(0),result[k][1].toFixed(0)])//build array of arrays of result STRING vals w/out decimal pts.
}
for (var j = 0;j<result.length;j++){
rangearray.push(firstcol+intarray[j][0]+":"+lastcol+intarray[j][1])
}
Logger.log(rangearray)
return rangearray
}
In the Sheet you provided, the cell 4548 contains the number 0.2584.
Your algorithm, upon finding that number, will insert it into the result array and decrement i, creating an infinite loop that appends data into your array.
In order to fix your issue, in the RUNSUM() function right after the for loop declaration (for (var i = 0;i<data.length;i++)), you should handle this situation (or otherwise, make your data "valid" by removing that outlier value).
Furthermore, I have rewritten the algorithm in the hopes of making it more clear and stable (see that it now handles the last range, and ignores values over 0.25). I believe this may be interesting to you:
function myAlgorithm() {
var sheet = SpreadsheetApp.getActive().getSheetByName('Test');
var values = sheet.getRange("A6:A").getValues();
var l = 0;
var results = []; // 1-indexed results
var currentSum = 0;
var offset = 5;
for (var i=0; i<values.length && typeof values[i][0] == 'number'; i++) {
if (values[i][0] >= 0.25) {
// Ignore the value larger or equal than 0.25
Logger.log('Found value larger or equal than 0.25 at row %s', i+1+offset);
if (l<i) {
results.push([l+1+offset, i+offset, currentSum]);
}
l = i+1;
currentSum = 0;
} else {
if (currentSum + values[i][0] > 0.25) {
results.push([l+1+offset, i+offset, currentSum]);
l = i;
currentSum = values[i][0];
} else {
currentSum += values[i][0];
}
}
}
// handle last range after exitig the for-loop
if (l<i) {
results.push([l+1+offset, i+offset, currentSum]);
}
for (var i=0; i<results.length; i++) {
Logger.log(results[i]);
}
}
Finally, if you decide to modify your code in order to handle this situation, I suggest you check out the information in the following link:
Troubleshooting in GAS

Finding Products within large Digits

I have been working on a way to find products of 5 digits within a large number. For example, I have the number 158293846502992387489496092739449602783 And I want to take the first 5 digits (1,5,8,2,9) and multiply them, then the second, (5,8,2,9,3), multiply then, then the third... And so on. Then I want to find the largest of all of them I find, now I came up with the following code to solve this problem:
// I put the digit into a string so I can modify certain parts.
var digit = "158293846502992387489496092739449602783";
var digitLength = digit.length;
var max = 0;
var tempHolder;
var tempDigit = 0;
var tempArray = [];
for(var i = 0; i<=digitLength; i++){
tempHolder = digit.substring(i, i+5);
tempArray = tempHolder.split("");
for(var j = 0; j < 5; j++){
tempDigit += tempArray[j];
}
if(tempDigit > max){
max = tempDigit;
}
}
console.log(max);
It logs to the console A longer number than what I put into it, along with 10 undefined, no spaces. Can anyone figure out the problem here?

Comparing 2 arrays to output total integer

I have 2 arrays of numbers. I want to go through each array and find the number of times 1 number from each array adds up to the particular amount x.
If the particular amount x is reached as many times as another set number n then the function should print 'YES'. If x does not reach the set number of n then the function should print 'NO'.
The values of x , n and both arrays are in a string input. These values have been split into arrays as seen below in the code.
I have set up 2 for loops to run through each array and an if statement that checks for the condition of x meeting n.
The arrays I'm using in this code should print out the result of 'YES' however every time I run the code I'm getting 'NO' ? I've tried tinkering with the code but nothing has worked.
Any idea on where this code is broke and how to fix the problem?
Thanks :)
code:
var input = '2\n3 10\n2 1 3\n7 8 9';
function processData(input) {
var inputArray = input.split('\n');
var n = inputArray[1][0];
var x = inputArray[1].split(' ')[1];
var arrayA = inputArray[2].split(' ');
var arrayB = inputArray[3].split(' ');
var total = 0;
for(var i = 0; i < arrayA.length; i++) {
for(var j = 0; j < arrayB.length; j++) {
if(arrayA[i] + arrayB[j] == x) {
total = total + 1;
} if (total == n) {
return 'YES';
}
}
}
return 'NO';
}
console.log(processData(input));
arrayA[i] and arrayB[j] are strings, so arrayA[i] + arrayB[j] will be the concatenation of them (ex: '2' + '3' === '23').
If your logic is correct (i didn't quite understand what you are trying to do), it should be enough to convert them to numbers before adding them, using parseInt or some other method:
if(+arrayA[i] + (+arrayB[j]) == +x) { // used unary + to convert to number
total = total + 1;
} if (total == n) {
return 'YES';
}
PS: A cleaner version would be to convert each string in the array to number, but that involves more than adding 3 characters to your code.
PS2: You have a weird way of getting the input data. If you get it from another place in your JS code, you could simply pass it as an object with the relevant structure, otherwise you could pass it around in a more ... common format, like JSON.

javascript storing array values

Im trying to get the total combined value of a set of numbers.
Im getting the numbers as the text in an element tag storing them in an array then adding them all together. My problem is that its not inserting the numbers into the array as pairs.. it adding them as single integers .what am doing wrong.
check the jsfiddle too see example
http://jsfiddle.net/Wd78j/
var z = $('.impressions').text();
var x = [];
for(var i = 0; i < z.length; i++){
x.push(parseInt(z[i]));
}
console.log(x);
var total = 0;
$.each(x,function() {
total += this;
});
$('#impressTotals').append("[Total:" +total + "]");
$('#array').append("[Array:"+x+"]");
When you get text, it's taking all the numbers and concatenating them into a string. The below takes each element one at a time and pushes it.
var x = [];
$('.impressions').each( function( ) {
var z = $(this).text();
x.push(parseInt(z, 10));
})
Of course, you could build the sum up inside that each function, but I did it like this to more closely mirror your code.
text() returns the concatenated text of all of your impressions elements, of which you're adding together each character.
You want to loop through each impressions element, and keep a running sum going. Something like this should work
var sum = 0;
$('.impressions').each(function(){
sum = sum + (+$(this).text());
});
Updated Fiddle
Or to keep your original structure (don't forget the radix parameter to parseInt):
var z = $('.impressions');
var x = [];
z.each(function(){
x.push(parseInt($(this).text(), 10));
});
console.log(x);
var total = 0;
$.each(x,function() {
total += this;
});
$('#impressTotals').append("[Total:" +total + "]");
$('#array').append("[Array:"+x+"]");
Updated fiddle
You are iterating over a string, you could just use $.map to build the array instead, if you need it, otherwise just iterate and sum up the values :
var x = $.map($('.impressions'), function(el,i) {return parseInt($(el).text(), 10);}),
total = 0,
n = x.length;
while(n--) total += x[n] || 0;
$('#impressTotals').append("[Total:" +total + "]");
$('#array').append("[Array:"+x+"]");
FIDDLE

how to add array element values with javascript?

I am NOT talking about adding elements together, but their values to another separate variable.
Like this:
var TOTAL = 0;
for (i=0; i<10; i++){
TOTAL += myArray[i]
}
With this code, TOTAL doesn't add mathematically element values together, but it adds them next to eachother, so if myArr[1] = 10 and myArr[2] = 10 then TOTAL will be 1010 instead of 20.
How should I write what I want ?
Thanks
Sounds like your array elements are Strings, try to convert them to Number when adding:
var total = 0;
for (var i=0; i<10; i++){
total += +myArray[i];
}
Note that I use the unary plus operator (+myArray[i]), this is one common way to make sure you are adding up numbers, not concatenating strings.
A quick way is to use the unary plus operator to make them numeric:
var TOTAL = 0;
for (var i = 0; i < 10; i++)
{
TOTAL += +myArray[i];
}
const myArray = [2, 4, 3];
const total = myArray.reduce(function(a,b){ return +a + +b; });
Make sure your array contains numbers and not string values. You can convert strings to numbers using parseInt(number, base)
var total = 0;
for(i=0; i<myArray.length; i++){
var number = parseInt(myArray[i], 10);
total += number;
}
Use parseInt or parseFloat (for floating point)
var total = 0;
for (i=0; i<10; i++)
total+=parseInt(myArray[i]);

Categories

Resources