2

suppose you have a spreadsheet like this in Google Sheets

enter image description here and you want to SUM all the values of the cells in Q:Q WHERE the CORRESPONDING cell (i.e. on the same row) in column D:D has certain given values ..

Now, if we were in some DB language like MySql I suppose this would be quite easy and you would probably instruct something like

SELECT cells in "Tot. / Anno" (Q:Q) WHERE "level" (D:D) is B or MID

and that would be it, but I guess nothing so direct is possible in Javascript; so I am afraid a little workaround is needed.

So, first of all, what's the logic behind the sheet?
In column D:D under level you have four options which you can select from, and these are: b, mid, pro and top. B stands for basic, and the logic here is trying to build a gradual budget.
Some kind of expenses are basical and you can't avoid them, like medical or your bank account or telephone; while others like leisure and travels are secondary and you can spare on them; so they are not fundamental, but most likely mid pro or even top :) This is the logic behind and the scale of values.

When you select a given criterium in D2 you should get the matching SUM in G2 based on the same gradual logic:

b   in D2  -->  only sum cells in Q:Q where value is b
mid in D2  -->  only sum cells in Q:Q where value is b OR mid
pro in D2  -->  only sum cells in Q:Q where value is b OR mid OR pro
top in D2  -->       sum cells in Q:Q where value is b OR mid OR pro OR top

Now, as to coding, this is the workaround that I've found. It does work, but it's not elegant.
I would like to hear some more efficient and proper way to deal with the matter: I am at a elementary level and I am sure a more effecient solution is available

// onEdit is meant to make sure that a real-time recalculation is triggered  
// at any change within the specified columns
function onEdit(e){

var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();

  var cell = sheet.getRange("Q2");
  var range = e.range;
  var columnOfCellEdited = range.getColumn();

  if (columnOfCellEdited === 4 || 17) {


// creating 2 different arrays from the two different comlumns (ranges)  
var range1 = sheet.getRange("D3:D100").getValues();
var range2 = sheet.getRange("Q3:Q100").getValues();  

// merging the two single arrays into one 2d array (you can't have one single 
// 2d array from the start because the two columns are not adjacent I guess)
var range=[],i=-1;
while ( range1[++i] ) { 
  range.push( [ range1[i][0], range2[i][0] ] );
}

// I tried to come up with a global SUM but I don't know why it does not work
// var valore_glob = range2.reduce(function(acc, val) { return acc + val; }, 0);

// as much filters as given values in the validation drop-down list: each  
// filter returns only the correspoding values

// FILTRO BASE - keeps only B values
var filt_bas = range.filter(function (dataRow) {  return dataRow[0] ===  'b';});
// splitting the 2d array into two singles arrays, we keep only the second  
// one for the follwing SUM
var yArray_bas = filt_bas.map(function(tuple) {    return tuple[1];});  
// summing up all the values within the resulting array
var valore_bas = yArray_bas.reduce(function(acc, val) { return acc + val; }, 0);

// FILTRO MID - keeps only MID values 
var filt_mid = range.filter(function (dataRow) {  return dataRow[0] ===  'mid';});
// splitting the 2d array into two singles arrays, we keep only the second  
// one for the follwing SUM
var yArray_mid = filt_mid.map(function(tuple) {    return tuple[1];});  
// summing up all the values within the resulting array
var valore_mid = yArray_mid.reduce(function(acc, val) { return acc + val; }, 0);

// FILTRO PRO - keeps only PRO  values  
var filt_pro = range.filter(function (dataRow) {  return dataRow[0] ===  'pro';});
// splitting the 2d array into two singles arrays, we keep only the second  
// one for the follwing SUM
var yArray_pro = filt_pro.map(function(tuple) {    return tuple[1];});  
// summing up all the values within the resulting array
var valore_pro = yArray_pro.reduce(function(acc, val) { return acc + val; }, 0);  

// FILTRO TOP - keeps only TOP values 
var filt_top = range.filter(function (dataRow) {  return dataRow[0] ===  'top';});
// splitting the 2d array into two singles arrays, we keep only the second  
// one for the follwing SUM
var yArray_top = filt_top.map(function(tuple) {    return tuple[1];});  
// summing up all the values within the resulting array
var valore_top = yArray_top.reduce(function(acc, val) { return acc + val; }, 0);   

var selector = sheet.getRange("D2").getValue();

switch (selector) {
  default:
    cell.setValue(valore_bas);
    break;
  case 'mid':
    cell.setValue(valore_bas + valore_mid);
    break;
  case 'pro':
    cell.setValue(valore_bas + valore_mid + valore_pro);
    break;
  case 'top':
    cell.setValue(valore_bas + valore_mid + valore_pro + valore_top);
}

  }
}

