Google Sheets: Automate Hidden Rows For Empty Cells - google-sheets

I'm importing information into a Google Sheet that I'm hoping to print as a delivery list invoice.
The code below allows items marked "0" in Column H to disappear off the invoice, allowing the final product to appear cleaner.
I'm trying to figure out how to make the "Hide Row" function trigger for empty cells, so I don't have to manually enter in 0's.
As far as I can tell, the Filter function won't work because it won't update "OnEdit", only manually. I plan to repeat this for many sheets (one per customer) and require it to be automatic.
This is the code I have so far.
function onEdit() {
var s = SpreadsheetApp.getActive()
.getSheetByName('BELLTOWN');
s.showRows(1, s.getMaxRows());
s.getRange('H:H')
.getValues()
.forEach(function (r, i) {
if (r[0] !== '' && r[0].toString()
.charAt(0) == 0) s.hideRows(i + 1)
});
}
function getNote(cell) {
return SpreadsheetApp.getActiveSheet().getRange(cell).getComment();
}

Change
if (r[0] !== '' && r[0].toString()
to
if (r[0] == '' && r[0].toString()

Related

How to delete multiple cells in a sing row, and continue that for 50 rows

I'm looking to see if there is a way to delete cell A2, and then have google sheets automatically delete cells E2,G2,H2,I2. I would want this for each row for 50 rows. What is happening right now is if I delete A2, google sheets automatically pushes the next oldest data. however, it then pushes down cell E2,G2,H2,I2, because it didnt give me enough time to delete them. Help is appreciated.
https://docs.google.com/spreadsheets/d/10eK6tyHVIX6JyFChXYvtS6JmYyUnE-Ib0pClSnouBoU/edit?usp=sharing
By using a simple comparison inside your trigger you can delete the data
Here's my approach by using the code you provided inside the Sheet:
function onEdit(e){
script1(e);
script2(e);
}
function script1(e){
var row = e.range.getRow();
var col = e.range.getColumn();
if(col === 1 && row > 1 && e.source.getActiveSheet().getName() === "Kanban"){
if (e.range.getValue() === '') {
e.source.getActiveSheet().getRange(`E${row}`).setValue('');
e.source.getActiveSheet().getRange(`H${row}`).setValue('');
} else {
e.source.getActiveSheet().getRange(row,8).setValue(new Date());
}
}
}
function script2(e){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Kanban");
var range = sheet.getRange("A2:I");
range.sort([{column: 8, ascending: true}]);
}
Where if (e.range.getValue() === '') and .setValue(''); will "delete" the data when it comes to first column and it's been deleted, otherwise it'll change the Timestamp cell.
Reference
Event Object

G-Sheets: Populate multiple cells from another sheet based on dropdown (multiple rows and columns)

I am trying to copy or populate (not sure on the correct terminology) data from multiple cells to the same sized multiple cells on another sheet based on the dropdown.
https://docs.google.com/spreadsheets/d/1ELxYcMG_kvzt6KLZoY8WyTBb3evv4dOmpYWvTlzvEVM/edit?usp=sharing
So in this spreadsheet, I'm trying to select A2 & A8 in "Results" and populate B2:I6 & B8:I12 based on the relevant rows/columns in "Data"
Is this possible?
I've done example for you to get you going, you need to implement how to ref triggering cell and get it's ref row index to do copyTo to that row index, not hardcode it like I did ..
This is the relevante code:
function onEdit(e){
// Set a comment on the edited cell to indicate when it was changed.
var range = e.range;
if( range.getA1Notation() == "A2" ){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[1];
var orgsheet = ss.getSheets()[0];
if( range.getValue() == "AAA" ){
sheet.getRange("B2:H6").copyTo(orgsheet.getRange("B2"), {contentsOnly:true});
} else if ( range.getValue() == "BBB" ) {
sheet.getRange("B8:H12").copyTo(orgsheet.getRange("B2"), {contentsOnly:true});
}
}
}
also, I think that you need to copy my sheet to your drive to be able to run the script without permission errors,
this is link to my sheet: https://docs.google.com/spreadsheets/d/1YVuQMBLEqgwGMPdjlV9cKRCML0vqi3UkMsZ66DR5Fls/edit#gid=0
cheers, k

How to total data based on current date across sheets

UPDATE: Resolved by moving to Excel. :(
I have a Google spreadsheet that has a page for every week. I currently am using the following custom function to count the total amount of times the name appears across all sheets.
function COUNTALLSHEETS(range, countItem, excluded) {
try {
var count = 0,
ex = (excluded) ? Trim(excluded.split()) : false;
SpreadsheetApp.getActive()
.getSheets()
.forEach(function (s) {
if (ex && ex.indexOf(s.getName()) === -1 || !ex) {
s.getRange(range)
.getValues()
.reduce(function (a, b) {
return a.concat(b);
})
.forEach(function (v) {
if (v === countItem) count += 1;
});
};
});
return count;
} catch (e) {
throw e.message;
}
}
function Trim(v) {
return v.toString().replace(/^\s\s*/, "")
.replace(/\s\s*$/, "");
}
When I run the function using
=COUNTALLSHEETS("$A$1:$G$40", A2, "Rosters")
it returns the value across all sheets except those excluded by the third argument of the function. In this case, the sheet titled 'Rosters' isn't counted.
My problem is that every week, I have to update the sheets that are excluded. I currently have all sheets dated in the future excluded. So instead of the short call to the function earlier, it looks more like
=COUNTALLSHEETS("$A$1:$G$40", A2, "Rosters, 5-9Dec, 12-16Dec, ... etc.")
Is there any way to modify the code itself or the function call to exclude future sheets automatically? I'm looking to pull the current date, compare it to a sheet title (which I can change to fit a certain format if needed), and exclude any sheets dated in the future.
Please let me know any ideas; if I need to share a copy of the spreadsheet I can.
Do you need to regularly access the excluded sheets? What about hiding them, and only counting unhidden sheets? Might keep things more organized/manageable at the same time as solving your problem. Since I don't thoroughly know your use-case, this may not be the right direction, of course.

COUNTIF Statements: Range Across All Sheets + Cell Reference as Criterion

1) Range Across All Sheets:
I've googled everything but nothing. Basically, I need a formula that looks for the same range across all sheets.
My current formula looks like this:
=COUNTIF(Aug_15!$G:$G, "Shaun")+countif(July_15!$G:$G, "Shaun)+countif(June_15!$G:$G, "Shaun")+countif(May_15!$G:$G, "Shaun")+COUNTIF(Apr_15!$G:$G, "Shaun")+COUNTIF(Mar_15!$G:$G, "Shaun")
The issue I have is, as a month passes, a new sheet for the month is created. So this lowers the automation dramatically as you have to edit the formula every month. I'm basically looking for something that will search G:G across all sheets for that criteria.
So in my imaginary world, it would look something like this:
=COUNTIF(ALLSHEETS!$G:$G, "Shaun")
2) Cell Reference as Criterion
I'm trying to make the criteria look for something from another cell. For example, I'd replace "Shaun" with the cell L3. But it doesn't work! It searches for literally the two characters L and 3!
Is there anyway to make the criteria a value from another cell?
Many Thanks,
Shaun.
As Akshin Jalilov noticed, you will need a script to achieve that. I happen to have written a custom function for that scenario some time ago.
/**
* Counts the cells within the range on multiple sheets.
*
* #param {"A1:B23"} range The range to monitor (A1Notation).
* #param {"valueToCount"} countItem Either a string or a cell reference
* #param {"Sheet1, Sheet2"} excluded [Optional] - String that holds the names of the sheets that are excluded (comma-separated list);
* #return {number} The number of times the item appears in the range(s).
* #customfunction
*/
function COUNTALLSHEETS(range, countItem, excluded) {
try {
var count = 0,
ex = (excluded) ? Trim(excluded.split()) : false;
SpreadsheetApp.getActive()
.getSheets()
.forEach(function (s) {
if (ex && ex.indexOf(s.getName()) === -1 || !ex) {
s.getRange(range)
.getValues()
.reduce(function (a, b) {
return a.concat(b);
})
.forEach(function (v) {
if (v === countItem) count += 1;
});
};
});
return count;
} catch (e) {
throw e.message;
}
}
function Trim(v) {
return v.toString().replace(/^\s\s*/, "")
.replace(/\s\s*$/, "");
}
You can use the custom function in your spreadsheet like this:
=COUNTALLSHEETS("B2:B10", "Shaun")
or when 'Shaun' is in C2
=COUNTALLSHEETS("B2:B3", C2)
There is an optional parameter allowing you to provide a string with comma-separated sheet names you wish to exclude from the count. Don't use this paramater if you want to count ALL sheets.
See if that works for you ?
1) Range Across All Sheets:
The only way you can do that is via script, otherwise Spreadsheet functions cannot dynamically read sheets in the spreadsheet.
2) Cell Reference as Criterion
If the value of L3 is "Shaun" you can do this:
=COUNTIF(Aug_15!$G:$G, L3)
Make sure that you don't put L3 in quotes.

