Hello I have about 120 employees working on two shifts and living in 4 cities to which they drive with company cars(9 people van, so driver + 8 passengers, the cars do not always have to be full).
I have prepared a sheet with two tabs. On the first I have:
Name of the empleyee in column A
City they live in in column B
Whether they have drivers' license(yes/no) in column C
Whether they are on vacation(yes/no) in column D
And on another tab I have created a table with a car registration plate(B1) and direction it drives to in cell B2.
I am trying to create a script that will(on button press, one button per car) push the data into D2:D10 based on if:
People are living in the same city AND they are not on vacation AND at least one of them has drivers' license.
I am not sure whether I even have the right idea for that script(or what to use to push the data) because the only script I have ever written was to archive a sheet and clear cells in the original one.
I would like to get some guidance on:
What I am doing is correct
How to take the data that will pass the if conditions and put it into the table
Here is a link to a copy of the sheet
function updateCar1() {
let ss = SpreadsheetApp.getActiveSpreadsheet();
let range = ss.getRange("A2:D61");
let town = ss.getCell("cars!B2");
for(range){
if(ss.getCell("cars!B2")==town && ss.getRange("D2:D61")=="No" && ss.getRange("C2:C61")=="Yes"){
}
}
}
Your idea is correct, the remaining issue is how to loop through a range
For this
first you need to get the values of the range, since you cannot loop through a range without it
then you need to loop through the rows of the 2D value range
for each row, you can access the values in the respective columns by their indices starting with 0 for the first column (in your case A)
Sample:
function updateCar1() {
let ss = SpreadsheetApp.getActiveSpreadsheet();
let range = ss.getRange("A2:D61");
let values = range.getValues();
let town = ss.getRange("cars!B2").getValue();
for(var i = 0; i < values.length; i++){
if(values[i][1] == town && values[i][3] =="No" && values[i][2] =="Yes"){
//do something
}
}
}
Related
Dear programming Community,
at first I need to state, that I am not quite experienced in VBA and programming in general.
What is my problem? I have created a topic list in google sheets in order to collect topics for our monthly meeting among members in a little dance club. That list has a few columns (A: date of creation of topic; B: topic; C: Name of creator; ...). Since it is hard to force all the people to use the same format for the date (column A; some use the year, others not, ...), I decided to lock the entire column A (read-only) and put a formular there in all cells that looks in the adjacent cell in column B and sets the current date, if someone types in a new topic (=if(B2="";"";Now()). Here the problem is, that google sheets (and excel) does then always update the date, when you open the file a few days later again. I tried to overcome this problem by using a circular reference, but that doesn't work either. So now I am thinking of creating a little function (macro) that gets triggered when the file is closed.
Every cell in Column B (Topic) in the range from row 2 to 1000 (row 1 is headline) shall be checked if someone created a new topic (whether or not its empty). If it is not empty, the Date in the adjacent cell (Column A) shall be copied and reinserted just as the value (to get rid of the formular in that cell). Since it also can happen, that someone has created a topic, but a few days later decides to delete it again, in that case the formular for the date shall be inserted again. I thought to solve this with an If-Then-Else loop (If B is not empty, then copy/paste A, else insert formula in A) in a For loop (checking rows 1 - 1000). This is what I have so far, but unfortunately does not work. Could someone help me out here?
Thanks in advance and best regards,
Harry
function NeuerTest () {
var ss=SpreadsheetApp.getActive();
var s=ss.getSheetByName('Themenspeicher');
var thema = s.getCell(i,2);
var datum = s.getCell(i,1);
for (i=2;i<=100;i++) {
if(thema.isBlank){
}
else {
datum.copyTo(spreadsheet.getActiveRange(), SpreadsheetApp.CopyPasteType.PASTE_VALUES, false);
}}
}
The suggested approach is to limit the calls to the Spreadsheet API, therefore instead of getting every cell, get all the data at once.
// this gets all the data in the Sheet
const allRows = s.getDataRange().getValues()
// here we will store what is written back into the sheet
const output = []
// now go through each row
allRows.forEach( (row, ind) => {
const currentRowNumber = ind+1
// check if column b is empty
if( !row[1] || row[1]= "" ){
// it is, therefore add a row with a formula
output.push( ["=YOUR_FORMULA_HERE"] )
} else {
// keep the existing value
output.push( [row[0]] )
}
})
Basically it could be something like this:
function myFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Themenspeicher');
var range = sheet.getRange('A2:B1000');
var data = range.getValues(); // <---- or: range.getDisplayValues();
for (let row in data) {
var formula = '=if(B' + (+row+2) + '="";"";Now())';
if (data[row][1] == '') data[row][0] = formula;
}
range.setValues(data);
}
But actual answer depends on what exactly you have, how your formula looks like, etc. It would be better if you show a sample of your sheet (a couple of screenshots would be enough) 'before the script' and 'after the script'.
The goal is to add a note or several notes over time to a google sheet based on the value chosen.
The script will allow for a button to be added in the sheet and run the script function to append to the next available column.
For example, the name Tim is chosen. A note is written, the button is pressed to run the function and it will add it to Column D (since it is the next available column for Tim).
Another example, the name Jeff is chosen. A note is written. Since there are no more columns the append column should automatically create a new column and allow for the note to be written in "Jeff's" row, which would be Column G
Not sure if this is at all possible but hope to get some suggestions or ideas.
A
B
C
D
E
F
Mark
B+
completed assignment partly but needed extra time since..
89433
other
Tim
A
checked
Jeff
C
n/a
assignment # 4
done
other
Steve
A
completed
get file
received
Elon
B-
three out of four
check email
Here's what I think you're trying to achieve -
And, this code should help you get started -
function main() {
const name = "Steve";
const note = "HELLO"
addNote(name, note);
}
function addNote(name, note) {
const ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
const values = ss.getDataRange().getValues();
for (var rowIndex = 0; rowIndex < values.length; rowIndex++) {
let row = values[rowIndex];
let nameColumn = row[0];
if (nameColumn == name) {
let rowValues = ss.getRange(`${rowIndex+1}:${rowIndex+1}`).getValues()[0]
.filter(value => value !== "")
let newColIndex = rowValues.length;
ss.getRange(rowIndex+1, newColIndex+1).setValue(note);
}
}
}
Although, I'd highly recommend choosing something else as a unique identifier as opposed to a name because if there are 2 rows with the same name, then the code would actually add notes against both (incorrectly).
This is a very complicated question.
I'm not even sure if what I'm hoping for is possible, but I have been surprised many times before. Here we go....
Here are the links to the two spreadsheets I am modeling my situation after:
John Doe and
Jane Doe.
In these spreadsheets, I recently set up a script for making dynamic dropdown lists according to this video: DYNAMIC DEPENDENT DROP DOWN LISTS IN GOOGLE SPREADSHEETS. It works great for me, but there is a whole level on top of it that I need to establish. First and foremost, be aware that I cannot move any of the cells/rows/columns.
In my system here, I have two individuals working on a TASK (TASK1), one a Writer and the other a Reviewer. The John Doe, for example, will log their task (TASK1), and in the Total line they will indicate the TASK TYPE (Write) and WRITING PARTNER (Jane Doe). Jane Doe will do the same and then give John Doe a score after her review in column O. The roles here could also be switched-- for example, see TASK2, where Jane Doe is the Writer this time and John Doe is the Reviewer.
The difference is the formula in column F under SCORE, where it populates differently according to the TASK TYPE specified in the individual's sheet: If it is specified as a Review task for that person, it will populate with the score their Reviewer gave; if it is not specified as a Review task, it will be kept blank; if their Reviewer has not yet left a Score, it will populate the placeholder "(Score)"; and lastly, if it is specified as a Write task.......
....the idea is that I want it to populate with the SCORE value in column F for the task having the same TASK NAME in whoever's spreadsheet is specified in the WRITING PARTNER column. I've figured out how to dynamically import the correct spreadsheet using a VLOOKUP within an IMPORTRANGE function: IMPORTRANGE(VLOOKUP(E4,'Named Ranges'!B:D,3,FALSE),"'2019'!whateverrangeIneed")
But beyond that, I haven't figured out how to return the correct value from Jane Doe's spreadsheet into John Doe's spreadsheet. Please see my attempts in John Doe's spreadsheet. The biggest issue is that I need to use not only column A="Total" as a criteria, but also column B of the row above the total="The TASK NAME to reference". If I could figure that out, I could paste that formula into "FORMULATOSOLVE" of the function in column F.
Ultimately, the point is to be able to report an Average Score for John Doe the Writer, based on his review scores, using the formula AVERAGE(FILTER('2019'!F:F,'2019'!D:D="Write",'2019'!E:E<>"None")).
As mentioned, I've recently used a script for the first time, so I'm not opposed to doing that here as well if needed, but I don't have the know-how to write it myself.
Thank you immensely for any help.
Since you have tried Apps Script I have written this simple script that does what you need.
This will work with the amount of Tasks that you want, since iterates over all the column length.
function getScore() {
// GET the two sheets
var writer = SpreadsheetApp.openById('WRITER SHEET ID').getSheetByName('2019');
var reviewer = SpreadsheetApp.openById('REVIEWER SHEET ID').getSheetByName('2019');
// Get the B column of each sheet
var writerTask = writer.getRange("B2:B").getValues();
var reviewerTask = reviewer.getRange("B2:B").getValues();
// Variables to save data
var writerScore = '';
var taskName = '';
// Iterate over every single row in column B of writer
for (var i = 1; i < writerTask.length; i++) {
// Check if the actual row is empty (which means is the total row)
// and also check if the row before is not empty
if (writerTask[i] == '' && writerTask[i-1] != ''){
// Assign the values inside the previous row to taskName (will be TASK1)
taskName = writerTask[i-1].toString();
// Get the cell where the score has to go
writerScore = writer.getRange('F'+(i+2))
// Iterate over the reviewers sheet
for (var j = 1; j < reviewerTask.length; j++) {
// Check if cell is empty and check the previous cell has the same
// value from the one on the writers sheet
if (reviewerTask[j-1].toString() == taskName && reviewerTask[j] == ''){
// Assign the value from the cell on reviewers sheet to the cell on the writers sheet
writerScore.setValue(reviewer.getRange('F'+(j+2)).getValue());
}
}
}
}
}
To run the script you can set up a button on the sheet you want, see: Clickable images and drawings in Google Sheets
if I understood it right, try:
=ARRAYFORMULA(IFERROR(IF(D4="Write", VLOOKUP(E4&B3,
{IMPORTRANGE("1rrshg9qcShUCqQDEhp74aRHzcyDRgsh5IWM60Lypup8", "2019!E2:E1000")&
IMPORTRANGE("1rrshg9qcShUCqQDEhp74aRHzcyDRgsh5IWM60Lypup8", "2019!B1:B1000"),
IMPORTRANGE("1rrshg9qcShUCqQDEhp74aRHzcyDRgsh5IWM60Lypup8", "2019!F2:F1001")}, 2, 0),
IF(D4="Review", IF(O4="", "(Score)", O4), )), "no data"))
note to allow access beforehand you do above:
This problem has been solved. The script and formula in use can be seen in the google sheets by following the links. The script used was:
function WriterScore(projectName, importedSheet){
// Create a set of variables
var foundScore = false;
var score = "";
var data = importedSheet;
while(foundScore == false){
for(var i = 0; i<data.length;i++){
if(data[i][1] == projectName){
for(var q = i; q<data.length;q++){
if(data[q][1] == projectName){
}else{
score = data[q][5]
return score
}
}
}
}
if(score == ""){
return "You do not have a project by that name.";
}
}
return score
}
And the final cell formula was:
=IF(D7="Write",WriterScore(B6,IMPORTRANGE(VLOOKUP(E7,'Named Ranges'!B:D,3,FALSE),"'2019'!A:F")),IF(D7="Review",IF(O7="","(Score)",O7),""))
We are using Google sheets for tracking attendance. Previously, the teachers were entering P, T, or A (for present, tardy, absent) for each period. I would still like users to have the option to enter a value for each period in a week, however it would be a great time saver if they could enter one value for the whole day.
What I'd like is that if a value is entered into any one of the "0" periods (green columns) with a "P" or "A" (data validation limits those options) an OnEdit function would copy that same letter ("P" or "A") to the following 8 columns and then delete the original value. (without the deletion the totals on the far right columns will be off). I would not want the OnEdit to be activitated based on edits in any of the non-green columns.
I will eventually have several tabs, each one a different week, but each exactly the same... so I'm thinking the function should work within whatever the activesheet is.
https://docs.google.com/spreadsheets/d/1NKIdNY4k66r0zhJeFv8jYYoIwuTq0tCWlWin5GO_YtM/edit?usp=sharing
Thank you for your help,
I wrote some code to get you started with your project. (I am also a teacher) You will have to make some changes based on what you are going for and it can probably be optimised to run faster. Good luck!
function onEdit(e) {
//create an array of the columns that will be affected
var allColumns = [2, 10];
//get the number values of the column and row
var col = e.range.getColumn();
var row = e.range.getRow();
//get the A1 notation of the editted cell for clearing it out
var cell = e.range.getA1Notation();
//only run if the cell is in a column in the allColumns array
if(allColumns.indexOf(col) > -1) {
//run the for loop for the next 8 cells
for(var i = col + 1; i < col + 9; i++) {
SpreadsheetApp.getActiveSheet().getRange(row, i).setValue(e.value);
SpreadsheetApp.getActiveSheet().getRange(cell).setValue('');
}
}
}
I'm trying to create a Google sheet script that will take a list of names and resend them to the Google Sheet. I have two columns of data, the first column contains a persons name. The second column contains multiple cells that the person in the first cell is inviting to a party. This creates a problem, the name in column 1 might be on row 2, but if they invite 20 people then column one has blank spaces rows 3-21. It may sound pointless right now to most of you, but I want to be able to sort the sheet alphabetically by the name of the person who did the inviting, AND be able to sort it into a separate sheet alphabetically by the name of the guest in Column 2, while still keeping the person who invited them tracked as well. This is the only way I could think of accomplishing the task.
I'm currently stuck on writing the array back to the sheet, I keep getting "Incorrect range height, was 1 but should be 339." I've figured out how to successfully get an array of data, filled exactly how I wanted it, but can't seem to get this part. I've searched through here and tried to implement the solutions I find, but have had no luck.
This is what I have come up with so far, and it works up until the setValues(
function inviteSorter() {
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var current = sheet.getSheets()[0];
var lastRow = current.getLastRow();
var rangeData = current.getRange(2,1,lastRow-1,3);
var numColumns = rangeData.getNumColumns();
// Logger.log(rangeData);
var info = rangeData.getValues();
var Name = {};
// Examines the cell in the first column, if it is empty replaces it with the name from the previous cell.
for ( var i = 0; i< info.length; i++){
if (typeof(info[i][0]) == "string" && info[i][0] == ""){
Name[i] = Name[i-1];
} else{
Name[i] = info[i][0];
}
}
var data = []
for (var i = 0; i<lastRow-1; i++){
data.push(Name[i]);
}
var writeRange = current.getRange(2,1,data.length,1);
writeRange.setValues([data]);
The value you are expecting should be a 2D array, 1 column of multiple rows. What you get when using data.push(Name[i]); is a simple array of strings.
Try this way : data.push([Name[i]]); this will return an array of arrays and should satisfy the conditions for setValues(data)
( don't forget to remove the brackets in your last setValues statement )