I have this script which will update daily in the morning. Is there a way to add a timestamp so that it will automatically insert when it was processed?
function archive5() {
var sss = SpreadsheetApp.openById('1ji3Q02uOyrDXyrl7LcghWLcLjz-CeSUgSCU7QD0BVEk');
var ss = sss.getSheetByName('Allergen Planner');
var range = ss.getRange('T21:U25')
var data = range.getValues();
var tss = SpreadsheetApp.openById('1j_Hy2GDKE-5dJVKSeBx0qFIWHOsvZpCTBm-SgPz7MSs');
var ts = tss.getSheetByName('Post Process Historical Data');
ts.getRange(ts.getLastRow()+1,2,5,2).setValues(data); //you will need to define the size of the copied data see getRange()
}
Whenever you update your spreadsheet - or it updates itself - a function to update its data is called, right? So what you can do is just use
ss.getRange(<timestamp_cell>).setValue(new Date()).
However, if what you need is updating some specific and yet not fix row (e.g. any row on C column, depending on certain conditions), you can refer to this question, which was very well answered, and I improved a bit.
Without the spreadsheet or a little more information about what you're trying to achieve, that's the best I can help you.
Related
I have the following formula: =ArrayFormula(INDEX(Items!F2:F,MATCH(C2,Items!E2:E,0)))
I would like to extend it such that the entire C column runs the same formula for values. Please help. If a script is necessary to achieve this, I'd like to explore that option too.
Use Apps Script!
Sheet functions (formulae) work great (especially if you are a master like player0), but I find it much easier to work within Apps Script for anything much more complicated than a simple INDEX MATCH. If you are willing to learn some JavaScript, I highly recommend learning some.
Custom Functions
You can write custom sheet functions in Apps Script that you can call with the traditional =FUNCTION() from a cell.
The way it works is that you write a function in Apps Script that returns a two dimensional array corresponding to the area that it needs to fill.
For example, if wanted a function to fill a 2 x 2 block with 1, you would need to make your function return:
[[1,1],[1,1]]
Or you can write it like this:
[
[1, 1],
[1, 1]
]
Implementing Index Match
There are many ways you can implement it, here is an example.
The example spreadsheet has 2 tabs, "Ledger" and "Items".
The goal of the function that follows is to get the costs of the items from the "Items" tab.
function ledgerIndexMatch(){
// Initializing the location of data
let ss = SpreadsheetApp.getActive();
let ledger = ss.getSheetByName("Ledger");
let source = ss.getSheetByName("Items");
let ledgerRange = ledger.getDataRange();
let sourceRange = source.getDataRange();
// Getting the values into a 2D array
let ledgerValues = ledgerRange.getValues();
let sourceValues = sourceRange.getValues();
// Discarding the first row (headers)
ledgerValues.shift();
sourceValues.shift();
// Initializing the output array
let output = [];
// This is where the INDEX MATCH happens
// For each row in ledger
ledgerValues.forEach(ledgerRow => {
// Get the second column (index 1)
let item = ledgerRow[1];
// Initialize the column
let value = [];
// For each row in the source
sourceValues.some(sourceRow => {
// Check if the item is there
if (item == sourceRow[0]) {
// if so, add to value
value.push(sourceRow[1]);
// stop looking for values
return true
// if not matched, keep looking
} else return false
})
// Add the found value (or blank if not found)
// to the output array.
output.push(value);
})
return output;
}
Which can be used like this:
Whats nice about Apps Script is that you can customize it to your heart's content. In this example, the function automatically detects the height of the respective tables, so you don't need to fiddle around with ranges.
You might want to extend this function with arguments so that its more flexible. Or you could just have a few versions of it for different operations, if you don't have too many. Or refactor it... its up to you.
References
Apps Script
Custom Functions
Tutorials
SpreadsheetApp
use:
=ARRAYFORMULA(IFNA(VLOOKUP(C2:C, Items!E2:F, 2, 0)))
I'm trying to make a Google form populate a spreadsheet with multiple rows based on 1 form entered data like this:
Form is simple with minimal customer information's https://docs.google.com/forms/d/1LrKlVuI7kxVU0lxz70Uu-2Obj4x3qIwe6nS-ErzbCAg/
Based on selected number of parts needed by customer (at the moment 1, 2 or 3) Form goes to sections where 1, 2 or 3 are entered by parts name and identification number https://docs.google.com/spreadsheets/d/1kAsgrB-2swL_tWYyXF5GLLX2PkVrkHYkcKr9eaQ8gnI/edit?usp=sharing
After entry i need for Forms to enter data in Sheet (or formatted sheet) as follows:
-Each parts entered (1, 2 or 3) should be on individual row with the same client name in common
Sheet editors can then enter data in extra rows such as "Delivery date" and "Item price"
short:
1 form submitted, 3 questions in form, 3 rows
p.s. cell coloring is used only to point out the common data between rows
One option would be to do the following:
#1. Install onFormSubmit trigger:
Install an onFormSubmit trigger attached to your spreadsheet, so that a function runs every time the form attached to the spreadsheet is submitted (this assumes that your Form is attached to your spreadsheet).
The trigger can be installed either manually, following these steps, or programmatically. To install the trigger programmatically, open a script bound to your spreadsheet by clicking Tools > Script editor, and copy an execute this function once:
function createTrigger() {
var ss = SpreadsheetApp.getActive();
ScriptApp.newTrigger("submitData")
.forSpreadsheet(ss)
.onFormSubmit()
.create();
}
This will fire a function called submitData every time the form is submitted. Next, then, would be to write function, which should append the submitted data in the format you desire.
#2. Function to append submitted data to sheet:
In order to append the data submitted through the Form on the sheet called Formated responses, you need to use the corresponding event object, which contains the submitted data. You can use this to check how many parts are submitted and the values for its corresponding fields. Then, the method appendRow could be used to append this data to the sheet.
It could be something like this:
function submitData(e) {
var sheet = SpreadsheetApp.getActive().getSheetByName("Formated responses");
var res = e.namedValues;
var numberOfParts = res["Number of parts"][0];
var mainFields = [res["Timestamp"][0], res["Client name"][0], numberOfParts];
switch (numberOfParts) {
case '1':
var fieldsOne = [res["Part identification number"][0], res["Part name"][0]];
sheet.appendRow(mainFields.concat(fieldsOne));
break;
case '2':
var fieldsTwo = [res["#1 part identification number"][0], res["#1 part name"][0]];
sheet.appendRow(mainFields.concat(fieldsTwo));
fieldsTwo = [res["#2nd part identification number"][0], res["#2nd part name"][0]];
sheet.appendRow(mainFields.concat(fieldsTwo));
break;
case '3':
var fieldsThree = [res["#1st part identification number"][0], res["#1st part name"][0]];
sheet.appendRow(mainFields.concat(fieldsThree));
fieldsThree = [res["#2nd part identification number"][1], res["#2nd part name"][1]];
sheet.appendRow(mainFields.concat(fieldsThree));
fieldsThree = [res["#3rd part identification number"][0], res["#3rd part name"][0]];
sheet.appendRow(mainFields.concat(fieldsThree));
break;
}
}
Note:
The function submitData could be made simpler (the switch could probably be removed, and a for loop used instead), but the names of the Form fields are not consistent with each other, hindering this option. Because of this, the function has a fair amount of repetition. I'd recommend you to fix the field names and rewrite the function a bit.
Reference:
Installable Triggers
Thanks for reading my first question. I'm just starting out with google sheets to please bear with me.
Here is the sheet I'm working with
So,this sheet if for a game I play and we assign guild attacks based on power levels. I'm looking to create a function or script and put it in Column O. I'd like this function to compare Column F to a specific cell in Column M and then return the user associated with Column F that is >= than the specific cell in Column M. Like this enter image description here
I highlighted the first three as an example.
I can obviously do this manually but I takes time, and was looking to automate this process so it becomes more efficient. I've tried Vlookups, MATCH, IF and I've been unsuccessful. Any help would be greatly appreciated. Again, I'm just a beginner with google sheets so please go easy on me. :)
Solution
As you metioned that you would also be happy with an scripted solution I have created this script that I believe solves your issue. It has comments explaining step by step how it works:
function myFunction() {
// Get our sheet
var ss = SpreadsheetApp.getActive().getSheetByName('Automate Test');
// Get the values of the ranges of F and M. Flat will convert the 2D array we get from getValues() into a 1D one which is easier to work with
var valuesF = ss.getRange('F2:F16').getValues().flat();
var valuesD = ss.getRange('D2:D16').getValues().flat();
var valuesM = ss.getRange('M2:M16').getValues().flat();
var valuesN = ss.getRange('N17:N31').getValues().flat();
// We will iterate through all the players in column M to find their opponents
for(i=0;i<valuesM.length;i++){
// We create an empty array to be filled with the list of possible opponents to then choose a random one of the list
var playersHigherEqual = [];
// Iterate through the opponent list
for(j=0;j<valuesF.length;j++){
// If the opponent meets the condition
if(valuesF[j]>= valuesM[i]){
// Add it to the array of possible opponents
playersHigherEqual.push(ss.getRange(j+2, 2).getValue());
}
}
//Finally we will set the opponent by choosing a random one out of the list. Note that i+2 is because the arrays start from 0
ss.getRange(i+2, 15).setValue(playersHigherEqual[Math.floor(Math.random()*playersHigherEqual.length)]);
}
// We will iterate through all the players in column M to find their opponents
for(i=0;i<valuesN.length;i++){
// We create an empty array to be filled with the list of possible opponents to then choose a random one of the list
var playersHigherEqual = [];
// Iterate through the opponent list
for(j=0;j<valuesD.length;j++){
// If the opponent meets the condition
if(valuesD[j]>= valuesN[i]){
// Add it to the array of possible opponents
playersHigherEqual.push(ss.getRange(j+2, 2).getValue());
}
}
//Finally we will set the opponent by choosing a random one out of the list. Note that i+2 is because the arrays start from 0
ss.getRange(i+17, 15).setValue(playersHigherEqual[Math.floor(Math.random()*playersHigherEqual.length)]);
}
}
Please let me know if you also need a sheet formula solution for this question. For more information about Apps Script check out the documentation.
I hope this has helped you. Let me know if you need anything else or if you did not understood something. :)
I am trying to sort out a performance issue in doing a data restore on iOS (i.e. data exists in a table, then I update some of it from a flat file backup). My actual case is 1250 times slower on iOS than Windows. I started with the raindrops example from Dexie.bulkPut, which does not exhibit this behavior (it's slower, but only by about 15%, and gradually modified it to be more like what I need to do.
What I have found is that if my table has a single compound key, I can bulkPut the data twice, and it takes nearly the same amount of time both times. But if there are two compound keys, the second write takes about the same time on the computer, but much, much longer on iOS (times in seconds).
Windows Windows iOS iOS
Records First write Second write First write Second write
20,000 2.393 1.904 5.057 131.127
40,000 5.231 3.941 9.533 509.616
60,000 7.808 8.331 14.188 1205.181
Here is my test program:
var db = new Dexie("test");
db.delete().then(function() {
db.version(1).stores({
raindrops: '[matrix+row+col], [matrix+done]'
});
db.open();
var t1 = new Date();
var drops = [];
for (var i=0;i<20000;++i) { // make a matrix
drops.push({matrix: 0, row: Math.floor(i/100)+1, col: i % 100 +1, done: i%2});
}
db.raindrops.bulkPut(drops).then(function(lastKey) {
t2 = new Date();
console.log("Time in seconds " + (t2.getTime() - t1.getTime())/1000);
db.raindrops.bulkPut(drops).then(function(lastKey) {
t3 = new Date();
console.log("Reputting -- Time in seconds " + (t3.getTime() - t2.getTime())/1000);
});
});
});
I am wondering if anyone has any suggestions. I need that second index but I also need for people to be able to do a restore in finite time (my users are not very careful about clearing browser data). Is there any chance of this performance improving? The fact that it's so much better for Windows suggests that it doesn't HAVE to be that way. Or is there a way I could drop the second index before doing a restore and then re-indexing? Done is 0 or 1 and that index is there so I can get a quick count of records with done set (or not set), but maybe it would be better to count manually.
I'm trying to implement a unique identifier in column A. For this, I'm leaning towards the solution which has been discussed here: Auto incrementing Job Reference (using the Utilities.formatString() in Google sheets).
For ease of reading, here is my current script (I made some slight alterations to the original script):
function Identifier(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.getSheetByName("Epics");
var startcell = sh.getRange('A2').getValue();
var colValues = sh.getRange('A2:A').getValues();// get all the values in column A in an array
var max=0;// define the max variable to a minimal value
for(var r in colValues){ // iterate the array
var vv=colValues[r][0].toString().replace(/[^0-9]/g,'');// remove the letters from the string to convert to number
if(Number(vv)>max){max=vv};// get the highest numeric value in the column, no matter what happens in the column... this runs at array level so it is very fast
}
max++ ; // increment to be 1 above max value
sh.getRange(sh.getLastRow(), 1).setValue(Utilities.formatString('E%04d',max));// and write it back to sheet's last row.
}
The original function is designed to work with trigger 'On form submit' but I need this logic to be triggered only when a new line is added to my sheet. Trigger 'On edit' won't work because with that, every change to the sheet results in the last ID to get overwritten with the next new ID.
How can I make sure that my function is only called when a new line is added to the Googlesheet?
Edit [20-Jan.-2015]
Meanwhile I have adapted the script somewhat:
function Identifier(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.getSheetByName("Epics");
var r = ss.getActiveRange();
var editRow = parseInt(r.getRow()); // get the row the edit happend on
var lastRow = ss.getLastRow();
var startcell = sh.getRange('A2').getValue();
var colValues = sh.getRange('A2:A').getValues();// get all the values in column A in an array
var max=0;// define the max variable to a minimal value
if(lastRow == editRow){
for(var r in colValues){ // iterate the array
var vv=colValues[r][0].toString().replace(/[^0-9]/g,'');// remove the letters from the string to convert to number
if(Number(vv)>max){max=vv};// get the highest numeric value in the column, no matter what happens in the column... this runs at array level so it is very fast
}
max++ ; // increment to be 1 above max value
sh.getRange(sh.getLastRow(), 1).setValue(Utilities.formatString('E%04d',max));// and write it back to sheet's last row.
}
}
This almost works as intended. Still to solve:
With above script, the last ID-Field gets updated with each update to the last line of the sheet. I need an adaptation to above script so that updates in the last line do not result in new ID's. (must have)
When inserting a new row in an existing range, this is currently not recognised by the script. (nice to have)
I have been researching something along these lines and I can help you with part of your problem. You can create an installed trigger, and use the OnChange event to trigger when a row is inserted.
function myFunction(event){
if(event.changeType=='INSERT_ROW'){
// do Something
}
}
I am still trying to find an answer for the getting the inserted row's location.