I have a google sheet where checkboxes control if a column must be included or not.
Each column represents one person, the checkbox on top of the column determines if the person must be taken into account into the computation.
The calculation consists in determining how much each person must pay (Monthly installments), according to:
a starting contribution (Input)
a percentage of the remaining amount to be repaid (Proportion of 300 000,00€-110 000,00€)
My data is structured like this:
My problem is that, when I uncheck one of the columns, the percentage associated with the person stays the same and this introduces an error in the calculation: the sum of the Input and the Monthly installments is no longer equal to the total sum 300 000,00€.
What I'd like to achieve is:
when unchecked, to automatically set to 0% the value of the Proportion cell
when checked, to allow user to enter any percentage in the Proportion cell
Do you have any idea on how to allow simultaneously user input and value based on a condition?
Thanks a lot!
EDIT
You can find here a working example
Adding this script to your Sheets can solve your task
function onEdit(e) {
let row = e.range.getRow(),
col = e.range.getColumn(),
firstCol = 2,
lastCol = 6;
if (sheet.getName() == 'MONEY' && col>=firstCol && col<=lastCol){
switch (row){
case 1:
if (!e.range.getValue()) e.range.offset(3,0).clearContent();
break;
case 4:
if (!e.range.offset(-3,0).getValue()) e.range.clearContent();
break;
}
}
}
Related
I am sorry if I made the wrong question, I'm getting started with Google Sheets and wanted to figure out how to work on some kind of score calculator based on IF conditionals. Here is the example:
It's going to be used on a Form Responses sheet (I don't want to use the Google Form's Quiz option) where I'll need to answer some Y/N questions, in the "Total" column, there is going to be a final score out of 100 based on the "Yes" or "No" answer.
Let's say that the Row # 2 is a submission from a Google Form, and let's say that every submission has by default a score of 100. What the "No" is doing is deducting 10 points from that initial score of 100.
I started with something like =MINUS("100", IF(A2= "No", 10)) but it only works with one cell.
Thank you so much!
Try this formula in E2:
=MINUS(100, COUNTIF(A2:D2, "No") * 10)
Output:
Update:
If you want to use the column header as reference for deduction points, You can use this Custom Function:
To write a custom function:
Create or open a spreadsheet in Google Sheets.
Select the menu item Tools > Script editor.
Delete any code in the script editor.
For this case, simply copy and paste the code below into your script editor
Click Save save.
Code:
function SCORE(range) {
var map = range.shift();
var data = range;
var result = [];
for(var i = 0; i < data.length; i++){
var tally = 0;
for(var j = 0; j < data[0].length; j++){
if(data[i][j] == "No"){
tally = tally + map[j];
}
}
result.push([100+tally])
}
return result;
}
To call the custom function
Click the cell where you want to use the function.
Type an equals sign (=) followed by the function name and any input value — for example, =SCORE(A1:D4) — and press Enter.
The cell will momentarily display Loading..., then return the result.
Example Usage:
Note: You must always include the column header(deduction points) in your range.
Reference:
COUNTIF
Custom Function
Tableau is giving me a hard time, trying to compare two items by percentages. I need to display the percentage different between the number (couintif) of string items based on condition.
Basically, I wrote two calculated fields like:
Calc field #1
IF [Outcome] = "Complete" Then 1 Else 0
Calc field #2
IF [Outcome] = "Pending" Then 1 Else 0
and a third field to get the percentage of pending sales to completed sales
Calc percentage
SUM(Calc field #1 / Calc field #2)
But it's not working. The first two fields work fine, validated them with dataset, but the third calculation doesn't work and always outputs 0
The formula for Calc percentage should be
SUM(Calc field #1) / SUM(Calc field #2)
As both the calculated fields are computed row-wise, it is important to aggregate while using it in a formula.
My intention is to convert a single line of data into rows consist of a specific number of columns in Google Sheets.
For example, starting with the raw data:
A
B
C
D
E
F
1
id1
attr1-1
attr2-1
id2
attr2-1
attr2-2
And the expected result is:
(by dividing columns by three)
A
B
C
1
id1
attr1-1
attr1-2
2
id2
attr2-1
attr2-2
I already know that it's possible a bit manually, like:
=ARRAYFORMULA({A1:C1;D1:F1})
But I have to start over with it every time the target range is moved OR the subset size needs to be changed (in the case above it was three)!
So I guess there will be a much more graceful way (i.e. formula does not require manual update) to do the same thing and suspect ARRAYFORMULA() is the key.
Any help will be appreciated!
I added a new sheet ("Erik Help") where I reduced your manually entered parameters from two to one (leaving only # of columns to be entered in A2).
The formula that reshapes the grid:
=ArrayFormula(IFERROR(VLOOKUP(SEQUENCE(ROUNDUP(COUNTA(7:7)/A2),A2),{SEQUENCE(COUNTA(7:7),1),FLATTEN(FILTER(7:7,7:7<>""))},2,FALSE)))
SEQUENCE is used to shape the grid according to whatever is entered in A2. Rows would be the count of items in Row 7 divided by the number in A2 (rounded to the nearest whole number); and the columns would just be whatever number is entered in A2.
Example: If there are 11 items in Row 7 and you want 4 columns, ROUNDUP(11/4)=3 rows to the SEQUENCE and your requested 4 columns.
Then, each of those numbers in the grid is VLOOKUP'ed in a virtual array consisting of a vertical SEQUENCE of ordered numbers matching the number of data pieces in Row 7 (in Column 1) and a FLATTENed (vertical) version of the Row-7 data pieces themselves (in Column 2). Matches are filled into the original SEQUENCE grid, while non-matches are left blank by IFERROR
Though it's a bit messy, managed to get it done thanks to SEQUENCE() function anyway.
It constructs a grid by accepting number of rows/columns input, and that was exactly I was looking for.
For reference set up a sheet with the sample data here:
https://docs.google.com/spreadsheets/d/1p972tYlsPvC6nM39qLNjYRZZWGZYsUnGaA7kXyfJ8F4/edit#gid=0
Use a custom formula
Although you already solved this. If you are doing this kind of thing a lot, it could be beneficial to look into Apps Script and custom formulas.
In this case you could use something like:
function transposeSingleRow(range, size) {
// initialize new range
let newRange = []
// initialize counter to keep track
let count = 0;
// start while loop to go through row (range[0])
while (count < range[0].length){
// add a slice of the original range to the new range
newRange.push(
range[0].slice(count, count + size)
);
// increment counter
count += size;
}
return newRange;
}
Which works like this:
The nice thing about the formula here is that you select the range, and then you put in a number to represent its throw, or how many elements make up a complete row. So if instead of 3 attributes you had 4, instead of calling:
=transposeSingleRow(A7:L7, 3)
you could do:
=transposeSingleRow(A7:L7, 4)
Additionally, if you want this conversion to be permanent and not dependent on formula recalculation. Making it in run fully in Apps Script without using formulas would be neccesary.
Reference
Apps Script
Custom Functions
I've found a lot of good information about the angular-ui-grid cell filters but I can't seem to locate an answer to my specific issue. Basically I have a grid set up with two columns:
Column A is a editable cell dropdown where you can pick two options, 'money' or 'percentage'. Column B is a number input displaying a value.
I have two filters in my app, 'currency' and 'percentage'. If I set up the columnDefs for either of those filters, then Column B will display all the values in that column as either a currency value or percentage.
What I need it to do though is to make it so that the filter is applied to Column B based on the setting of Column A, so I end up with this:
Value Type Value
---------------------------------------
Money $100.00
Percentage 100%
I've got some code to basically check that after the edit:
$scope.gridApi.edit.on.afterCellEdit($scope, function(rowData) {
var row = _.indexOf($scope.items, rowData);
if (rowData['valueType'] == 'percentage') {
// Apply percentage filter to the value cell for this row
} else {
// Apply currency filter to the value cell for this row
}
$scope.gridApi.core.refresh();
});
...I'm just not sure how to apply the filter to the individual cells. Any guidance would be appreciated!
If anyone else ever has the same question, I managed this by creating a filter that took the row entity as an argument, then applied the custom filters inside of that new filter based on the entity values.
I have two sheets used to track a construction project.
On the first sheet, a list of tasks is incorporated into a timeline with cost projections, etc. The tasks are something like the following:
Cut Concrete
Pour new pad
Frame
Roof
The second sheet is for tracking individual purchases, each of which is associated with a task from the first sheet (e.g., Cut Concrete). It looks something like the following:
DATE PAYEE ITEM CATEGORY COST
----- ---------- ---------- -------- ------
10/25 Home Depot (10) 2x4's Frame ▽ $54.00
Using Data Validation, the Category dropdown in the second sheet references the list of items from the first sheet. This is working perfectly. Here's the problem...
If I change the item on the first sheet (for instance, "Frame" to "Framing"), although the dropdown is updated, any previously entered rows (such as the one shown above), just show a validation error (i.e., a red indicator in the right corner of the cell).
Since a construction project can easily have hundreds of items purchased, rather manually looking for data validation errors, is there a way to have the second sheet's values updated? For instance...
Add a script that watches for content changes in the first sheet. When the user starts editing a "task" cell, its original value is noted; and upon exiting the cell, if the value has changed, the script looks through the second spreadsheet for the original value and replaces it with the new one. (Seems like a lot of hassle.)
Find some way for the dropdown to insert a cell reference to the first sheet instead of the actual value. That way, the dropdown cell is always referencing the source item (i.e., the "task" cell's current content).
A more obvious feature I don't know about.
As per my favored possibility noted in my question, I figured out how to write a script that would run after a value was selected from the dropdown, thus overwriting the literal value with a cell reference.
The following script runs after the user makes a selection in the dropdown menu:
function onEdit(event){
var activeSheet = event.source.getActiveSheet();
var activeSheetName = activeSheet.getName();
var activeCell = activeSheet.getActiveCell();
var activeColumn = activeCell.getColumn();
if (activeSheetName == "Envelope (Spent)" && activeColumn === 4) {
var destinationCell = activeCell;
var destinationContent = destinationCell.getValue();
var sourceSheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];
var sourceSheetName = sourceSheet.getName();
var sourceRange = sourceSheet.getRange("D:D");
var sourceValues = sourceRange.getValues();
for (var i = 0; i < sourceValues.length; i++) {
if (sourceValues[i] == destinationContent) {
var sourceRow = sourceSheet.getRange("D" + i + ":D" + i).getRow() + 1;
destinationCell
.setValue("='" + sourceSheetName + "'!D" + sourceRow)
.setNote(destinationContent)
;
}
}
}
}
To allow for an easy recovery in case the two sheets somehow get out of sync, the originally selected value, derived from the dropdown's data validation, is inserted as a note. I figured it was easier to clear all the notes in the future than to find myself with a bunch of entries that don't correspond with the source list of tasks.
The data validation in Google sheets is always inserting the Literals you type or pick so you can't link it to the original cell. You have to solve the whole thing through scripts
Get a list of all data validation items through onOpen()
Create an onEdit() function that runs if the data validation range is edited, checks which field is changed and then goes through the purchases, checks which purchases have the old value and replaces them with the new one.