Google Sheets: Comparing Columns with Checkboxes - javascript

I am trying to create a to do list spreadsheet. In one column, "Daily Tasks", I have a list of tasks. In the other column, I have checkboxes. My goal is to create a script that will add all of the "checked" tasks to an array.
I am attempting to do this in my script using a nested for loop. However, when I log my new array that should contain the checked items (trueArr), I see just a series of what appear to be empty arrays.
How do I change my script so that my array contains the checked items from my spreadsheet?
Here is a link to my spreadsheet
Here is my code:
//spreadsheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
//worksheet
var todoList = ss.getSheetByName("To-Do List");
function completedArray(){
var tasks = todoList.getRange("c6:d22").getValues();
var checks = todoList.getRange("b6:b22").getValues();
var trueArr = [];
for(var i =0; i <tasks.length; i++){
for(var j=0; j<checks.length;j++){
if(checks[j]===true){
trueArr.push(tasks[j])
}
}
Logger.log(trueArr);
}
}
In my log, I expect to see the same items that are on my "Daily Tasks" list on my spreadsheet, but instead I only see empty arrays.

Explanation / Issues:
You are very close besides the following two things:
The checks array is a 2 dimensional array since you use getValues() to create it. Meaning that checks[j] is a row or in other words a 1 dimensional array. It is fundamentally wrong to compare an array with a value. Instead you should be using checks[j][0] in the if statement since this will iterate over each value of the single column. Another approach would be to use flat to convert the 2D array to 1D and then use your current code.
The second issue has to do with a for loop that you don't use anywhere. Your code iterates over i but you don't use or need i in your code. Keep also in mind that tasks and checks have the same number of rows (same length), therefore one for loop is enough in this scenario.
Solution:
//spreadsheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
//worksheet
var todoList = ss.getSheetByName("To-Do List");
function completedArray(){
var tasks = todoList.getRange("c6:d22").getValues();
var checks = todoList.getRange("b6:b22").getValues();
var trueArr = [];
for(var j=0; j<checks.length;j++){
if(checks[j][0]===true){
trueArr.push(tasks[j])
}
}
Logger.log(trueArr);
}
The structure of trueArr would be: [[task2, task2], [task3, task3], [task9, task9]] since tasks[j] is also a row or 1D array. This is why you will end up with a collection of 1D arrays.

I found a way to create the same array of checked items using the filter method and an arrow function. Made a two dimensional array that includes both checkboxes and list items. Then I filtered by the checkboxes in the first column. No need for loops!
//spreadsheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
//worksheet
var todoList = ss.getSheetByName("To-Do List");
var dailies = todoList.getRange("b6:e22").getValues();
function checkedItems(){
var checkedItems = dailies.filter(row => row[0] === true);
Logger.log(checkedItems);
}
Got the same result with a fraction of the code!

Related

How do I input an specific cell for every i in a loop on google script?