Two related questions on jqGrid column header filters and the advanced filtering dialog

In developing my first ASP.NET MVC 3 app using the jqGrid to display some data, I'm using the column header filters and also allowing for the advanced filter toolbar filtering to be done. Independently these things work pretty well.
First question - Has anyone a solution for communicating the current column header filter settings to the advanced filters?
As an example, a user can filter on the "Ice Cream Name" column, entering a partial name, e.g., "Chocolate", and it'll filter down to "Chocolate Explosion", "Dark Chocolate", etc. - great. What would be nice would be to open the advanced filter and have that "contains 'Chocolate'" column filter automatically populated in the advanced filter. I recognize that the other direction (where someone could AND or OR two values for the same column, e.g. 'Chocolate' OR 'Caramel') becomes problematic but in the other direction, it seems like it might be possible. Perhaps this is just a setting of the grid I'm missing. Anyone solved this?
Second question - I currently can do some filtering with the column header filters, show some result set in the grid and then go into the advanced filter dialog and set up a different filter. That will display the correct results but the column header filters are not cleared, giving the impression that the filtering is not working. How can I reset those column header filters after the use clicks the "Find" button on the dialog?
I find your question very interesting, so I prepared the demo which demonstrate how one can combine Advanced Searching dialog and Toolbar Searching in one grid.
One important, but simple trick is the usage of recreateFilter: true. Per default the searching dialog will be created once and then will be only hide or show. As the result the postData.filters parameter will be not refreshed. After setting recreateFilter: true the problem with filling of the advanced searching dialog with the values from the searching toolbar will be solved. I personally set the default searching options as the following
$.extend(
$.jgrid.search,
{
multipleSearch: true,
multipleGroup: true,
recreateFilter: true,
overlay: 0
}
);
Now to more complex part of the solution is the function refreshSerchingToolbar which I wrote. The function is not so simple, but it's simply in use:
loadComplete: function () {
refreshSerchingToolbar($(this), 'cn');
}
The last parameter is the same parameter which you used as defaultSearch property of the searching toolbar method filterToolbar (the default value is 'bw', but I personally prefer to use 'cn' and set jqGrid parameter ignoreCase: true).
If you fill the advanced searching dialog of the demo with the following field
and click the "Find" button, you will have the following grid:
(I marked the 'Total' column as non-searchable with respect of search: false to show only that all works correctly in the case also)
One can see that all fields of the searching toolbar excepting "Amount" are filled with the values from the searching dialog. The field are not filled because we used "grater or equal" operation instead of "equal". The function refreshSerchingToolbar fills only the elements of the searching toolbar which can be produced by the
Just as a reminder I should mention that in case of the usage of Filter Toolbar it is very important to define searchoptions.sopt options of the colModel. For all non-string column contains (dates, numbers, selects, int, currency) it is extremely important to have 'eq' as the first element of the sopt array. See here and here for details.
If you change the filter of the Advanced Dialog to the following
you will have as expected
At the end I include the code of the refreshSerchingToolbar function:
var getColumnIndex = function (grid, columnIndex) {
var cm = grid.jqGrid('getGridParam', 'colModel'), i = 0, l = cm.length;
for (; i < l; i += 1) {
if ((cm[i].index || cm[i].name) === columnIndex) {
return i; // return the colModel index
}
}
return -1;
},
refreshSerchingToolbar = function ($grid, myDefaultSearch) {
var postData = $grid.jqGrid('getGridParam', 'postData'), filters, i, l,
rules, rule, iCol, cm = $grid.jqGrid('getGridParam', 'colModel'),
cmi, control, tagName;
for (i = 0, l = cm.length; i < l; i += 1) {
control = $("#gs_" + $.jgrid.jqID(cm[i].name));
if (control.length > 0) {
tagName = control[0].tagName.toUpperCase();
if (tagName === "SELECT") { // && cmi.stype === "select"
control.find("option[value='']")
.attr('selected', 'selected');
} else if (tagName === "INPUT") {
control.val('');
}
}
}
if (typeof (postData.filters) === "string" &&
typeof ($grid[0].ftoolbar) === "boolean" && $grid[0].ftoolbar) {
filters = $.parseJSON(postData.filters);
if (filters && filters.groupOp === "AND" && typeof (filters.groups) === "undefined") {
// only in case of advance searching without grouping we import filters in the
// searching toolbar
rules = filters.rules;
for (i = 0, l = rules.length; i < l; i += 1) {
rule = rules[i];
iCol = getColumnIndex($grid, rule.field);
cmi = cm[iCol];
control = $("#gs_" + $.jgrid.jqID(cmi.name));
if (iCol >= 0 && control.length > 0) {
tagName = control[0].tagName.toUpperCase();
if (((typeof (cmi.searchoptions) === "undefined" ||
typeof (cmi.searchoptions.sopt) === "undefined")
&& rule.op === myDefaultSearch) ||
(typeof (cmi.searchoptions) === "object" &&
$.isArray(cmi.searchoptions.sopt) &&
cmi.searchoptions.sopt[0] === rule.op)) {
if (tagName === "SELECT") { // && cmi.stype === "select"
control.find("option[value='" + $.jgrid.jqID(rule.data) + "']")
.attr('selected', 'selected');
} else if (tagName === "INPUT") {
control.val(rule.data);
}
}
}
}
}
}
};
UPDATED: The above code is no more needed in case of usage free jqGrid 4.13.1 or higher. It contains the new default option loadFilterDefaults: true of the filterToolbar, which refreshes the values of the filter toolbar and the filter operations (if searchOperators: true option of filterToolbar is ised) if postData.filters and search: true are set (the filter is applied). Free jqGrid refreshes the filter toolbar on jqGridAfterLoadComplete (if loadFilterDefaults: true are set) or if the event jqGridRefreshFilterValues are explicitly triggered.
I know it's an old post - but if you have multiple grids on the same page the above code can add the filter text to the wrong grid.
Changing this in the first loop in refreshSearchingToolbar, from
control = $("#gs_" + $.jgrid.jqID(cm[i].name));
to
control = $("#gview_"+$grid.attr('id')+" #gs_" + $.jgrid.jqID(cm[i].name));
and this in the second loop from
control = $("#gs_" + $.jgrid.jqID(cmi.name));
to
control = $("#gview_"+$grid.attr('id')+" #gs_" + $.jgrid.jqID(cmi.name));
should do the trick.
Kudos to Oleg

Resources