We’ve looked previously at a technique to sum data from your Airtable base. This post looks at another adding up technique - summing data by some attribute of the data. Here’s our scenario:

We’ve got a set of scores for a set of people across multiple dates. We want to add these up by person (or in SQL terms, “group by”), to get a total score for each. We want to summarise this data in a second table, ending up with this result.

To achieve this we’re going to use the Javascript reduce method, so let’s take a quick diversion into reduce generally before building our Airtable script. From MDN

“The reduce() method executes a reducer function (that you provide) on each element of the array, resulting in single output value.”

(Aside: note that the “single output value” could be an integer, object or an array)

Let’s start with a very simple reduce example that sums up the values in an array. Here’s our script (copy this into your Airtable script block):


let array = [ 1, 2, 3, 4 ];
let total = array.reduce(
  ( accumulator, currentValue ) => accumulator + currentValue,
  0
)
console.log(total)

We have an array of integers and we want to total these up. The reduce function has two arguments - the accumulator and the currentValue. The currentValue is straightforward - this is the element of the array were currently working with as we iterate through the array. The accumulator is the value of the reduce function whose value is remembered across each iteration throughout the array, and ultimately becomes the final, single resulting value.

On the right-hand side of the arrow, we have the function we want to use at each iteration, in this case to add the new currentValue to the current value of the accumulator. You’ll also notice we have a 0 after the function, which is the initial value of the accumlator. Finally, we console.log the total to see the result.

Run the script and you will see, as expected, that the total of 10 is output to the console.

You can play around with some variants on this to get a better feels for what is happening. For example, this script:


let array = [ 1, 2, 3, 4 ];
let total = array.reduce(
  (accumulator, currentValue) => accumulator + (currentValue * currentValue),
  100
)
console.log(total)

will square each value of the array before adding it to the accumulator. Note that we have also set the accumulator to have an initial value of 100, which gives us a result of 130 when the reducer is run. There’s a lot more to reduce functions, so have a look at the examples on MDN and try some out in your script block. Diversion over.

Before we get into our Airtable script, let’s think of the result we need from the reduce function to be able to update the Summary table. We need an array of objects of the form:

[
...
  {
    "Name": "Jim",
    "Total Score": 100
  },
...
]

As we iterate throught the set of records from the Data table, we want to create a new object in our result array if the person doesn’t already exist and, if the person does already exists, we want to add the score from the current iteration to the total score for that person at that point.

Here’s our script:


let dataTbl = base.getTable('Data');
let dataQuery = await dataTbl.selectRecordsAsync();

let result = dataQuery.records.reduce((acc, record) => {
    let name = record.getCellValue('Name');
    let score = record.getCellValue('Score');
    let found = acc.find(item => item['Name'] == name);
    if (!found) {
        acc.push({'Name': name, 'Total Score': score})
    }
    else {
        found['Total Score'] = found['Total Score'] + score
    }
    return acc;
    }, []);

console.log(result)

Notice in the last line of our reduce function that the initial value is an empty array:

}, []);

and on each iteration of the function, we’re going to add an object to, or update an existing object in, the array.

The first line of the reduce function defines the output (result), the array we’re working on (dataQuery.records), the accumulator (acc) and the variable for each iteration (record):

let result = dataQuery.records.reduce((acc, record) => {

We then define two variables to hold the values we need from each record:


let name = record.getCellValue('Name');
let score = record.getCellValue('Score');

We then define a variable found, where we look through the current state of the acc array to see if an object for the current person already exists:

let found = acc.find(item => item['Name'] == name);

array.find() returns the first element that matches the condition if true or undefined if there are zero elements matching the condition.

We then have an if/else statement that uses the variable found. If found is undefined, then we create the object from the current iteration in the acc array:


if (!found) {
  acc.push({'Name': name, 'Total Score': score})
}
		

but if found returns a record from the acc array, we update its total score by adding the score of the current iteration to the existing Total Score:


else {
  found['Total Score'] = found['Total Score'] + score
}

Outputting the result to the console, we get an array of objects, each object with a Name and a Total Score.

To finish off our script we loop through the result array and create a record our Summary table with the data from each result:


let sumTable = base.getTable('Summary');
for (let res of result) {
    await sumTable.createRecordAsync(res);
    output.text('New summary record created!')
}

As we have already formatted each element of the result array to the correct format needed to create a record, we can simply pass in the element to the createRecordAsync function.

The full script is:


let dataTbl = base.getTable('Data');
let dataQuery = await dataTbl.selectRecordsAsync();

let result = dataQuery.records.reduce((acc, record) => {
    let name = record.getCellValue('Name');
    let score = record.getCellValue('Score');
    let found = acc.find(item => item['Name'] == name);
    if (!found) {
        acc.push({'Name': name, 'Total Score': score})
    }
    else {
        found['Total Score'] = found['Total Score'] + score
    }
    return acc;
    }, []);

console.log(result)

let sumTable = base.getTable('Summary');
for (let res of result) {
    await sumTable.createRecordAsync(res);
    output.text('New summary record created!')
}