One of my projects is making a sales spreadsheet.
The sales spreadsheet contains the names of the products and their prices are in the documentation, the challenge is getting the prices to automatically show up on the cell right next to the product name in the spreadsheet.
Here's what I did:
function Autoprice() {
var sales = SpreadsheetApp.getActive().getSheetByName('Sales')
var salesrow = sales.getRange('D2:D'+sales.getLastRow())
var productnames = salesrow.getValues()
size = productnames.length+1
for (var i = 0; i< size; i++){
if (productnames[i+1]=='Diary')
{
sales.getRange('F'+i).setValue(31.90)
}
And I just input all the prices manually.
The thing is, google script does not read the sales.getRange('F'+1) as I thought it would, and I can't find the correct way to read that for every item in 'DI' cell, i want to put a price on 'FI' cell.
Try using this script, I modified a couple of lines in the sample you shared and added comments next to it to explain.
function Autoprice() {
var sales = SpreadsheetApp.getActive().getSheetByName('Sales')
var salesrow = sales.getRange('D2:D'+sales.getLastRow())
var productnames = salesrow.getValues()
size = productnames.length+1
for (var i = 0; i< size; i++){
if (productnames[i]=='Diary') //If you do productnames[i+1], you're not starting from the beginning of the range, basically you're starting from D3 instead of D2
{
sales.getRange(i+2,6).setValue(31.90) //You can try getRange(row, column) instead
}
}
}
Reference:
getRange(row, column)
You are trying to loop through a 2-dimensionall array (not technically... but each element is a single array).
So to see D2's value you would need productnames[0][0]
However, you can easily fix this using the flat() function. Modify one line of code below:
var productnames = salesrow.getValues().flat();
Also consider learning to use the debugger. If you step through your code, this is easy to see.

How to retrieve a particular row from an array in google apps script?

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}`);
}

How do I insert an array into a Google Doc using data from Google Sheets?

I am trying to pull a range of names from a Google sheet and place it into a Google Doc.In the spreadsheet, the last names("lastNames") come before the first names ("firstNames"), and both are in separate columns. I am trying to place the first and last names together into my doc with the first names first.
I used a for loop to put the first and last names together into an array ("fullNames"), and that part works just fine. When I used Logger.log, all the first names and last names are together in an array, with each full name separated by a common, just the way I wanted them to be.
What I can't figure out how to do is actually insert this new array into the body of the document. I am using the appendTable method, but every time I try to I get the following error: "The parameters (number[]) don't match the method signature for DocumentApp.Body.appendTable."
What changes do I have to make to my code to actually place my new array into my google doc?
function namePusher() {
var ss = SpreadsheetApp.openById("1CHvnejDrrb9W5txeXVMXxBoVjLpvWSi40ehZkGZYjaY");
var lastNames = ss.getSheetByName("Campbell").getRange(2, 2, 18).getValues();
var firstNames = ss.getSheetByName("Campbell").getRange(2, 3, 18).getValues();
//Logger.log(firstNames);
var fullNames = [];
for(var i = 0; i < firstNames.length; i++){
var nameConcat = firstNames[i] + " " + lastNames[i]
fullNames.push(nameConcat);
}
//Logger.log(fullNames);
var doc = DocumentApp.getActiveDocument().getBody();
doc.appendTable(fullNames);
}
Modification points:
I think that there 2 reasons in your issue.
Values retrieved by getValues() is 2 dimensional array.
data of appendTable(data) is required to be 2 dimensional array.
In your script, fullNames is 1 dimensional array. By this, such error occurs.
In your script, the values are retrieved 2 columns using 2 getValues(). In this case, the cost will become a bit high. You can retrieve the values using one getValues().
When these points are reflected to your script, it becomes as follows.
Modified script:
function namePusher() {
var ss = SpreadsheetApp.openById("1CHvnejDrrb9W5txeXVMXxBoVjLpvWSi40ehZkGZYjaY");
var values = ss.getSheetByName("Campbell").getRange("B2:C19").getValues(); // Modified
var fullNames = [];
for(var i = 0; i < values.length; i++){ // Modified
var nameConcat = [values[i][1] + " " + values[i][0]]; // Modified
fullNames.push(nameConcat);
}
var doc = DocumentApp.getActiveDocument().getBody();
doc.appendTable(fullNames);
}
References:
getValues()
appendTable(cells)
One simple way to fix your code is by replacing
fullNames.push(nameConcat);
by
fullNames.push([nameConcat]);
The problem with your script is that fullNames is an Array of strings but your should pass an Array of Arrays of strings (or objects that might be coerced to strings).
Basic demo
var data = [
['A','B','C'],
[1, 'Apple','Red'],
[2, 'Banana','Yellow']
];
function myFunction() {
const doc = DocumentApp.getActiveDocument();
const body = doc.getBody();
body.appendTable(data);
}
As mentioned on Tanaike's answer there are other "improvement opportunities"
Reduce the number of calls to the Google Apps Script Classes and Methods
Use better ways to manage Arrays and to concatenate strings.

How to automatically add questions in a Google Form based on columns on Google Sheets? [duplicate]

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);
}

Google Sheets: how to make returned value of custom function overflow into a row?

I have written a custom function which returns a simple array. (it is a simple dirty 3D lookup over multiple sheets). Here is the code if it helps:
function get3DCellValues(startSheet, endSheet, cell) {
var sheets = SpreadsheetApp.getActiveSpreadsheet().getSheets();
var sum = 0;
var cellValues = [];
for (var i = (startSheet); i < endSheet; i++ ) {
var sheet = sheets[i];
var val = sheet.getRange(cell).getValue();
cellValues.push(val);
}
//Logger.log(cellValues);
return cellValues;
}
The problem is that when I return cellValues, the values overflow down the column. But I want it to overflow rightward through the row instead. Is there a way to do so? Thank you.
Google's guide has this to say about custom functions returning values:
Every custom function must return a value to display, such that:
If a custom function returns a value, the value displays in the cell
the function was called from. If a custom function returns a
two-dimensional array of values, the values overflow into adjacent
cells as long as those cells are empty
But this doesn't seem to be helpful to me.
Each entry in the array represents one row.
e.g. [[1,2],[3,4]] would be two rows [1,2] and [3,4].
[1,2,3,4] is interpreted as [[1],[2],[3],[4]], so it's 4 rows with one value each.
If you want only one row you could write [[1,2,3,4]].
So you'd have to change your code like this
...
var cellValues = [[]];
...
cellValues[0].push(val);
SpiderPig's very clear answer helped me immensely.
If you always want to return just one row, then you can also write your code as
...
var cellValues = [];
...
cellValues.push(val);
...
return [cellValues];
This will return an array that contains your cellValues array as its first and only entry

Categories