1

let's begin with the simple form. Imagine you have a simple dataset like this one: you want to retrieve the cumulative amounts for each asset.
numbers-wo-weeks
First I would filter (arrange) the array by the asset value

  var pairs   = ["eur", "usd", "pop", "dot", "cad", "sol"];
  for(i=0; i<pairs.length; i++){
  var filtArray1  = dataArray.filter(function filt(el){return el.asset === pairs[i];});  
    filtArrays.push(filtArray1);

and then perform any sort of operations, like summing up for istance:

  var values  = Object.keys(filtArrays[i]).map(function(e){return filtArrays[i][e].amount});
  parziali.push(values);

  var somma   = values.reduce(function(acc, val) { return acc + val; }, 0);
    somme.push(somma); 

//result "somme": [9.0, 9.0, 6.0, 6.0, 9.0, 3.0]
}

ok, you can use indexOf(); or any other faster method but that's not the point.

The point is: imagine we add an extra layer to this data set now, some date field. Now we have:
numbers-casual
and, as for before, you want to be able to retrieve the amount for each asset for each week(those are week numbers of the year)..
How do you do that?
It has suddenly become exponential. What type of process can you use to keep it iterative (i.e.: automatic) and light on work-load at the same time?

You could even want to add an additional layer of data at this point..
wallets
as you can see we have two different wallets, both holding euros, but in the same week.
Now we want to be able to retrieve the amount of each asset for each week AND for each wallet. As I said, it kind of becomes exponential: how do you approach that? thanks

ps: the data was obviously previously treated with

  for(i=0; i<data.length; i++){
    var dataRow = data[i];
    var record = {};
    record['weeks']    = dataRow[0];
    record['asset']    = dataRow[1];
    record['amount']   = dataRow[2];
    dataArray.push(record);}  
