3

I'm trying to work out a formula to sum up values across different sheets for particular rows as in the following:

Sheet 1
A   |  B
---------
bob |  3
foo | 14
bar |  7

Sheet 2
A   |  B
---------
bob |  2
foo |  1
bar |  9

But with the caveat that a new 'Sheet 3' can be added with relevant data and will automatically be picked up.

How do I go about getting the summed values for each row in each sheet while handling new (properly named) sheets?

Results
-------
bob |  5
foo | 15
bar | 16

We can assume that the row values are all the same, such as in a named range, People = {bob, foo, bar}.

My attempt has been something like:

={People,ARRAYFORMULA('Sheet 1'!B1:B3+'Sheet 2'!B1:B3)}

but for new sheets, I would have to manually update the formula by adding in

+'Sheet 3'!B1:B3

I've tried using INDIRECT to dynamically generate the sheet names but it doesn't take an array. I'm guessing that I might have to use

SpreadsheetApp.getActiveSpreadsheet().getSheets()

in a script, but if I could do it just as a formula, it would be easier to explain to people inheriting the spreadsheet.

Thanks

Silfheed
  • 11,585
  • 11
  • 55
  • 67
  • similar to this Q: https://stackoverflow.com/questions/21081069/sum-range-over-sheets-in-google-spreadsheets – Max Makhrov Feb 08 '18 at 08:36
  • @MaxMakhrov No, not similar. The answer to that question is essentially what I indicated is my first attempt. It doesn't address how to handle sheets created after the formula is generated. – Silfheed Feb 08 '18 at 18:47

3 Answers3

2

I have alternate solution to your problem, using a slightly different approach. I would suggest pulling all of the data into one results page and summing it there. Then you don't need a massive 'sum' function or a script.

Using indirect you can pull information from each sheet, provided the data is in the same cell location on each sheet. If that's not possible you could also use vlookup to pull the data. I've shared an example of how I would do this using your numbers.

Syntax for 'bob' value on Sheet1 =iferror(indirect("'"&C$2&"'!"&"b3"),"") where C$2 is the Sheet name (in this case Sheet1) and B3 is the value for bob on Sheet1. Then you can copy this formula across the columns and update your sheet names at the top of the table.

https://docs.google.com/spreadsheets/d/15pB5CclseUetl5zSRPDOR9YA4u6_7TK8RB8CpSxqhnk/edit?usp=sharing

cgm990
  • 36
  • 4
0

Sample File

The workaround is to use a custom function:

enter image description here

Warning! The function won't refresh automatically. It will refresh if you add and then delete row above it.

Syntax: =sumBySheets("Sheet1", "Sheet2", "B1:B3")

  • "Sheet1" — sheet from
  • "Sheet2" — sheet to
  • "B1:B3" — range address for a sum.

There may be more sheets between Sheet1 and Sheet2:

enter image description here

The code:

function test_sumBySheets()
{
  var sheet1 = 'Sheet1';
  var sheet2 = 'Sheet2';
  var range = 'b1:b3';      
  Logger.log(sumBySheets(sheet1, sheet2, range));    
}

function sumBySheets(sheet1, sheet2, address)
{
  var file = SpreadsheetApp.getActive();
  var s1 = file.getSheetByName(sheet1);
  var s2 = file.getSheetByName(sheet2);

  var i1 = s1.getIndex() - 1; //get sheet indexes
  var i2 = s2.getIndex() - 1;

  var sheets = file.getSheets();

  var result = [];  
  var arrays = [];

  // remember all the data
  for (var i = i1; i <=i2; i++)
  {
    var s = sheets[i];
    var range = s.getRange(address);
    arrays.push(range.getValues());       
  }

  return sumByArrays_(arrays);

}

function sumByArrays_(arrays)
{
  // take array #1
  var arr = arrays[0];
  l = arr.length;
  ll = arr[0].length

  // plus all arrays
  for (var x = 1, xx = arrays.length; x < xx; x++) // loop arrays
  {
    for (var i = 0; i < l; i++) { // loop rows
      for(var ii = 0; ii < ll; ii++) { // loop cells
        arr[i][ii] += arrays[x][i][ii];
      }
    }  
  }

  return arr;

}

Note:

  • please run the function test_sumBySheets first and get the permissions.
Max Makhrov
  • 17,309
  • 5
  • 55
  • 81
0

Been a while, but here's what I ultimately ended up with for my solution.

The following code dynamically loads all sheets associated with the spreadsheet (note that I edited it a bit for readability on SO, might be some typos):

// These vars indicate what sheet, column and rows to start 
// the list of sheets.
var main_sheet = 'Sheet1';
var sheet_col = 'A';
var sheet_row_start = 1;

function onEdit(e) {
  // The onEdit trigger is the most useful as it fires most often.
  // Therefore it is more likely to update the list of sheets relatively
  // quickly.
  _set_sheet_cells();
}

function _clear_sheet_cells(num_sheets, sheets_length) {
  // Clear sheet cells, in case number of sheets has dropped.
  var sheet_ctr = Number(num_sheets);
  var stats = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(main_sheet);
  if (sheet_ctr) {
    if (sheet_ctr >= sheets_length) {
      for (var i=0 ; i<sheet_ctr ; i++) {
        stats_sheet.getRange(sheet_col+(sheet_row_start+i)).clearContent();
      }
    }
  }
}

function _get_sheets() {
  // Gather our sheets, can be combined with a regex to prune sheets.
  var out = new Array();
  var sheets = SpreadsheetApp.getActiveSpreadsheet().getSheets();
  for (var i=0 ; i<sheets.length ; i++) {
      out.push( sheets[i].getName() );
    }
  }
  return out  
}

function _set_sheet_cells() {
  var userProperties = PropertiesService.getUserProperties();
  var num_sheets = Number(userProperties.getProperty('sheet_names'));
  var sheets = _get_sheets();
  if (num_sheets == sheets.length) {
    // Do nothing, no changes, remove if concerned about renaming sheets.
    return;
  }
  _clear_sheet_cells(num_sheets, sheets.length);
  var stats = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(main_sheet);
  for (var j=0 ; j<sheets.length ; j++) {
    // Put one sheet name per row.
    stats_sheet.getRange(sheet_col+(sheet_row_start+j)).setValue(sheets[j]);
  }
  userProperties.setProperty('num_sheets', sheets.length)
}

Once I had all the sheets loading dynamically, I just used @cgm990's answer, with the benefit that if I added another sheet to the spreadsheet, it automatically updated all my sums.

Silfheed
  • 11,585
  • 11
  • 55
  • 67