How to clear cells if ISBLANK? - google-apps-script

I want to add clearing function. If "B1" is blank, then clear C1,D1,E1,F1;
But I don't know how I can do it. I tried custom function in formatting, but not works.
Can u help me?

The simple answer to your question is as follows:
function myFunction() {
let your_sheet = SpreadsheetApp.openByUrl('https://docs.google.com/spreadsheets/d/your_sheet_id').getSheetByName('Sheet1')
let cell_check = your_sheet.getRange('B1');
if(cell_check.isBlank()) {
your_sheet.getRange('C1:F1').clearContent();
}else{
console.log('not empty')
}
}
But I would probably advise considering the use of numbered ranges instead of named ranges as it will be easer to reason about at scale if you are running through multiple rows.
function myNumberedRangeFunction() {
let your_sheet = SpreadsheetApp.openByUrl('https://docs.google.com/spreadsheets/d/your_sheet_id').getSheetByName('Sheet1');
let cell_check = your_sheet.getRange(1,2);
/* getRange(
1, the first row,
2, is the second column
)
*/
if(cell_check.isBlank()) {
your_sheet.getRange(1,3,1,4).clearContent();
/* getRange(
1, the first row,
3, the third column,
1, get one row,
4, get four columns
)
*/
}else{
console.log('not empty')
}
}
Typically, if I am manipulating a large spreadsheet, I will pull the entire spreadsheet data_table = your_sheet.getRange(1,1,1,1).isBlank() ? [] : Array.from(your_sheet.getRange(1,1,your_sheet.getLastRow(),your_sheet.getLastColumn()).getValues()); and convert it to a table array that I can analyze and manipulate all at once and then just place the changes all at once with
your_sheet.getRange(1,1,data_table.length,data_table[0].length).setValues(data_table)

You can hide, but not clear, the values in columns C:F with this conditional formatting custom formula rule:
=isblank($B1)
The rule should set the text color to match the cell fill color, making the value in the cell "invisible".

Related

How to get a value after a character, from a string based on a text match using formula?

I got the following value:
tradicional;cropped$9$10;mullet$5$7
In cell A1, I can choose between tradicional, cropped and mullet. In cell A2, I pick 1, or 2.
If I pick cropped and 2, the value to be returned would be 10.
If I pick mullet and 1, the value to be returned would be 5.
If
I'd go for len and left, but I don't see how this is going to work using the matching criteria.
Here's a practical example: https://docs.google.com/spreadsheets/d/1dFzXmtKj15EzApTKUKv8yF7_mAIB1COPSgMLMMmFE4E/edit?usp=sharing
Appreciate your help.
Description
You can split the text string on semicolon ";" into 3 parts. The depending on which part you choose, you can split it on dollar sign "$" then you can get the "item" and return an integer. I leave it to you to figure out how to incorporate into your script.
Script (Test Case)
function makeAChoice() {
try {
console.log("You chose "+getChoice("cropped",2));
console.log("You chose "+getChoice("mullet",1));
console.log("You chose "+getChoice("somethingelse",1));
}
catch(err) {
console.log(err);
}
}
function getChoice(choice,item) {
try {
var text = "tradicional;cropped$9$10;mullet$5$7";
text = text.split(";");
text = text.filter( s => s.includes(choice) );
if( text.length < 1 ) throw "Error choice ["+choice+"] not found!";
text = text[0].split("$");
return parseInt(text[item]);
}
catch(err) {
console.log(err);
}
}
Console.log
8:32:38 AM Notice Execution started
8:32:38 AM Info You chose 10
8:32:38 AM Info You chose 5
8:32:38 AM Info Error choice [somethingelse] not found!
8:32:38 AM Info You chose undefined
8:32:38 AM Notice Execution completed
Reference
https://www.w3schools.com/jsref/jsref_split.asp
https://www.w3schools.com/jsref/jsref_filter.asp
https://www.w3schools.com/jsref/jsref_includes.asp
https://www.w3schools.com/jsref/jsref_parseint.asp
Per my comments to your original post, I feel that there is a lot we don't know about your bigger goal. But as you aren't able to provide that, this solution will work for your one exact example.
Place the following formula in C4:
=ArrayFormula(IFERROR(VLOOKUP(A4;SPLIT(FLATTEN(SPLIT(E4;";"));"$");B4+1;FALSE)))
(See the new sheet "Erik Help.")
The inner SPLIT splits the E4 string at every semicolon.
FLATTEN sends that all to one column.
The outer SPLIT then splits at each "$".
VLOOKUP can then try to find the Col-A term in the first column of the resulting virtual chart. If found, it will return the column value that matches the Col-B value + 1 (since column 1 of the virtual array is the labels, e.g., 'tradicional,' etc.).
If no match is found for both the Col-A and Col-B data, then IFERROR returns null.

