Project description: Begin with a roster spreadsheet with multiple tabs (one for each group), each tab with a list of members for rows. Columns are rehearsal or program dates. Second sheet contains scanned attendance records. Open the attendance record sheet and read each record. One column contains tab name of sheet on the roster sheet. Change to that sheet and search for the scanned member. Mark the column matching the date with an X to denote attendance. Mark the scanned attendance record with an X to denote processed.
Everything is working save the final writing of the X to the sheet opened by ID. I can't figure out the syntax to update the appropriate row/col cell.
I'd appreciate some help. Thanks!
function processAttendanceRecords(){
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var ss = SpreadsheetApp.openById("1234567");
var data = ss.getDataRange().getValues();
for (var i = 1; i < data.length; i++) {
var rhrsDate = Utilities.formatDate(data[i][3], "PST", "M/d");
sheet.setActiveSheet(sheet.getSheetByName(data[i][1]));
var members = sheet.getDataRange().getValues();
var col = members[0].indexOf(rhrsDate);
for (var j = 1; j < members.length; j++) {
if (data[i][6] == members[j][0] && data[i][7] == members[j][1]) {
SpreadsheetApp.getActiveSheet().getRange(j+1,col+1).setValue('X');
SpreadsheetApp.getActiveSheet(ss).getRange(i+1,9).setValue('X');
}
}
}
}
If you are accessing spreadsheet by ID then it is good practice to mention the sheet name or else it will take the first sheet by default.
var ss = SpreadsheetApp.openById("123456").getSheetByName("Sheet1");
Since you haven't defined the sheet name you can't use getRange(Row, Column). So your need to mention the sheet name like above and then change the last line.
ss.getRange(i+1,9).setValue('X');
Related
I have two sheets. On one sheet (Sheet1), I have a column 'A' which has 5 fixed values, and a column 'B' which records the timestamp value in 'A' change. The other sheet (Sheet2) is meant to record all the changes in the first sheet in one day.
I use this simple query in the recording sheet:
=QUERY(Sheet1!A$1:X,"select * where C>="& Sheet2!D1)
with Sheet1!C has timestamps and Sheet2!D1 is the timestamp of 12:00 AM today
The problem is when I change the value of a row in C columns more than one time, instead of creating a new row in Sheet2 it change the value of that row in Sheet2 into new values.
So how do I change my code to get my desire results?
EDIT 2: here is my new code, but it doesn't help.
function importdata(x) {
// var row = x.range.getRow();
// var col = x.range.getColumn();
var addedsheet=SpreadsheetApp.getActive().getSheetByName("Change"); // Sheet where I want to keep the record of the change
var original_sheet = SpreadsheetApp.getActive().getSheetByName("Master"); //sheet where the change is happended
var compared_value = addedsheet.getRange(1,4).getValue(); // Cell D1 of sheet "Change", which has timestamp of today
var insert_area = original_sheet.getRange("A2:X").getValues() // area to get value from "Master" sheet to put into "Change"
var compared_area = original_sheet.getRange("C2:C").getValues(); // area where has timestamp
if (compared_area >= compared_value){
addedsheet.values.append([insert_area])}
} //if timestamp of one row from Master is greater than the value at Change!D1 =>append that row at the end (this is what I'm trying to do)
EDIT 3: I fixed the above code by append[insert_area][0] instead of [insert_area]
But then I have a new problem: there will a chance that a row in sheet 1 will be overwrited in sheet 2. I try something like this, but it returns nothing on the sheet.
function for_each_row(){
var addedsheet=SpreadsheetApp.getActive().getSheetByName("Change"); // Sheet where I want to keep the record of the change
var original_sheet = SpreadsheetApp.getActive().getSheetByName("Master"); //sheet where the change is happended
var compared_value = addedsheet.getRange(1,4).getValue(); // Cell D1 of sheet "Change", which has timestamp of today
var number_of_row_2 = addedsheet.getLastRow;
var number_of_row_1 = original_sheet.getLastRow();
for (var i=2; i<number_of_row_1 +1; i++){
var compared_stamp = original_sheet.getRange("C"+i).getValues();
var insert_values = (original_sheet.getRange(i,1,1,24).getValues())
if (compared_stamp > compared_value){
var insert_values = (original_sheet.getRange(i,1,1,24).getValues());
for (var j = 2; j<number_of_row_2 +1; j++){
var value_from_sheet = addedsheet.getRange(j,1,1,24).getValues();
if (insert_values ===value_from_sheet){
return
}
else(
addedsheet.appendRow(insert_values[0]))
}
}
}
}
My thought is if a row satisfies the 1st condition then the value will be check in sheet 2. If sheet 2 didn't have that row then append that row.
Issue:
If I understand you correctly, you want to do the following:
If sheet Master is edited, iterate through all rows in this sheet (excluding headers), and for each row, check the following:
Column C has a higher value than cell D1 in sheet Change.
This row, with these exact values, does not exist in Change.
If these conditions are meet, append the row to Change.
Solution:
Use filter and some to filter out rows that don't match your two conditions, and use setValues to write the resulting rows to your other sheet.
Code snippet:
function onEdit(e) {
var editedSheet = e ? e.range.getSheet() : SpreadsheetApp.getActiveSheet();
if (editedSheet.getName() === "Master") {
var addedSheet = SpreadsheetApp.getActive().getSheetByName("Change");
var compared_value = addedSheet.getRange(1,4).getValue();
var newData = editedSheet.getRange("A2:X" + editedSheet.getLastRow()).getValues();
var currentData = addedSheet.getRange("A2:X" + addedSheet.getLastRow()).getValues();
var filteredData = newData.filter(row => row[2] >= compared_value)
.filter(row => !currentData.some(currentRow => JSON.stringify(currentRow) === JSON.stringify(row)));
addedSheet.getRange(addedSheet.getLastRow()+1,1,filteredData.length,filteredData[0].length).setValues(filteredData);
}
}
Note:
An easy way to check if two rows have the same values is using JSON.stringify(row).
I'm assuming the timestamps are not Dates. If they are, you should compare them using getTime(). So you should change the corresponding code to newData.filter(row => row[2].getTime() >= compared_value.getTime()).
I am trying to count visible columns in a spreadsheet with no luck. I've been trying using SUBTOTAL function but it's applied to hidden/visible rows only. I also tried working with CELL("width") function, but it doesn't return 0 when a cell is hidden
Is there any other option to ignore hidden columns in a count formula?
You can definitely create your own custom function using Google Apps Script.
For example, the following function counts the number of visible columns in your active sheet:
function countVisibleColumns() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet()
var n_cols = sheet.getMaxColumns();
var hidden_cols = []
var cnt = 0;
for (var i=1; i<=n_cols ; i++) {
if ( sheet.isColumnHiddenByUser(i) ){
continue;}
else {cnt +=1} }
Logger.log(cnt)
return cnt;
}
You just need to click on Tools => Script editor and then copy the aforementioned code into a blank script. Then you can directly use the function as a formula in the google sheet like =countVisibleColumns().
See screenshot attached for more information.
I have a Google spreadsheet in which I record my freelance jobs. I have it set up that each line calculates whether it is paid for. (Payments are pulled from a separate sheet.)
What I would like to do is to generate an invoice, where I would select the customer and I get a listing of all unpaid entries for that customer.
Using a arrayed filter function does the job, but I can't use that as an invoice because I need the total line underneath, and would prefer the table format matching the count of entries.
Is it possible to insert such information into a Google Doc as a table, or within Sheets, to push the lines following an array down?
I thought this would be a simple enough concept but I can't find anything that does the full deal.
You could try this script. I'm not sure if the final results is what you are looking for. In case it is not, it can be easily modified:
function onEdit(e) {
//If you change the Customer in the Invoice sheet, it runs the code
if (e.range.getA1Notation() == 'A1' && e.source.getSheetName() == 'Invoice'){
var sprsheet = SpreadsheetApp.getActiveSpreadsheet();
var invoice = sprsheet.getSheetByName("Invoice");
var times = sprsheet.getSheetByName("Times");
var in_customer = invoice.getRange("A1").getValue(); //Name you selected in the dropdown menu
var data = times.getRange("A1:H").getValues(); //All the data from the Time sheet
var total = 0;
//Loops through all the data looking for unpaid subtotals from that customer
for (var i = 0; i < data.length; i++){
/*> "i" represents the row, the second number is the column
> The rows start at 0 since it is the first array position.
*/
if (in_customer == data[i][2]) {
if (data[i][7] == 'N'){
total += Number(data[i][5]); //Accumulates each subtotal into total
invoice.appendRow([data[i][0], data[i][1], data[i][3], data[i][5]]);
}
}
}
invoice.appendRow(["Total: ","","", total]);
}
}
This results in (I changed some values to test it):
As you see I added some headers.
References:
Range Class
onEdit Trigger
I have a google sheet like this example to track scores for disc golf:
https://docs.google.com/spreadsheets/d/1uxDFXg2kivZWKICeVklugyXH1OWqsq_s5qXZYzgHkt8/edit?usp=sharing
It works great for tracking day to day scores but it would be really awesome to have a single sheet at the beginning that could say everyones total scores that they have gotten. Also note that the names may not be the same in each sheet.
So in this example I would want to have a new sheet that would automatically calculate the scores from the other sheets and show:
Mike 67,71,65
George 83,70
Phillip 79,72,65
John 66,71
Henry 69
I am very unfamiliar with excel formulas and have been struggling to get this started. Any help would be greatly appreciated.
You can use a Google Apps Script to accomplish what you are looking for. The idea of the code is that it will iterate over every Sheet in your Spreadsheet, gather all the players and all their values, and finally create a summary and put it into the "Summary" sheet (that sheet must exist in your Spreadsheet, with strictly the same name):
function updateSummary() {
var sheets = SpreadsheetApp.getActive().getSheets();
var summarySheet = SpreadsheetApp.getActive().getSheetByName('Summary');
var allScores = {};
for (var i=0; i<sheets.length; i++) {
if (sheets[i].getName() == 'Summary') continue;
var nColumns = sheets[i].getLastColumn();
var names = sheets[i].getRange(1, 1, 1, nColumns).getValues()[0];
var scores = sheets[i].getRange(20, 1, 1, nColumns).getValues()[0];
for (var j=0; j<nColumns; j++) {
var currentName = names[j];
var currentScore = scores[j];
if (!allScores.hasOwnProperty(currentName))
allScores[currentName] = [];
allScores[currentName].push(currentScore);
}
}
summarySheet.clear();
for (var key in allScores) {
var row = [key].concat(allScores[key]);
summarySheet.appendRow(row);
}
}
This will create, with the data given, the following data in the "Summary" Sheet:
Instead, if you prefer to have two columns as you described in your question (with the second one holding every score separated by commas), you would simply need to replace the last for-loop in the code above for the following one:
for (var key in allScores) {
var row = [key].concat(allScores[key].join(','));
summarySheet.appendRow(row);
}
Finally, you can create an image in the "Summary" sheet which can serve as a button to run the script. To do so:
Within your Sheet, click on Insert>Image>Image over cells.
Select any image of your choice.
Select the newly created image and click on the three dots icon that appears on the top-right corner of the image.
Click on "Assign script" and put the function name (in this case, updateSummary) and click on OK.
try:
=QUERY(TRANSPOSE({
'MikeGeorgePhillipJohn 121519'!A1:D20,
'MikeGeorgePhillipJohn 122019'!A1:D20,
'MikeJosephPhillipHenry 122719'!A1:D20}),
"select Col1,sum(Col20)
where Col1 is not null
group by Col1
label sum(Col20)''", 0)
=ARRAYFORMULA(SPLIT(TRANSPOSE(QUERY(QUERY(TRANSPOSE({
'MikeGeorgePhillipJohn 121519'!A1:D20,
'MikeGeorgePhillipJohn 122019'!A1:D20,
'MikeJosephPhillipHenry 122719'!A1:D20}),
"select max(Col20)
where Col1 is not null
group by Col20
pivot Col1", 0),,999^99)), " "))
if I have a workbook with 20 or so sheets, is there a way I can easily save each one as their own workbook? Is this possible with scripts?
This can be done with a script as follows: (a) get the list of sheets; (b) for each, create a new spreadsheet, naming it after the sheet; (c) copy the sheet there; (d) delete the initial "Sheet1" sheet from the new spreadsheet, so that it doesn't get in the way.
function saveEach() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheets = ss.getSheets();
for (var i = 0; i < sheets.length; i++) {
var newss = SpreadsheetApp.create(sheets[i].getSheetName());
sheets[i].copyTo(newss);
newss.deleteSheet(newss.getSheets()[0]);
}
}
Reference: SpreadsheetApp