suggestions are welcome, thanks.

credits:
Writing google Javascript similar to vlookup
Sum elements of an array with specific attribute
Spreadsheet Non-Adjacent Column data
How to find the sum of an array of numbers
How to filter an array of arrays?
Split a 2D array into single arrays

latest working actual code as in code.gs: https://jsfiddle.net/John_Galassi/5ahrLmg9/

John Galassi
  • 309
  • 2
  • 16
  • If this was Excel I'd be looking at a simple SUMIFS function, but I have no idea about Google Sheets – Harassed Dad Sep 17 '19 at 08:10
  • To clean it up a little, I suggest you to use arrow functions to make this (and other examples) `var filt_bas = range.filter(function (dataRow) { return dataRow[0] === 'b';});` to this `var filt_bas = range.filter((dataRow) => dataRow[0] === 'b');` a bit easier on the eyes if you ask me. – MauriceNino Sep 17 '19 at 08:13
  • I agree with @HarassedDad, the [sumif](https://support.google.com/docs/answer/3093583?hl=en) function should be sufficient? – Casper Sep 17 '19 at 08:22
  • @MauriceNino I did try it in the beginning, but it seemed to me this notation wasn't working on GAS, can you confirm? I'll try that again anyway, thanks – John Galassi Sep 17 '19 at 09:06
  • @HarassedDad, Casper yes that could work in excel, but my wish would be to implement something from the code side. thanks for your answers though – John Galassi Sep 17 '19 at 09:08

1 Answers1

2

Using built-in functions you could try

=iferror(sumproduct(regexmatch(D3:D, "^("&vlookup(D2, {"b", "b"; "mid", "b|mid"; "pro", "b|mid|pro"; "top", "b|mid|pro|top"}, 2, 0)&")$"), Q3:Q))

or, depending on your locale

=iferror(sumproduct(regexmatch(D3:D; "^("&vlookup(D2; {"b"\ "b"; "mid"\ "b|mid"; "pro"\"b|mid|pro"; "top"\ "b|mid|pro|top"}; 2; 0)&")$"); Q3:Q))

Or, you can create a custom function in google script:

function sumOnCondition(filter, checkrange, sumrange) {
var arr, ind;
arr = ["b", "mid", "pro", "top"];
ind = arr.indexOf(filter);
arr = (filter === "top") ? arr : arr.slice(0, ind + 1);
return checkrange.map(function(r, i) {
    return arr.indexOf(r[0]) > -1 ? Number(sumrange[i]) : 0;
}).reduce(function(a, b) {
    return a + b;
})
}

and in the spreadsheet enter in G2

=sumOnCondition(D2, D3:D, Q3:Q)

or, depending on your locale

=sumOnCondition(D2; D3:D; Q3:Q)

See if that helps?

JPV
  • 26,499
  • 4
  • 33
  • 48
  • I get an error in both case. I get "TypeError: Impossibile chiamare il metodo "map" di undefined. (riga 6, file "sum")" – John Galassi Sep 21 '19 at 13:38
  • 1
    Can you share a copy of your spreadsheet so I can take a closer look? – JPV Sep 22 '19 at 14:22
  • yes of course thanks, let me prepare it and I'll post a link at the end of my original question. thanks – John Galassi Sep 22 '19 at 15:47
  • Hi again, your locale requires the use of semi-colons as argument separators. I've updated both formulas and entered them in your spreadsheet (cells T2 and U2). See if it now works? – JPV Sep 23 '19 at 13:37
  • thanks man, it seems to work. there's still a couple of questions I would like to pose but it seems to work for now. Really many thanks, it's really slim and more elegant than my previous one. I hope I can ask you on additional questions later on. thanks – John Galassi Sep 23 '19 at 15:57
  • Most welcome, @JohnGalassi. Feel free to start a new thread whenever you need help. – JPV Sep 23 '19 at 19:19