Is there a way to sort a table based on a cell value in Angular?

My current table looks like this:
Status
Draft
Pending
Complete
I want to sort them based on the value of the cells. Is there a way to do that? I've only been able to sort them using this code:
onChange(status: string){
const sortState: Sort = {active: status, direction: 'desc'};
this.sort.active = sortState.active;
this.sort.direction = sortState.direction;
this.sort.sortChange.emit(sortState);
}
But I want to sort using the values of the status themselves since I'd want to create a button which when click sorts starting from complete or draft or pending.
I'm a little confused by your question, but I think I understand what you're asking.
You're going to want to convert your values into an array and then use the .sort() function. So, assuming you have an array of your cells, we can call that let array = Cell[], you can then access the status of the cells like this:
sortCells(){
let array = Cell[]; // here we're assuming there is already a cell type and a cell.active parameter, like shown in your example.
let possibleValues = ["Draft","Pending","Complete"]; // easier way to compare two values
array.sort((a,b)=>{
let aIndex = possibleValues.indexOf(a.active); // index of gets the location of the element in an array
let bIndex = possibleValues.indexOf(b.active);
if(a > b){
return -1;
} else if(b > a){
return 1;
}else{
return 0; // they are equal
}
})
}
You can read more about sort here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort

Use only the 5 higher values in amCharts pie?

My data call can have lots of small elements (in percentage) that I would like to ignore, I only need the top 5 in my amCharts pie.
Can this be accomplished with amCharts or I should treat the data before?
please see my [jsfiddle][1]
[1]: http://jsfiddle.net/pbarros/xznxbnc7/3/
thanks
You can use the hideLabelsPercent property to set a threshold for the lowest allowed percentage you want a label for. If you want to do this dynamically, you can set this in the init event by finding the 5th largest percents value in the chart's chartData array and use it as your hideLabelsPercent threshold. I've updated your handleInit method to do this:
function handleInit(e) {
if (e.chart.chartData.length > 5) {
//sort the copy of the chartData array from largest to smallest
//if your data is pre-sorted, then you can skip this step
var sortedData = e.chart.chartData.slice().sort(function(lhs, rhs) {
return rhs.percents - lhs.percents;
});
//set the threshold equal to the 5th largest slice's percents value so that the rest are hidden
e.chart.hideLabelsPercent = sortedData[4].percents;
//redraw the chart
e.chart.validateNow();
}
}
Updated fiddle: http://jsfiddle.net/xznxbnc7/9/
Edit since I misread the question
If you only want to show the top five slices from your dataset, you can filter on your backend or use the init method to sort and modify your dataProvider to contain only the top five.
function handleInit(e) {
if (e.chart.chartData.length > 5) {
//sort the copy of the chartData array from largest to smallest
//if your data is pre-sorted, then you can skip this step
var sortedData = e.chart.dataProvider.slice().sort(function(lhs, rhs) {
return rhs[e.chart.valueField] - lhs[e.chart.valueField];
});
//only put in the top 5.
e.chart.dataProvider = sortedData.slice(0, 5);
// redraw the chart
e.chart.validateData();
}
}
Fiddle: http://jsfiddle.net/g3cchyjg/1

How to count for specific string and color?

