I am very new to JavaScript so please forgive the question if easy.
I have created a Google Sheet. I have used the first tab called Form, to create a data entry form, this data is then saved, using JavaScript to another tab called, Removal_list. All works fine.
I have added a search function to recall the data from the Removal_list to Form using names. What I cannot work out is how to show not just the first object that meets the criteria. Example, I will have a list of names (column B in the sheet), which can be duplicated. When I search it will only return the first occurrence. What I would like is the search to allow me to see each occurrence, one at a time. So if John Smith is in my list five times, the first occurrence will populate (as it does now), if this is not the one I want to see then, I somehow, move on to the next occurrence.
// search values
var SEARCH_COL_IDX = 1
function Search() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var formS = ss.getSheetByName("Form");
var valuesFound = false;
var str = formS.getRange("D18").getValue();
var values = ss.getSheetByName("Removal_list").getDataRange().getValues();
for (var i = 0; i < values.length; i++) {
var row = values[i];
if (row[SEARCH_COL_IDX] == str) {
formS.getRange("B5").setValue(row[0]);
formS.getRange("B7").setValue(row[1]);
formS.getRange("B9").setValue(row[2]);
formS.getRange("B11").setValue(row[3]);
formS.getRange("B13").setValue(row[4]);
formS.getRange("B15").setValue(row[5]);
formS.getRange("E5").setValue(row[6]);
formS.getRange("E7").setValue(row[7]);
formS.getRange("E9").setValue(row[8]);
formS.getRange("E11").setValue(row[9]);
formS.getRange("E13").setValue(row[10]);
formS.getRange("E15").setValue(row[11]);
formS.getRange("G9").setValue(row[12]);
formS.getRange("G11").setValue(row[13]);
formS.getRange("G14").setValue(row[14]);
valuesFound=true;
return;
}
if(valueFound=false){
var ui=SpreadsheetApp.getUi();
ui.alert("No record found");
}
}}
Related
This is my first Google Script and I'm struggling a little bit while trying to bring back the values from a specific cell & sheet to another sheet.
I have a total of 18 columns, being the first one the ID which is going to be the input that the user would need to add in order to retrieve the data from one sheet to another. As the first one is the ID, and will be already be inputted by the user, I would need to retrieve the data from columns 2 to 18
Here is my code:
function SearchID() {
var columnIndex = 0;
var ss = SpreadsheetApp.getActiveSpreadsheet();
var formS = ss.getSheetByName("Manual Costs App"); // App sheet
var str = formS.getRange("D6").getValue(); // The ID to search will be inputted here
var dataS = ss.getSheetByName("Inputs").getDataRange().getValues(); // Retrieving the data based on the user input
for (var i = 0; i<=dataS.length; i++) {
var row = dataS[i];
if (row[columnIndex] == str) {
formS.getRange("D9").setValue(row[1]);
formS.getRange("D13").setValue(row[2]);
formS.getRange("D14").setValue(row[3]);
formS.getRange("D15").setValue(row[4]);
formS.getRange("D16").setValue(row[5]);
formS.getRange("D18").setValue(row[6]);
formS.getRange("D19").setValue(row[7]);
formS.getRange("D20").setValue(row[8]);
formS.getRange("D21").setValue(row[9]);
formS.getRange("D22").setValue(row[10]);
formS.getRange("D23").setValue(row[11]);
formS.getRange("D25").setValue(row[12]);
formS.getRange("D26").setValue(row[13]);
formS.getRange("D27").setValue(row[14]);
formS.getRange("D28").setValue(row[15]);
formS.getRange("D29").setValue(row[16]);
formS.getRange("D30").setValue(row[17]);
break;
}
}
}
The link to a sample spreadsheet of what I'm building is here
Update: Everything is fixed now! What I did was removing the space in the for loop. After that, it retrieved the data but a TypeError: Cannot read property '0'. Also solved it adding a break after the loop to avoid it.
It's a type in your for loop, notice how spelled length:
for (var i = 1; i <= values.lenght; i++)
You also don't want to set values line by line like you do, get a longer range and set the values with setValues() rather than setValue(). There's quite a bit of refactoring to do there actually.
The user did found the answer and updated it on the question.
Posting here as community wiki so it can be seen more clearly.
User's answer:
Update: Everything is fixed now! What I did was removing the space in the for loop. After that, it retrieved the data but a TypeError: Cannot read property '0'. Also solved it adding a break after the loop to avoid it.
function SearchID() {
var columnIndex = 0;
var ss = SpreadsheetApp.getActiveSpreadsheet();
var formS = ss.getSheetByName("Manual Costs App"); // App sheet
var str = formS.getRange("D6").getValue(); // The ID to search will be inputted here
var dataS = ss.getSheetByName("Inputs").getDataRange().getValues(); // Retrieving the data based on the user input
for (var i = 0; i<=dataS.length; i++) {
var row = dataS[i];
if (row[columnIndex] == str) {
formS.getRange("D9").setValue(row[1]);
formS.getRange("D13").setValue(row[2]);
formS.getRange("D14").setValue(row[3]);
formS.getRange("D15").setValue(row[4]);
formS.getRange("D16").setValue(row[5]);
formS.getRange("D18").setValue(row[6]);
formS.getRange("D19").setValue(row[7]);
formS.getRange("D20").setValue(row[8]);
formS.getRange("D21").setValue(row[9]);
formS.getRange("D22").setValue(row[10]);
formS.getRange("D23").setValue(row[11]);
formS.getRange("D25").setValue(row[12]);
formS.getRange("D26").setValue(row[13]);
formS.getRange("D27").setValue(row[14]);
formS.getRange("D28").setValue(row[15]);
formS.getRange("D29").setValue(row[16]);
formS.getRange("D30").setValue(row[17]);
break;
}
}
}
I am using google sheets quite a lot, but now I am trying to use google apps script to get and update dynamic data retrieved from formulas into a static table.
So, I have a sheet called 'dynamique', with formulas retrieving, filtering and sorting data from other spreadsheets.
I want to be able to work on this data, so I am trying to create a button which would copy all the values from the 'dynamique' sheet into another sheet called 'statique'. That is, I want a formula which would check if the values from the column C of the 'dynamique' sheet are in the column C of the 'statique' sheet. And if the values aren't there, I want the script to copy them. (columns A and B are empty)
I've managed to get my script to work for one column, but now, I want to copy the whole line.
For example, if the value in dynamique!C10 can't be found in statique!C:C, my script writes the value of dynamique!C10 in the first empty cell of the column statique!C:C. But I want it to write dynamique!C10:J10 into my destination sheet (say it's going to be maybe statique!C8:J8).
Here is my code, working for only one cell.
function dynamicToStatic() {
var dynSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("dynamique");
var staSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("statique");
var dynLength = dynSheet.getRange("C1:C").getValues().filter(String).length;//.getLastRow();
var staLength = staSheet.getRange("C1:C").getValues().filter(String).length;
var staRange = staSheet.getRange(6,3,staLength-1);
var staValues = staRange.getValues();
var rangeToCheck = dynSheet.getRange(6,3,dynLength-1,8);
var valuesToCheck = rangeToCheck.getValues();
var numRows = rangeToCheck.getNumRows();
var staNumRows = staRange.getNumRows();
for (i = 0; i<= numRows; i++) {
var row = valuesToCheck[i];
var index = ArrayLib.indexOf(staValues , -1 , row);
if (index == -1) {
//if (staValues.indexOf(row) != -1) {
staSheet.getRange(i+6,3,1,8).setValues(row);
}
}
var timestamp = new Date();
staSheet.getRange(4,3).setValue('List updated on the: '+timestamp);
}
Now I can't manage to retrieve the whole line of the array, so as to be able to copy it using range.setValues(). I always get error messages.
Any help would be more than appreciated...
function gettingFullRows() {
const ss=SpreadsheetApp.getActive();
const sh=ss.getSheetByName('Sheet1');
const shsr=2;//data startrow
const vA=sh.getRange(shsr,1,sh.getLastRow()-shsr+1,sh.getLastColumn()).getValues();
let html='';
vA.forEach((r,i)=>{
html+=Utilities.formatString('<br />Row:%s is %s',i+shsr,r.join(','));
});
SpreadsheetApp.getUi().showModelessDialog(HtmlService.createHtmlOutput(html), "Row");
}
So i did some re-writing to your code and made some comments in there. I hope this will make some things clear.
Array's are 0 indexed. So if the value is NOT found in the .indexOf then it would return -1. Also (for speed) i first push all the result to a array and then set the array in one "action" this saves a lot of time. The calls to and from a sheet takes the most time.
For the conversion to a 1d array i used spread operator
See this link for difference in const / var / let
The timestamp string i updated with the use of Template literals
If you have some questions, shoot! (also i did not test this ofcourse)
function dynamicToStatic() {
const dynSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("dynamique");
const staSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("statique");
const dynValues = dynSheet.getRange(1,3,dynSheet.getLastRow(),8).getValues();
//This is a 2d array
const staRaw = staSheet.getRange(6, 3, staSheet.getLastRow()).getValues();
//Convert to 1d array, for the indexoff later on, this way it is easier.
const staValues = [].concat(...staRaw);
//to store the output, as a 2d array, inside the if you see i push it in as array so you have the 2d array for the setValues.
const output = [];
for (let i = 0; i < dynValues.length; i++){
//i = the index of the array (row) inside the array of rows, the 0 would be the values of column C.
if (staValues.indexOf(dynValues[i][0]) >= 0){
output.push([dynValues[i]]);
}
}
//Start by the lastrow + 1, column C(3), ouput is a array of arrays(rows), then get the [0].lengt for the columns inside the row array.
staSheet.getRange(staSheet.getLastRow()+1, 3, output.length, output[0].lenght).setValues(output);
const timestamp = new Date();
staSheet.getRange(4,3).setValue(`List updated on the: ${timestamp}`);
}
I'm new with Google scripts and now I have to make a form with a list of choices. These choices should be picked up from the Google sheet.
So the first question is how to chose only unique values from some range of my spreadsheet?
The second is how to pass this list so that they will be the items in the list?
The code I've tried is:
function getMembranesList() {
var ss = SpreadsheetApp.openByUrl("https://docs.google.com/spreadsheets/......");
var itemList = ss.getSheetByName('Answers').getRange("Q1:Q").getValues();
var form = FormApp.getActiveForm();
var item = form.addListItem()
item.setTitle('test question');
item.createChoice(itemList);
}
Looking at the methods available to populate the ListItem, you have to choose one and set your data up so it matches the expected input. For my example, I chose the setChoiceValues method, which looks for an array. So I have to manipulate the items into an array.
One thing the getRange.getValues() method does NOT get you is how many non-blank items are returned in the list. I used this quick way to get a count of those items, so I have a maximum bound for my loops. Then, I formed the itemArray and added only the non-blank items to it.
After that, it's just a matter of creating the ListItem and adding the values:
function getMembranesList() {
var ss = SpreadsheetApp.openByUrl("https://docs.google.com/spreadsheets/...");
var itemList = ss.getSheetByName('Answers').getRange("Q1:Q").getValues();
var itemCount = itemList.filter(String).length;
var itemArray = [];
for (var i = 0; i < itemCount; i++) {
itemArray[i] = itemList[i];
}
var form = FormApp.getActiveForm();
var item = form.addListItem();
item.setTitle('test question');
item.setChoiceValues(itemArray);
}
I'm having an issue pulling the correct values out of a for loop in Google Sheets.
Here's my code:
Note: this is a snippet from a larger function
function sendEmails() {
var trackOriginSheet = SpreadsheetApp.getActiveSpreadsheet().getName();
var getMirSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Miranda");
//Set a new object to hold conditional data
var holdingData = new Object();
//Create function to get values from origin sheet
var returnedValues = function (trackOriginSheet) {
//Load dynamic variables into an object via returnedValues()
if (trackOriginSheet === getMirSheet) {
var startMirRow = 2; // First row of data to process
var numRowsMir = 506; // Number of rows to process
// Fetch the range of cells A2:Z506
var dataRangeMir = getMirSheet.getRange(startMirRow, 1, numRowsMir, 26);
// Fetch values for each cell in the Range.
var dataMir = dataRangeMir.getValues();
for (var k in dataMir) {
var secondRowMir = dataMir[k];
var intRefDescMir = secondRowMir[3];
var intAdminActionsMir = secondRowMir[4];
//Push returned data to holdingData Object
holdingData.selectedData = secondRowMir;
holdingData.refDesc = intRefDescMir;
holdingData.adminActions = intAdminActionsMir;
}
}
}
Here's a copy of the sheet I'm working on
What I need to have happened here first, is track the origin sheet, then create an object to hold data returned from the returnedValues() function. Later, I'll call the properties of this object into a send email function.
The problem is that I need to be able to pull data from the selected sheet dynamically (the "Miranda" sheet in this case.) In other words, when a user selects the "Yes" option in column I of the Miranda sheet, the first thing this script needs to do is pull the values of the variables at the top of the for loop within the same row that the user selected "Yes." Then, I'm pushing that data to a custom object to be called later.
It's apparent to me, that I'm doing it wrong. There's, at least, something wrong with my loop. What have I done? :)
EDIT:
After reviewing the suggestion by VyTautas, here's my attempt at a working loop:
for (var k = 0; k < dataMir.length; k++) {
var mirColI = dataMir[k][8];
var mirRefDesc = dataMir[k][2];
var mirAdminActions = dataMir[k][3];
var mirDates = dataMir[k][4];
if (mirColI === "Yes") {
var activeRowMir = mirColI.getActiveSelection.getRowIndex();
//Pull selected values from the active row when Yes is selected
var mirRefDescRange = getMirSheet.getRange(activeRowMir, mirRefDesc);
var mirRefDescValues = mirRefDescRange.getValues();
var mirAdminActionsRange = getMirSheet.getRange(activeRowMir, mirAdminActions);
var mirAdminActionsValues = mirAdminActionsRange.getValues();
var mirDatesRange = getMirSheet.getRange(activeRowMir, mirDates);
var mirDatesValues = mirAdminActionsRange.getValues();
var mirHoldingArray = [mirRefDescValues, mirAdminActionsValues, mirDatesValues];
//Push mirHoldingArray values to holdingData
holdingData.refDesc = mirHoldingArray[0];
holdingData.adminActions = mirHoldingArray[1];
holdingData.dates = mirHoldingArray[2];
}
}
Where did all that whitespace go in the actual script editor? :D
You already correctly use .getValues() to pull the entire table into an array. What you need to do now is have a for loop go through dataMir[k][8] and simply fetch the data if dataMir[k][8] === 'Yes'. I also feel that it's not quite necessary to use for (var k in dataMir) as for (var k = 0; k < dataMir.length; k++) is a lot cleaner and you have a for loop that guarantees control (though that's probably more a preference thing).
You can also reduce the number of variables you use by having
holdingData.selectedData = mirData[k]
holdingData.refDesc = mirData[k][2] //I assume you want the 3rd column for this variable, not the 4th
holdingData.adminActions = mirData[k][3] //same as above
remember, that the array starts with 0, so if you mirData[k][0] is column A, mirData[k][1] is column B and so on.
EDIT: what you wrote in your edits seems like doubling down on the code. You already have the data, but you are trying to pull it again and some variables you use should give you an error. I will cut the code from the if, although I don't really see why you need to both get the active sheet and sheet by name. If you know the name will be constant, then just always get the correct sheet by name (or index) thus eliminating the possibility of working with the wrong sheet.
var titleMirRows = 1; // First row of data to process
var numRowsMir = getMirSheet.getLastRow(); // Number of rows to process
// Fetch the range of cells A2:Z506
var dataRangeMir = getMirSheet.getRange(titleMirRows + 1, 1, numRowsMir - titleMirRows, 26); // might need adjusting but now it will only get as many rows as there is data, you can do the same for columns too
// Fetch values for each cell in the Range.
var dataMir = dataRangeMir.getValues();
for (var k = 0; k < dataMir.length; k++) {
if (dataMir[k][7] === 'Yes') { //I assume you meant column i
holdingData.refDesc = dataMir[k] //this will store the entire row
holdingData.adminActions = dataMir[k][3] //this stores column D
holdingData.dates = dataMir[k][4] //stores column E
}
}
Double check if the columns I have added to those variables are what you want. As I understood the object stores the entire row array, the value in column called Administrative Actions and the value in column Dates/Periods if Applicable. If not please adjust accordingly, but as you can see, we minimize the work we do with the sheet itself by simply manipulating the entire data array. Always make as few calls to Google Services as possible.
Sheet 2 has all the items and their statuses, while Sheet 1 has only some of the items from Sheet 2. I want to be able to see every time an item mentioned on Sheet 1 is listed as having a status update, i.e. e date, on Sheet 2.
Here's what I have so far, but having trouble calling the right range to work with. Is there a simpler way to do what I want to do?
function statusupdate() {
var activesht = SpreadsheetApp.getActiveSpreadsheet();
var statussht = activesht.getSheetByName("Sheet 2"); //get sheet on which status update occurs
var statusrng1 = statussht.getRangeByName('B');
var statusrng2 = statussht.getRangeByName('C');
var mainsht = activesht.getSheetByName("Sheet 1"); //get sheet where you show a specific thing has been updated, if that thing mentioned here.
var mainrng = mainsht.getRangeByName('F');
if (statusrng1 == mainrng) {
var date = statusrng2.getValue();
var daterng = mainrng.getRangeByName('E');
daterng.setValues(date);
}
}
Spreadsheet formula
You can have the rows in one sheet follow those in another without using a script. For example, say we have a sheet named Items that contains one row for every item we carry, with the item number in the first column.
We can use VLOOKUP() to search for the row containing info about individual items, and select specific columns from it.
For example, this formula would be used in B2, and could be copied to other cells in our sheet:
=VLOOKUP($A2,Items!$A$2:$C$7,COLUMN(),false)
Script
There are a few issues with your script.
.getRangeByName('B') - This method gets a named range. Given the name, I suspect you mean to get column B, and NOT a named range. If that's the case, you could use this instead:
var statusrng1 = statussht.getRange('B:B');
In A1Notation, the range B:B is the entire column B.
You intend to copy values, so there is another step required beyond identifying ranges; you need to first read the values from a range, and then later write them to a different range. For that, you need to use methods like getValues() and setValues().
Here's an updated version of your script, adapted to the example spreadsheet described above.
function statusupdate() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
//get sheet on which status update occurs
var statusSheet = ss.getSheetByName("Items");
var statusRange = statusSheet.getDataRange();
var statusData = statusRange.getValues();
//get sheet where you show a specific thing has been updated, if that thing mentioned here.
var trackingSheet = ss.getSheetByName("Tracking");
var trackingRange = trackingSheet.getDataRange();
var trackingData = trackingRange.getValues();
// Loop over all rows in the Tracking sheet to update from the Items sheet
// Start with row=1, because row 0 contains headers
for (var row=1; row<trackingData.length; row++) {
var item = trackingData[row][0];
if (item == '') continue; // skip rows without item #
// Look for item in Items sheet
var statusRow = null;
for (var sRow=1; sRow<statusData.length; sRow++) {
if (statusData[sRow][0] == item) {
// Found our match, grab that row
statusRow = statusData[sRow];
break;
}
}
// If we found a matching row, copy the status
if (statusRow) {
// Customize this depending on how your sheets are organized
trackingData[row][1] = statusRow[1];
trackingData[row][2] = statusRow[2];
}
}
// All values have been copied to trackingData, now write to sheet
trackingRange.setValues(trackingData);
}