John Galassi
  • 309
  • 2
  • 16
  • 2
    You can leverage sheet formulas, specifically the [QUERY](https://support.google.com/docs/answer/3093343) function to extract the data you need. You'll need to understand Google's [Visualization Query Language](https://developers.google.com/chart/interactive/docs/querylanguage) which partially implements SQL. If you need this done using GAS, check out the following stackoverflow thread: [Using Bound Google Scripts to Generate a Query Object](https://stackoverflow.com/questions/51327982/using-bound-google-scripts-to-generate-a-query-object) – TheAddonDepot Dec 28 '19 at 22:11
  • thanks man, I'll check that out tomorrow – John Galassi Dec 28 '19 at 22:43
  • @DimuDesigns thanks for your comment. I checked that out and it turns out to be something that I have been actually looking in the past days. I already had in mind to use a DB approach but I had come to the conclusion that I could only do it with this method: https://developers.google.com/apps-script/guides/jdbc#creating_other_database_connections . What you are telling me is actually relieving since if I understand well I can make my sheet my own database. I need to look into it more in detail, and I am probably going to use it in my second version of this little project – John Galassi Dec 29 '19 at 19:22

2 Answers2

1

The problem, as I understand it, is not exponential. The complexity is still linear, one is simply adding more criteria when scanning through the available data.

The technique is to accumulate the data based on the fields that you're trying to group to achieve the subtotals. For example, if you want to group by year/month, then all that is needed is the year/month to define the subtotal buckets. Eg, you will have a bucket for '20181', '20182', '20183', etc. As you loop through the entries in the array, you will use the year/month to identify the bucket and to add the entries value into that subtotal bucket.

If you are including additional fields as part of the subtotal grouping (eg, currency and year/month), then it is simply a matter of adjusting the bucket names to include both currency and year/month. Ie, your subtotal buckets will now be '~cad~20181~', '~cad~20182~', '~eur~20181~', etc. This uniquely identifies the subtotal buckets by currency and year/month. Then, as before, when looping through the entries in the array, you are taking the values from the array entry to identify the currency and year/month bucket that the value belongs... Note that the tildes are arbitrary delimiters to separate the field values when constructing the subtotal bucket names.

a = [
  {geo:"cad", ym:20182, value:3},
  {geo:"eur", ym:20181, value:1},
  {geo:"pop", ym:20182, value:2},
  {geo:"usd", ym:20181, value:3},
  {geo:"cad", ym:20182, value:3},
  {geo:"sol", ym:20181, value:1},
  {geo:"cad", ym:20181, value:3},
  {geo:"pop", ym:20182, value:2},
  {geo:"pop", ym:20181, value:5}
];

 var result = a.reduce( (totals, entry) => {
   let key = '~' + entry.geo + '~' + entry.ym + '~';
   totals[key] = ( totals[key] || 0 ) + entry.value;
   return totals;
 }, {} );


console.log( result );

Variation of the code using a for loop.

arr = [
  {geo:"cad", ym:20182, value:3},
  {geo:"eur", ym:20181, value:1},
  {geo:"pop", ym:20182, value:2},
  {geo:"usd", ym:20181, value:3},
  {geo:"cad", ym:20182, value:3},
  {geo:"sol", ym:20181, value:1},
  {geo:"cad", ym:20181, value:3},
  {geo:"pop", ym:20182, value:2},
  {geo:"pop", ym:20181, value:5}
];

result = {};

// Loop through each entry in arr

for ( let i = 0; i < arr.length; i++ ) {
  
  // Create the subtotal bucket name based on the fields
  // defining the grouping.

  let key = '~' + arr[ i ].geo + '~' + arr[ i ].ym + '~';
  
  // Now that we know which subtotal bucket arr[i].value
  // belongs, let's add it to the bucket.
  result[ key ] = (result[ key ] || 0 ) + arr[ i ].value ;

}

console.log( result );

In both examples, I've collected the values into an object with each property representing a subtotal bucket. A Map object can be used as well, but is less clear in exemplifying the critical concept of naming the subtotal buckets according to how the subtotals are to be grouped...

Hope this helps.

Trentium
  • 3,419
  • 2
  • 12
  • 19
  • if that's not too much bother I have an extension to ask you. I've tried your script and I like it very much. It's very elegant, it seems to work for my case but I am striving to understand the approach behind it. a) could you explain a bit more in depth the logic behind the passages and b) could you translate it into non-arrow syntax since GAS doesn't accept it and it makes it a bit less immediate for me to understand. e.g. I can't understand what exactly I am doing when I am feeding totals[key] or why key is still readable even with ```'|'```. Sorry I am still not very fluent – John Galassi Dec 28 '19 at 17:19
  • 1
    @JohnGalassi, I expanded the explanation of the approach. Additionally, I changed the delimiter from '|' to '~', as the pipe character is used for many purposes within javascript and my use of it as a string delimiter probably introduced some confusion. The choice of delimiter when defining the keys for the subtotal buckets is generally arbitrary... – Trentium Dec 28 '19 at 18:02
  • @JohnGalassi oh, and "why is key still readable even with '|' "? An object property name can contain just about any character. Eg, `obj = {'!@#$%': 12}` is permissible, although you can't use the dot notation to retrieve the object property. `obj.!@#$%` will throw an error, but `obj['!@#$%']` works just fine. – Trentium Dec 28 '19 at 19:10
  • Man, you can't understand how much help this solution of yours gave me. I've been stuck with this section of code for almost a week now. I had already opened a question but it was probably too vague and I didn't know how to approach it, until yesterday I ended up with this new code using filtering. And that brought me to open this question. I still can't understand fully what's the logic behind -it seems just too easy to be possible- and yet it works!! A real smart and elegant piece of coding! Where did you get the idea from this? Did you use it somewhere else already or produced just now?thks – John Galassi Dec 28 '19 at 19:57
  • 2
    @JohnGalassi, I have years of software development experience, so I'm sure I've run across a variation of this problem before. Plus I find that if I focus on the algorithm and step-by-step manual process of the problem, then it's just a matter of figuring out what tools javascript has to apply to the solution. Glad I could help. – Trentium Dec 28 '19 at 20:21
  • anyway it's a great solution. thanks again and I hope I can ask something extra if needed in the process, thks – John Galassi Dec 28 '19 at 20:38
  • Sorry to bother, but I am having a hard time to treat the result. I can't extract single values, like I would do with an array. How can I do that? – John Galassi Dec 29 '19 at 16:19
  • @JohnGalassi, based on Dimu Designs comment above, I gather that you're using Google spreadsheets. If so, try searching for posts in that genre. For example, within stackoverflow, look through https://stackoverflow.com/search?q=%5Bgoogle-apps-script%5D+getvalue . Within that search is a slew of posts about getting values from a google spreadsheet via scripting, including a lot of code examples. – Trentium Dec 29 '19 at 16:47
  • I am sorry probably I didn't explain clearly. My problem is not getting values from the cells, my problem is extracting values from the ```Result``` of the script I am now using. I get a continious string of text ```{asset: cad ym: 20181 venue: binance=3.0, asset: eur ym: 20182 venue: binance=5.0, ..``` where I can't extract single values, as I would normally do in ```{value:1.0, venue: "binance", ym:20181.0, geo:"eur"}, {value:6.0, venue: "binance", ym:20181.0, geo:"eur"},```. I know it's my fault but I cant figure out how to turn this string into somthing more usable – John Galassi Dec 29 '19 at 18:14
  • 1
    Probably worthwhile posting that as another stackoverflow question with the details of the script, as it's not clear how that is happening. – Trentium Dec 29 '19 at 19:15
  • I've tried to explain the issue better in a different question. You can check it out here if you wish, thanks https://stackoverflow.com/questions/59522987/parsing-an-unabridged-continous-set-of-string-values-into-usable-indexable-key-v?noredirect=1#comment105218140_59522987 – John Galassi Dec 29 '19 at 22:32
1

As suggested in the comments, you can use a simple query:

E1:

=query(A:D,"select A,B,C,sum(D) where A is not null group by A,B,C ",1)
TheMaster
  • 45,448
  • 6
  • 62
  • 85
  • thanks for you comment, but I would like to solve it code-side rather than with a formula; creating as many variables as many different weeks are involved. My need is not simply filtering the values, but rather creating different sets of data and then write them to the cells. – John Galassi Dec 29 '19 at 21:46
  • 1
    @John the query does produce different data and it's not simple filtering. Try and see! – TheMaster Dec 29 '19 at 21:47