I've been looking into how to count cells with the countif function, and how to count cells that are colored using scripts and custom functions (like this thing: http://pastebin.com/4Yr095hV), but how would i count cells with a specific string AND color?
Example, I want to count every cell containing the word "one" that has a fill color of white.
EDIT: I was told to add what i had so far, but I am not sure what was meant by that. For counting cells with a specific string I used:
=COUNTIF(A1:A247,"string")
and for counting cells that are colored i used this what was on this page: https://webapps.stackexchange.com/questions/23881/google-spreadsheet-calculating-shaded-cells
but i still don't know how to combine these two TOGETHER.
EDIT: For those looking for this answer, I've found a way to utilize the script Tom posted, and adjusted a line within it.
For Tom's script to work with "wildcards", i used something called .indexOf to always look for any cells containing the string (effectively treating it as if there is always a star before and after the string). On line 32 of his script, I altered it to this:
.map (function(e,i,a) { if (e.toString().toUpperCase().indexOf(this.toString().toUpperCase()) >= 0){ return 1 } else { return 0 } },str))
So now whenever I want to look for a White cell containing the string "Apple1", it will count it regardless of if it's written as "OrangeApple1B" or whatever. And the casing doesn't matter since it seems like this script always converts the given string to Upper Case anyways.
I am still trying to find out how to incorporate this on a totally different spreadsheet though (using something like IMPORTRANGE to count cells on a TOTALLY DIFFERENT SHEET using this script)...
function countIfStringAndColor(r, str, color) {
var COLORS = {
"BLACK":"#000000",
"DARK GRAY 4":"#434343",
"DARK GRAY 3":"#666666",
"DARK GRAY 2":"#999999",
"DARK GRAY 1":"#B7B7B7",
"GRAY":"#CCCCCC"
};
var range = SpreadsheetApp
.getActive()
.getActiveSheet()
.getRange(r.toString());
color = color.indexOf("#") == 0 ? color : COLORS[color.toString().toUpperCase()];
return range
.getBackgrounds()
.reduce(function(a,b) { return a.concat(b) })
.map (function(e,i,a) { return e.toString().toUpperCase() === this.toString().toUpperCase(); },color)
.map(function(e,i,a) { return [e, this[i]] },
range
.getValues()
.reduce(function(a,b) { return a.concat(b) })
.map (function(e,i,a) { return e.toString().toUpperCase() === this.toString().toUpperCase() },str))
.filter(function(e,i,a) {return a[i][0] && a[i][1] })
.length;
}
METHOD OF OPERATION
The function takes three arguments: Range (String), String, String
The associative array 'COLORS' is supplied to convert the common names of colors to hex format. There are about 90 more colors in the list that I didn't supply for space reasons. I can get you the full list if you would like.
Grabbing the Range.
Checks to see if color is already in hex format. If not it tries to find a common name key in COLORS and return the hex value. From here out everything is toString() and toUpperCase() to help prevent errors.
The code from here out is one chain of array manipulation that will produce the solution for the function to return.
Grab the needed background colors.
.reduce, coupled with .concat (both Array Methods), is used to flatten the background color array. It changes it from a rectangular array of arrays to a one dimensional list.
.map goes through each element of the array and applies the given function. In this case we are seeing if the array element (e) is the same as the color supplied. Take note of how 'color' is called outside the closing curly bracket. It is the 'thisArg', and the 'this' inside the function is an image of it. The array is now reduced to a series of true/false elements.
This map is used to combine the two arrays, 'color' and 'str'. The indented part right below is the same steps we used to get 'color' to a series of true/false elements, but now applied to 'str'. All those operations are performed while 'str' is being called as the thisArg for the current map function. The map function then returns a single array of the form [color,str] which is made up of many elements of [true,false] [true,true] [false,false] pairs.
We are only interested in the solutions where both 'color' and 'str' are true, so we can use .filter to remove all the other elements, leaving use with an array of only [true, true] pairs.
Each [true, true] pair is a unique solution to the equation. We can just grab the length of the array to see how many solutions we have found! This is the value that is passed to the return at the beginning.

Qtableview, add widgets between rows

I have a QTableview (multiple columns, sorting) and would like to add a button that shows additional data below the current row. For the rendering of this additional data I would like to use another widget, that fills up a variable height and spans all the rows.
While I know that I can create delegates for cells, I was wondering if this is possible for rows or whether that would mean that I would have to inherit from a tableview and modify its paint method, which seems to be lot of work for a novice like me.
QVariant YourTableModel::data(const QModelIndex & index, int32_t role) const
{
if (!index.isValid()) {
return QVariant();
}
if (role == Qt::DisplayRole || role == Qt::EditRole)
{
switch (index.column())
{
case YOUR_COL:
double theDouble = getDoubleFromModelSomewhere();
return QString::number(theDouble, 'f', 3); // Format shows 3 decimals
}
}
return QVariant();
}
If I have understood your question properly then I think this is the answer.
QTableView *view = new QTableView;
view->setItemDelegateForRow(int row, QAbstractItemDelegate *delegate);