I have this formula which adds a border to a row if a certain cell in that row contains any text:
=$C5<>""
I want to change the formula to not just look at column C, but at the entire row in the table (which is A:I), i.e. if any text exists at all in that row, it must apply the formatting.
Can't seem to get it right.
Just use this formula
=COUNTA($A5:$I5)>0
COUNTA counts non-blank cells so if there is any text in that range COUNTA will be > 0 and trigger the conditional formatting
Try this. I've tested it and It works for me. You need to change the "Sheet1" name with your sheet's name. Currently the macro is set to row 100. Check the values for i = 1 to 100
Sub FormatCells()
Dim Wks As Worksheet: Set Wks = Sheets("Sheet1")
Dim i As Integer
For i = 1 To 100 ' set the max no of Rows
LookupRange = Wks.Rows(i)
If Application.WorksheetFunction.CountA(Wks.Rows(i)) <> 0 Then
With Wks.Rows(i).Borders(xlDiagonalDown).LineStyle = xlNone
Wks.Rows(i).Borders(xlDiagonalUp).LineStyle = xlNone
End With
With Wks.Rows(i).Borders(xlEdgeLeft)
.LineStyle = xlContinuous
.Weight = xlMedium
End With
With Wks.Rows(i).Borders(xlEdgeTop)
.LineStyle = xlContinuous
.Weight = xlMedium
End With
With Wks.Rows(i).Borders(xlEdgeBottom)
.LineStyle = xlContinuous
.Weight = xlMedium
End With
With Wks.Rows(i).Borders(xlEdgeRight)
.LineStyle = xlContinuous
.Weight = xlMedium
End With
End If
Next i
End Sub
Hope this helps. Cheers!
Related
The top right cell (Natwest) is a list from a range using data validation.
The Opening Balance 1,000.00 is sourced from another sheet using a lookup formula.
Using simple if statements, the cumulative balance is then produced - according to the Amount column and whether the Natwest account occurs in the Dr(+) or Cr (-) column
i.e. =if(B4=$D$1,D3+A4,if(C4=$D$1,D3-A4,D3)) and copied down.
Natwest
Amount Dr Cr Balance
1,000.00
100.00 Natwest Account 1 1,100.00
200.00 Account 2 Natwest 900.00
400.00 Natwest Account 1 1,300.00
It works fine, except that when a new row is inserted, the if statement formula is not copied into the new row.
I am looking for an arrayformula solution (or other formula inside the cell solution), so that the Cumulative Balance still works, but doesn't need to be copied into column D new row - when a new row(s) are inserted.
(I don't mind the Natwest (drop down from the list) or the Opening Balance 1,000.00 to be moved elsewhere if required for a solution.)
Thanks for your help.
Something adding up in between the same range of the arrayformula is always going to be tricky with circular dependency. I suggest to get the initial value and add it the SUMIF of second column and substract the SUMIF of second column up to each value. With BYROW you can do it like this:
=BYROW(A4:A,LAMBDA(each,SUMIF(INDIRECT("B4:B"&ROW(each)),D1,A4:each)-SUMIF(INDIRECT("C4:C"&ROW(each)),D1,A4:each)+D3))
Alternate solution:
You can use this custom function from AppScript for automatically calculating cumulative balance
Code:
function customFunction(startnum, key, range) {
var res = [];
var current = startnum;
range.forEach((x) => {
res.push(x.map((y, index) => {
return y == key && index == 1 ? current = (current + x[0]) : (y == key && index == 2 ? current = (current - x[0]) : null)
}).filter(c => c))
})
return res;
}
Custom Function Parameters:
=customFunction(startnum, key, range)
startnum = opening balance
key = Account name
range = cell range
Sample output:
=customFunction(D3,D1,A4:C)
Struggling with a specific Lua table problem, extracting values from a smaller table to set on/off switches for a longer table.
I have an iterator:
for i = 0,11 do
some things
end
Inside the iterator with length 12 I wish to extract values from a table that might look like this (always <= iterator length):
t = {0,2,4,6,8,10}
and generate a new table with the iterator so that if index i = t value insert 1 else insert 0. That is, the new table would look like this:
newTable = {1,0,1,0,1,0,1,0,1,0,1,0}
Help?
t = {0,2,4,6,8,10}
newTable = {}
for i = 0, 11 do
newTable[i] = 0
end -- Set the entire new table to 0's
for _, v in ipairs(t) do
newTable[v] = 1
end -- Loop through t, replace the 0's in new table with a 1 where newTable[v]
Say we have the following spreadsheet in google sheets:
a a
b b
c
d e
e d
How would I build a formula that counts the number of rows in column B that do not match the corresponding row in column A, and are not blank? In other words I want to get the number of rows that changed to a new letter in column B. So in this example the formula should return 2.
Thank you for your help.
UPDATE:
Now suppose I have this spreadsheet:
a a
b b b
c a
d e e
e d e
How would I build on the last formula for the third column, where the value returned is:
(the number of rows in column 3 that don't match the corresponding row in column 2) + (if column 2 is blank, the number of rows in column 3 that do not match the corresponding row in column 1)
and I also don't want to count blanks in the third column.
The value returned in this case should be 2 (rows 3 and 5).
To me it sounds like you could use:
=SUMPRODUCT((B:B<>"")*(B:B<>A:A))
=IFNA(ROWS(FILTER(A:B,
(A:A<>B:B)*
(B:B<>"")
)),0)
FILTER by matching conditions * for AND + for OR.
ROWS counts rows
IFNA returns 0 if nothing was found.
or with QUERY
=INDEX(QUERY(A:B,"select count(B) where B<>A"),2)
Try this:
=ARRAYFORMULA(COUNTA($B$1:$B)-SUM(COUNTIFS($A$1:$A, $B$1:$B,$B$1:$B,"<>")))
I see 2 ways to complete this.
First you could add a function to each row to return 1 or 0 if the value changed and was not blank and then sum results. This unfortunately adds a messy column in your spreadsheet.
=if(A1<>IF(ISBLANK(B1),A1,B1),1,0)
Second you could create a function where you would pass the range as a string.
The call from the worksheet would look like this:
=myFunction("A1:B5")
Then create a script by opening Tools -> Script editor and use something like this
function myFunction(r) {
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getRange(r);
var numRows = range.getNumRows();
var areDifferent = 0;
for (let i=1; i<= numRows; i++) {
let currentValue = range.getCell(i,1).getValue();
let cmpValue = range.getCell(i,2).getValue();
if ((currentValue != cmpValue) && (cmpValue != "")) {
areDifferent++;
}
}
return areDifferent;
}
I'm trying to make a formula that can recognize in Column A the name Brooke B for instance here, from there I'd like to SUM the values listed in Column I Cash Discounts for that specific user.
(Yes this user has no Cash Discounts, thus column I states "Non-Cash Payment").
There's about 80 users total here, so I'd prefer to automate the name recognition in Column A.
Sheet: https://docs.google.com/spreadsheets/d/1xzzHT7VjG24UJ4ZXaiZWsfzroTpn7jCJLexuTOf6SQs/edit?usp=sharing
Desired Results listed in Cash Discounts sheet, listed per user in column C.
You are trying to calculate the total amount of the Cash Discount per person given to people in a list. You have data that has been exported from a POS system to which that you have added a formula to calculate the amout of the discount on a line by line basis. You have speculated whether the discount totals could be calculated using SUMIFS formulae.
In my view, the layout of the spreadsheet and the format of the POS report do not lend themselves to isolating discrete data elements though Google sheets functions (though, no doubt, someone with greater skills than I will disprove this theory). Column A, containing names, also includes sub-groupings (and their sub-totals) as well as transaction dates. There are 83 unique persons and over 31,900 transaction lines.
This answer is a script-based solution which updates a sheet with the names and values of the discount totals. The elapsed execution time is #11 seconds.
function so5882893202() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
// get the Discounts sheet
var discsheetname = "Discounts";
var disc = ss.getSheetByName(discsheetname);
//get the Discounts data
var discStartrow = 3;
var discLR = disc.getLastRow();
var discRange = disc.getRange(discStartrow, 1, discLR-discStartrow+1, 9);
var discValues = discRange.getValues();
// isolate Column A
var discnameCol = discValues.map(function(e){return e[0];});//[[e],[e],[e]]=>[e,e,e]
//Logger.log(discnameCol); // DEBUG
// isolate Column I
var discDiscounts = discValues.map(function(e){return e[8];});//[[e],[e],[e]]=>[e,e,e]
//Logger.log(discDiscounts); // DEBUG
// create an array to build a names list
var names =[]
// get the number of rows on the Discounts sheet
var discNumrows = discLR-discStartrow+1;
// Logger.log("DEBUG: number of rows = "+discNumrows);
// identify search terms
var searchPercent = "%";
var searchTotal = "Total";
// loop through Column A
for (var i=0; i<discNumrows; i++){
//Logger.log("DEBUG: i="+i+", content = "+discnameCol[i]);
// test if value is a date
if (Object.prototype.toString.call(discnameCol[i]) != "[object Date]") {
//Logger.log("it isn't a date")
// test whether the value contains a % sign
if ( discnameCol[i].indexOf(searchPercent) === -1){
//Logger.log("it doesn't have a % character in the content");
// test whether the value contains the word Total
if ( discnameCol[i].indexOf(searchTotal) === -1){
//Logger.log("it doesn't have the word total in the content");
// test whether the value is a blank
if (discnameCol[i] != ""){
//Logger.log("it isn't empty");
// this is a name; add it to the list
names.push(discnameCol[i])
}// end test for empty
}// end test for Total
} // end for percentage
} // end test for date
}// end for
//Logger.log(names);
// get the number of names
var numnames = names.length;
//Logger.log("DEBUG: number of names = "+numnames)
// create an array for the discount details
var discounts=[];
// loop through the names
for (var i=0;i<numnames;i++){
// Logger.log("DEBUG: name = "+names[i]);
// get the first row and last rows for this name
var startrow = discnameCol.indexOf(names[i]);
var endrow = discnameCol.lastIndexOf(names[i]+" Total:");
var x = 0;
var value = 0;
// Logger.log("name = "+names[i]+", start row ="+ startrow+", end row = "+endrow);
// loop through the Cash Discounts Column (Column I) for this name
// from the start row to the end row
for (var r = startrow; r<endrow;r++){
// get the vaue of the cell
value = discDiscounts[r];
// test that it is a value
if (!isNaN(value)){
// increment x by the value
x = +x+value;
// Logger.log("DEBUG: r = "+r+", value = "+value+", x = "+x);
}
}
// push the name and the total discount onto the array
discounts.push([names[i],x]);
}
//Logger.log(discounts)
// get the reporting sheet
var reportsheet = "Sheet10";
var report = ss.getSheetByName(reportsheet);
// define the range (allow row 1 for headers)
var reportRange = report.getRange(2,1,numnames,2);
// clear any existing content
reportRange.clearContent();
//update the values
reportRange.setValues(discounts);
}
Report Sheet - extract
Not everyone wants a script solution to their problem. This answer seeks to supply a repeatable solution using common garden-variety formula/functions.
As noted elsewhere, the layout of the spreadsheet does not lend itself to a quick/simple solution, but it IS possible to break down the data to compile a non-script answer. Though it may "seem" as though the following formula are less than "simple, when taken one-at-a-time they are logical, very easy to create, and very easy to verify successful outcomes.
Note: It is important to know at the outset that the first row of data = row#3, and the last row of data = row#31916.
Step#1 - get Text values from ColumnA
Enter this formula in Cell J3, and copy to row 31916
=if(isdate(A3),"",A3):
evaluates Column A, if the content is a date, returns blank, otherwise, returns the context
Taking Customer "AJ" as an example, the content at this point includes:
AJ
10% BuildingDiscount
10% BuildingDiscount Total:
Northwestern 10%
Northwestern 10% Total:
AJ Total:
Step#2 - ignore the values that contain "10%" (this removes both headings and sub-subtotals
Enter this formula in Cell K3 and copy to row 31916
=iferror(if(search("10%",J3)>0,"",J3),J3): searches for "10%" in Column J. Returns all values except those that containing "10%".
Taking Customer "AJ" as an example, the content at this point includes:
AJ
AJ Total:
**Step#3 - ignore the values that contain the word "Total"
Enter this formula in Cell L3 and copy to row 31916.
=iferror(if(search("total",K3)>0,"",K3),K3)
Taking Customer "AJ" as an example, the content at this point includes:
AJ
Results after Step#3
You might wonder, "couldn't this be done in a single formula?" and/or "an array formula would be more efficent". Both those thoughts are true, but we're looking at simple and easy, and a single formula is NOT simple (as shown below); and given that, an array formula is out-of-the-question unless/until an expert can wave a magic wand over the data.
FWIW - Combining Steps#1, 2 & 3
each of the Steps#1, 2 and 3 build on each other. So it is possible to create a single formula that combines these steps.
enter this formula in Cell J3, and copy dow to row #31916.
=iferror(if(search("total",iferror(if(search("10%",if(isdate(A3),"",A3))>0,"",if(isdate(A3),"",A3)),if(isdate(A3),"",A3)))>0,"",iferror(if(search("10%",if(isdate(A3),"",A3))>0,"",if(isdate(A3),"",A3)),if(isdate(A3),"",A3))),iferror(if(search("10%",if(isdate(A3),"",A3))>0,"",if(isdate(A3),"",A3)),if(isdate(A3),"",A3)))
As the image showed, step#3 concludes with mainly empty cells in Column L; the only populated cell is the first instance of the customer name at the start of their transactions - such as "Alec" in this example. However (props to #Rubén) it is possible to populate the blank transaction Cells in Column L. An arrayformula to find the previous non-empty cell in another column on Webapps explains how.
Step#4 - Create a customer name for each transaction row.
Enter this formula in Cell M3, it will automatically populate the cells to row#31916
=ArrayFormula(vlookup(ROW(3:31916),{IF(LEN(L3:L31916)>0,ROW(3:31916),""),L3:L31916},2))
Step#5 - Get the discount amount for each transaction value
The discount values are already displayed in Column I. They are interspersed with text values, so the formula for tests if this is a total line by testing the value in Column D; only if there is a vale (Product item) does the formula then test of there is a value in column I.
Enter this formula in Cell N3, it will automatically populate the cells to row#31916
=ArrayFormula(if(len(D3:D31914)>0,if(ISNUMBER(I3:I31916),I3:I31916,0),""))
Screenshot after step#5
Reporting by Query
Reporting is done via queries. These can go anywhere, but it is probably more convenient to put it on a separate sheet.
Step#6.1 - query the results to create report showing total by ALL customers
=query(Discounts_analysis!$M$2:$N$31916,"select M, sum(N) where N is not null group by M label M 'Customer', sum(N) 'Total Discount' ",1)
Step#6.2 - query the results to create report showing total by customer where the customer received a discount
=query(Discounts_analysis!$M$2:$N$31916,"select M, sum(N) where N >0 group by M label M 'Customer', sum(N) 'Total Discount' ",1)
Step#6.3 - query the results to create report showing customers with no discount
- `=query(query(Discounts_analysis!$M$2:$N$31916,"select M, sum(N) where N is not null group by M label M 'Customer', sum(N) 'Total Discount' ",1),"select Col1 where Col2=0")`
Queries screenshot
I need your help in excluding the suppressed amounts from the SUM in the group footer. In the details section, I am having duplicate deal no and amounts.So, in the Section Expert for the details sections, I performed a condition on the formula "Suppress" to suppress the duplicated fields which is:
{deal_no} = (next{deal_no})
The above condition is applied in the report and I was able to suppress the duplicates. Now in the group footer, I am performing a running total using the formulas to calculate the SUM for the amounts. However, this SUM is calculating the suppressed amounts and it is giving the results. So how to remove them from the calculation.
This is the answer
WhilePrintingRecords;
If {deal_no} <> (next{deal_no}) OR OnLastRecord then //this excludes the duplicates
(
numbervar gsum := gsum + {#Cost};
numbervar grand := grand + gsum;
);
what you can do is instead of using running total you can just summarize by right clicking on the field and using Insert Summary which will give correct result.
I have made function funsumColumn(dtfunSum, "intProductId", "decCBMCMS")
and pass datattable, table id name, and column name that i want to sum to.
This function returns sum of unique id row.
Private Function funsumColumn(ByVal dt As DataTable, ByVal idColumnName As String, ByVal ColumnForSum As String) As Decimal
Dim dtnew As New DataTable
dtnew.Columns.Add(idColumnName, GetType(Integer))
dtnew.Columns.Add(ColumnForSum, GetType(Double))
Dim dtMainTable As DataTable
Dim ComputeColumnSum As Decimal
Try
Dim ExistIdInLocalTable As Integer
Dim foundRow() As DataRow
Dim PIid As Integer
For i As Integer = 0 To dt.Rows.Count - 1
PIid = dt.Rows(i).Item(idColumnName)
foundRow = dtnew.Select("" & idColumnName & "='" & PIid & "'")
ExistIdInLocalTable = foundRow.Count
If foundRow.Count = 0 Then
dtnew.Rows.Add(dt.Rows(i).Item(idColumnName), dt.Rows(i).Item(ColumnForSum))
Else
End If
Next
dtMainTable = dtnew
ComputeColumnSum = dtMainTable.Compute("sum(" & ColumnForSum & ")", "")
Return ComputeColumnSum
Catch ex As Exception
End Try
Return ComputeColumnSum