I am trying to delete a row's data based on the active cell. I have looked through Google's documentation and I cannot figure out how to do this. I have also searched the forums and found a couple of similar scenarios, but I cannot get it to work. I tried to do this by lopping through an array of data and deleting the row based on the value in the array, but this does not work either. I am sure that I am overcomplicating things here. Here is the code that I have so far...
function clearProject() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var source = ss.getSheetByName('Projects');
var sourceArray = source.getRange(1,1,10).getValues();
var sourceCell = source.getActiveCell();
var sourceData = sourceCell.getValues();
//var sourceRow = sourceCell.getRange(sourceCell, );
var target = ss.getSheetByName('Timeline').getRange(1, 1, 10).getValues();
for (var i = 0; i < sourceArray.length; i++){
if(i == sourceData){
sourceCell.clearContent();
}
}
Thank you for any help that you can afford!
First of all you cannot compare like this if(i == sourceData) due to getValues returns
...the rectangular grid of values for this range. Returns a
two-dimensional array of values, indexed by row, then by column. The
values may be of type Number, Boolean, Date, or String, depending on
the value of the cell. Empty cells will be represented by an empty
string in the array. Remember that while a range index starts at 1, 1,
the JavaScript array will be indexed from [0][0].
And I didn't catch your goal, could you plesae add some more explanation?
It could be as simple as that :
function delRowsInActiveRange() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var source = ss.getActiveRange();
var height = source.getValues().length;
var rowPosition = source.getRowIndex();
ss.getActiveSheet().deleteRows(rowPosition, height);
}
This function deletes all the rows that include selected cells.
Related
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 would like to create a filter for a column in a spreadsheet, then retrieve the list of default criteria values created for the filter. I believe that my code returns a Filter object without any values for it.
function TestFilter(){
var spreadsheet = SpreadsheetApp.getActive();
var sheet = spreadsheet.getActiveSheet();
sheet.getRange(1, 2, sheet.getMaxRows(), 1).createFilter();
var filter = sheet.getFilter();
var output = filter.getColumnFilterCriteria(2).getCriteriaValues();
return output;
}
You can use the following functions for this:
getHiddenValues()
Returns the values to hide.
getVisibleValues()
Returns the values to show.
In case your filter is set to hide all of the possible values, you will obtain what you desire by using the function getHiddenValues().
However, this will not be possible if your filter is only hiding a subset of your values. For that case, you could use a Google Apps Script function such as the following below to obtain the distinct values:
function getDistinctValues(range) {
var values = range.getValues();
var unique = {};
for (var i=0; i<values.length; i++) {
for (var j=0; j<values[i].length; j++) {
var key = values[i][j];
if (key !== null && key !== undefined && key !== '')
unique[key] = true;
}
}
return Object.keys(unique);
}
The usage of it would be, in case you were attempting to obtain the distinct values on your A column:
var distinctValues = getDistinctValues(sheet.getRange("A2:A"));
Note that this function will return the values as Strings. In case you want to obtain the actual numeric value instead of a String, you can parse the values simply by using the following code:
var distinctValues = getDistinctValues(sheet.getRange("A2:A")).map(parseFloat);
I believe there is a bug with 2 out of 3 of these functions, by using something like this:
var filter = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("sheetName").getFilter();
var criteriaValues = filter.getColumnFilterCriteria(9).getCriteriaValues();
Logger.log("criteria Values length " + criteriaValues.length);
Logger.log(criteriaValues);
var visibleValues = filter.getColumnFilterCriteria(9).getVisibleValues();
Logger.log("visible Values length " + visibleValues.length);
Logger.log(visibleValues);
var hiddenValues = filter.getColumnFilterCriteria(9).getHiddenValues();
Logger.log("hidden Values length " + hiddenValues.length);
Logger.log(hiddenValues);
and setting a filter on column I (9th from the left) regardless of how many or which values I filter by, I only ever see the values that I've hidden from the column, the criteriaValues and visibleValues arrays are always empty, while hiddenValues always shows correctly the values that are filtered out.
If someone could double check this and confirm it would be great, otherwise, maybe I'm just doing something really silly, in which case please let me know as well :).
This is created based on https://developers.google.com/apps-script/reference/spreadsheet/filter-criteria.html
Blockquote
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.
I ask the same matrix table (multiple answer) question twice in my Qualtrics survey. When I display the question to the user the second time, I want the answers to be auto populated from the first time the user answered the question. I've looked at the code snippets in the documentation, so I know how to select the checkboxes in the current question, e.g.
for (var i = 1; i <= rows; i++) {
for (var j = 1; j <= cols; j++) {
this.setChoiceValue(i, j, true);
}
}
My issue is that I can't figure out how to get the selected answers from the previous question. I have tried something like this but it doesn't work (cannot read property 'attr' of null):
var isChecked = $("#QR~QID2~" + i + "~" + j").attr('checked');
This page from the documentation suggests using piped text, something along the lines of:
var selectedChoice = "${q://QID2/ChoiceGroup/SelectedChoices}";
This seems to give me the unique statements in the rows of the survey that have been selected, but I need to get the selected answer (col) for each statement (row) in the question. I'm not sure how to formulate the correct piped text.
Any advice would be much appreciated! Thanks.
Edit 1: Here is the code I ended up using.
Qualtrics.SurveyEngine.addOnload(function()
{
/*Place Your Javascript Below This Line*/
var numChecks = $(this.getQuestionContainer()).select('input[type="checkbox"]');
var numCols = 4;
var numRows = numChecks.length / numCols;
var map = {};
//I won't have more than 20 rows in the matrix
map[1] = "${q://QID53/SelectedAnswerRecode/1}";
map[2] = "${q://QID53/SelectedAnswerRecode/2}";
map[3] = "${q://QID53/SelectedAnswerRecode/3}";
map[4] = "${q://QID53/SelectedAnswerRecode/4}";
map[5] = "${q://QID53/SelectedAnswerRecode/5}";
map[6] = "${q://QID53/SelectedAnswerRecode/6}";
map[7] = "${q://QID53/SelectedAnswerRecode/7}";
map[8] = "${q://QID53/SelectedAnswerRecode/8}";
map[9] = "${q://QID53/SelectedAnswerRecode/9}";
map[10] = "${q://QID53/SelectedAnswerRecode/10}";
map[11] = "${q://QID53/SelectedAnswerRecode/11}";
map[12] = "${q://QID53/SelectedAnswerRecode/12}";
map[13] = "${q://QID53/SelectedAnswerRecode/13}";
map[14] = "${q://QID53/SelectedAnswerRecode/14}";
map[15] = "${q://QID53/SelectedAnswerRecode/15}";
map[16] = "${q://QID53/SelectedAnswerRecode/16}";
map[17] = "${q://QID53/SelectedAnswerRecode/17}";
map[18] = "${q://QID53/SelectedAnswerRecode/18}";
map[19] = "${q://QID53/SelectedAnswerRecode/19}";
map[20] = "${q://QID53/SelectedAnswerRecode/20}";
for (var i = 1; i <= numRows; i++) {
//Get the recode values for row i
var rowValues = map[i].split(",");
//Loop through all the recode values for the current row
for (var c = 0 ; c < rowValues.length; c++) {
var val = parseInt(rowValues[c].trim());
//Select the current question's checkboxes corresponding to the recode values
this.setChoiceValue(i, val, true);
}
}
});
Edit 2:
I'm getting some strange behavior now. I'm trying to populate a table with only 3 rows, so I would think that
"${q://QID53/SelectedAnswerRecode/1}";
"${q://QID53/SelectedAnswerRecode/2}";
"${q://QID53/SelectedAnswerRecode/3}";
would give me the values for the first three rows from the previous table for question "QID53" . But actually those return empty strings, and it's not until calling
"${q://QID53/SelectedAnswerRecode/5}";
"${q://QID53/SelectedAnswerRecode/6}";
"${q://QID53/SelectedAnswerRecode/7}";
that I get the first three values.
For a table of 14 rows, nothing returns until calling
"${q://QID53/SelectedAnswerRecode/4}";
and it leaves the last 3 rows in the table empty.
Am I wrong in assuming that the number after "SelectedAnswerRecode" is the row number? Is there something about an offset that I'm missing?
I think you'll want to pipe in the recode values. Something like:
var r1ar = parseInt("${q://QID2/SelectedAnswerRecode/1}");
var r2ar = parseInt("${q://QID2/SelectedAnswerRecode/2}");
Then set the values:
this.setChoiceValue(1, r1ar, true);
this.setChoiceValue(2, r2ar, true);
Make sure your recode values match the column ids.
Edits/additions based on comments below:
Piped values in the javascript get resolved server side before the page is sent to the browser, so they are fixed values. There is no way to make them dynamic in javascript. If your question has a variable number of rows due to display logic or carryover, you'll have to include all the possible piped values in the javascript, then check them to see which ones are valid.
For a multiple answer matrix, you can convert the comma separated list into an array using str.split(',') then loop through the array. Qualtrics includes spaces after the commas in comma separated lists, so you'll have to trim() the strings in the array.
I have a sheet which has some user details in different columns, in the last column "G" I have a unique ID which is the string 'UID' I'd like to iterate each row until it finds the one with the UID in column G, I then need it to return the row number for that user.
I've cherry picked different bits of different tutorials and answers and have come up with the below. I've tried debugging and it keeps returning 0.0 for the row.
There are users which do not have unique IDs, could that be the issue?
Any ideas?
var data = getQueryString("?"+claim_code);
var UID = data.uniqueid;
Logger.log(UID);
// Find out the row from the UID
var column = response.getRange('G:G');
var values = column.getValues();
var row = 0;
while ( values[row][0] == UID ) {
row++;
Logger.log(row);
}
You need to do the while only on values and use !=
Because in that way, it will go over your values UNTIL it find the row you are looking at.
Btw, you might want to limit the range to the specific amounts of rows you have. It's will run much faster than the current G:G that cause it to run on all your rows.
Here is the code that will find the value for you:
while ( values[row] != UID ) {
row++;
Logger.log(row);
}
You can get all the values of the sheet, and then iterate eachrow to find your value:
The example is here: https://docs.google.com/spreadsheets/d/1LGv_u_z_DuUpW01o8yMpHkceM3N45G3ruqQ8JtHkNEs
function find(){
var idToFind = "A12347";
var userRow = findID(idToFind);
Logger.log(userRow);
}
function findID(userID) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var values = sheet.getSheetValues(2, 1, sheet.getLastRow()-1, sheet.getLastColumn());
for(var x=1; x<values.length; x++){
if(userID == values[x][6]){
return x;
}
}
return 0; // Error, not found userID
}