Skip to content

Instantly share code, notes, and snippets.

@brainysmurf
Last active November 2, 2022 00:20
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save brainysmurf/b3bf73bf47384635f9d7 to your computer and use it in GitHub Desktop.
Save brainysmurf/b3bf73bf47384635f9d7 to your computer and use it in GitHub Desktop.
Convenient Fill Down On Submit script for Google Sheets with linked Google Form
/*
by ClassroomTechTools.com
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
INSTRUCTIONS:
In a Google Sheet linked with a Google Form:
Copy, paste, and save this file into a script file (Tools -> Script Editor)
Prepare the data:
1) Make a second row below the first (so you basically have two header rows)
2) Give new columns a label (content of label name is not significant)
3) Define the second row under that new column "FillDown" (exactly that)
4) Define the third row (the first row with data) as the formula you desire
Prepare the trigger:
Set up an on submit trigger to run "fillDownOnSubmit"
Go to the form, and add a new response.
When you go back to the sheet, you'll see that the data is auto filled down for you.
*/
/*
Two utility functions that provide for classical classes in js, from http://speakingjs.com/es5/ch17.html
*/
function copyOwnPropertiesFrom(target, source) {
Object.getOwnPropertyNames(source) // (1)
.forEach(function(propKey) { // (2)
var desc = Object.getOwnPropertyDescriptor(source, propKey); // (3)
Object.defineProperty(target, propKey, desc); // (4)
});
return target;
};
function subclasses(SubC, SuperC) {
var subProto = Object.create(SuperC.prototype);
copyOwnPropertiesFrom(subProto, SubC.prototype);
SubC.prototype = subProto;
SubC._super = SuperC.prototype;
};
/*
Define an Range 'class' that lets us iterate
See which columns are there, etc.
*/
function AbstractRange(range, options) {
this.options = typeof options !== 'undefined' ? options : {};
this.range = range;
this.initSheet();
this.numHeaderRows = this.options.numHeaderRows || this.sheet.getFrozenRows() || this.defaultNumHeaderRows();
this.initHeaders();
this.initValues();
// Store any extra headers in headersExtra and make this.headers correct
this.headersExtra = null;
if (this.headers.length > 1) this.headersExtra = this.headers.slice(1);
this.headers = this.headers[0];
this.initObjects();
// For iteration:
this._curRow = 0;
this._curCol = 0;
}
AbstractRange.prototype.defaultNumHeaderRows = function () {
return 1;
}
AbstractRange.prototype.next = function () {
var curRow = this._curRow;
var curCol = this._curCol;
this._curCol += 1;
if (this._curCol >= this.values[0].length) {
this._curRow += 1;
this._curCol = 0;
}
if (this.values[curRow] && this.values[curRow][curCol]) return this.values[curRow][curCol];
return null;
}
AbstractRange.prototype.initSheet = function () {
this.sheet = this.range.getSheet();
}
AbstractRange.prototype.initHeaders = function () {
this.headers = this.sheet.getRange(1, this.range.getColumn(), this.numHeaderRows, this.range.getLastColumn()).getValues();
}
AbstractRange.prototype.initValues = function () {
this.values = this.range.getValues();
}
AbstractRange.prototype.initObjects = function () {
this.objects = [];
this.values.forEach(function (row, rowIndex) {
var rangeObject = {};
this.headers.forEach(function (header, headerIndex) {
rangeObject[header] = row[headerIndex];
}.bind(this));
this.objects.push(rangeObject);
}.bind(this));
}
function FillDownOnSubmit(range, options) {
FillDownOnSubmit._super.constructor.call(this, range, options);
}
FillDownOnSubmit.prototype.initHeaders = function () {
this.headers = [];
}
FillDownOnSubmit.prototype.initObjects = function () {
this.objects = [];
// initial values for looping through headers
var lastColumnInRange = this.range.getLastColumn();
var lastColumnInSheet = this.sheet.getLastColumn();
var headers = this.sheet.getRange(1, lastColumnInRange+1, this.numHeaderRows, lastColumnInSheet).getValues();
headers.forEach(function (headerRow, headerRowIndex) {
headerRow.forEach(function (header, headerIndex) {
if (this.passes(header, headerRowIndex, headerIndex)) {
var column = lastColumnInRange + headerIndex + 1;
obj = {
column: column,
source: this.sheet.getRange(this.numHeaderRows+1, column),
}
this.objects.push(obj);
}
}.bind(this));
}.bind(this));
}
FillDownOnSubmit.prototype.passes = function (header, headerRowIndex, headerIndex) {
if (this.options.hasOwnProperty('secondRow')) {
return headerRowIndex == 1 && header === this.options.secondRow;
}
return header !== '';
}
FillDownOnSubmit.prototype.fillDown = function () {
//this.sheet.setActiveSelection(this.range); // this results in an error on the server
this.values.forEach (function (row, rowIndex) {
this.objects.forEach(function (obj) {
var row = this.range.getRow()+rowIndex;
if (row > this.numHeaderRows+1) {
var dest = this.sheet.getRange(row, obj.column);
obj.source.copyTo(dest);
}
}.bind(this));
}.bind(this));
}
subclasses(FillDownOnSubmit, AbstractRange);
function fillDownOnSubmit(e) {
(new FillDownOnSubmit(e.range, {secondRow: "FillDown"})).fillDown();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment