I have simple code that sums a range of number based on its font color. But it also sums the subtotals in that range. How can I skip a cell if its a subtotal or in some other way exclude to sum the subtotals?
Here is my code:
Public Function SumByColor(pRange1 As Range, pRange2 As Range) As Double
'Update by Extendoffice
Application.Volatile
Dim rng As Range
Dim xTotal As Double
xTotal = 0
For Each rng In pRange1
If rng.Font.Color = pRange2.Font.Color Then
xTotal = xTotal + rng.Value
End If
Next
SumByColor = xTotal
End Function
assuming subtotals are either use subtotal formula or sum (and nothing you want to include does) this works:
Public Function SumByColor(pRange1 As Range, pRange2 As Range) As Double
'Update by Extendoffice
Application.Volatile
Dim rng As Range
Dim xTotal As Double
xTotal = 0
For Each rng In pRange1
If InStr(1, LCase(rng.Formula), "sum") = 0 And InStr(1, LCase(rng.Formula), "subtotal") = 0 Then
If rng.Font.Color = pRange2.Font.Color Then
xTotal = xTotal + rng.Value
End If
End If
Next
SumByColor = xTotal
End Function
if there are sums you want included and the subtotals do use the subtotal formula then remove the first instr condition that I added
credit to #Foxfire And Burns And Burns who got there first though
Related
I am trying to create a VBA function that loops through each cell in a range, checking if it is equal or not to the cell to the left of it, and if it is a certain color. If it's not equal to the left cell and is that certain color, it adds a number in the same row but a different column to a running sum.
For whatever reason, the condition of the left cell being equal to the current cell is not working: it will still include cells that are the same value as the cell to the left. How do I fix this?
Sub TestFormulas()
Dim x As Long
x = SumRenewed(Range("E2:E9000"))
MsgBox (x)
End Sub
' This function checks cell color and adds it to a sum if it is a certain color.
' It also checks to see if the cell is the same as what's to the left of it. If it is the same, it gets omitted.
' This prevents unnecessary older irrelevant month from being included.
Function SumRenewed(rRng As Range)
Dim lngSum As Long
Dim intIndex As Integer
Dim lngSomething As Variant
For Each cl In rRng
intIndex = cl.Interior.ColorIndex
If cl <> Left(cl, 1) And cl.Interior.ColorIndex = 43 Then '43 is the color index for light green
lngSomething = CLng(Cells(cl.Row, 2))
MsgBox (lngSomething)
lngSum = WorksheetFunction.Sum(lngSomething, lngSum)
lngSomething = CVar(lngSomething)
End If
Next cl
SumRenewed = lngSum
End Function
I have tried numerous workarounds for offsets, assigning Left(cl, 1) to a variable and changing the data type, and Googled every which way I can think for 2.5 days.
Sum Up Column If Matching Criteria (Incl. ColorIndex)
In VBA
Sub TestFormulas()
Dim ws As Worksheet: Set ws = ActiveSheet ' improve!
Dim rg As Range
Set rg = ws.Range("E2", ws.Cells(ws.Rows.Count, "E").End(xlUp))
Dim MySum As Double
MySum = SumRenewed(rg, "D", "B", 43)
MsgBox MySum
End Sub
The Function
Function SumRenewed( _
ByVal SingleColumnRange As Range, _
ByVal CompareColumnID As Variant, _
ByVal SumColumnID As Variant, _
ByVal SingleColumnColorIndex As Long) _
As Double
Application.Volatile
Dim lrg As Range: Set lrg = SingleColumnRange.Columns(1)
Dim crg As Range: Set crg = lrg.EntireRow.Columns(CompareColumnID)
Dim srg As Range: Set srg = lrg.EntireRow.Columns(SumColumnID)
'Debug.Print lrg.Address, crg.Address, srg.Address
Dim lCell As Range ' Lookup cell
Dim r As Long ' Range Row
Dim lString As String ' Lookup String
Dim cString As String ' Compare String
Dim sValue As Variant ' Sum Value
Dim Total As Double ' Total Sum
For Each lCell In lrg.Cells
r = r + 1
lString = CStr(lCell.Value)
cString = CStr(crg.Cells(r).Value)
If StrComp(lString, cString, vbTextCompare) <> 0 Then ' not equal
If lCell.Interior.ColorIndex = SingleColumnColorIndex Then
sValue = srg.Cells(r).Value
'Debug.Print r, lString, cString, sValue
If VarType(sValue) = vbDouble Then ' is a number
Total = Total + sValue
End If
End If
End If
Next lCell
SumRenewed = Total
End Function
In Excel (not recommended)
Note that it will update on each calculation due to Application.Volatile. It will never update if the color has changed. Hence it is practically useless in Excel.
=SumRenewed(E2:E21,"D","B",43)
I have a Range of Cells in Excel with numbers (Let's say A1:Z1) and I want to get three highest numbers. Answer to this part of the question I found here - Finding highest and subsequent values in a range
But I want also to get the cell reference of these values.
firstVal = Application.WorksheetFunction.Large(rng,1)
secondVal = Application.WorksheetFunction.Large(rng,2)
thirdVal = Application.WorksheetFunction.Large(rng,3)
After getting the values, try looping through the range and assign range variables to these. Then print the addresses of the range variables:
Sub TestMe()
Dim firstVal As Double
Dim secondVal As Double
Dim thirdVal As Double
Dim rng As Range
Set rng = Worksheets(1).Range("A1:B10")
With Application
firstVal = Application.WorksheetFunction.Large(rng, 1)
secondVal = Application.WorksheetFunction.Large(rng, 2)
thirdVal = Application.WorksheetFunction.Large(rng, 3)
End With
Dim myCell As Range
Dim firstCell As Range
Dim secondCell As Range
Dim thirdCell As Range
For Each myCell In rng
If myCell.Value = firstVal And (firstCell Is Nothing) Then
Set firstCell = myCell
ElseIf myCell.Value = secondVal And (secondCell Is Nothing) Then
Set secondCell = myCell
ElseIf myCell.Value = thirdVal And (thirdCell Is Nothing) Then
Set thirdCell = myCell
End If
Next myCell
Debug.Print firstCell.Address, secondCell.Address, thirdCell.Address
End Sub
The check firstCell Is Nothing is done to make sure that in case of more than one top variable, the second one is assigned to the secondCell. E.g., if the range looks like this:
then the top 3 cells would be A2, A3, A1.
Not very efficient but you can specify how many addresses to return:
Sub Tester()
Debug.Print Join(Largest(ActiveSheet.Range("A1:Z1"), 3), ", ")
End Sub
Function Largest(rng As Range, howMany As Long)
Dim rv(), n As Long, c As Range, lg
ReDim rv(1 To howMany)
n = 1
Do
lg = Application.Large(rng, n)
For Each c In rng
If c.Value = lg Then
If IsError(Application.Match(c.Address, rv, 0)) Then
rv(n) = c.Address
n = n + 1
Exit For
End If
End If
Next c
Loop While n <= howMany
Largest = rv
End Function
I am trying to write a function that automatically deletes the minimum value for a given selection of cells. I know how to find the minimum value but I just don't know how to delete that value.
Here's what I've got.
Function MinDel(Stuff)
MinDel = Application.Worksheetfunction.min(stuff)
End Function
How do I delete the MinDel value?
You could modify the function from here like that
Option Explicit
Function AddressOfMax(rng As Range) As Range
Set AddressOfMax = rng.Cells(WorksheetFunction.Match(WorksheetFunction.Max(rng), rng, 0))
End Function
Function AddressOfMin(rng As Range) As Range
Set AddressOfMin = rng.Cells(WorksheetFunction.Match(WorksheetFunction.Min(rng), rng, 0))
End Function
Sub TestIt()
Dim rg As Range
Dim rgMin As Range
Set rg = ActiveSheet.Range("A1:A6")
Set rgMin = AddressOfMin(rg)
rgMin.Clear
End Sub
As mentioned in the comments, a UDF (User Defined Function) cannot change a value or range in Excel by default and a Sub should be used. This is my way to delete the minimal value in the Selection:
Public Sub DeleteMinimum()
Dim myRange As Range
Dim minValue As Double
Dim myMin As Range
If Not TypeOf Selection Is Excel.Range Then Exit Sub
Dim valueAssigned As Boolean: valueAssigned = False
minValue = 0
For Each myRange In Selection
If IsNumeric(myRange) Then
If Not valueAssigned Then
valueAssigned = True
minValue = myRange
Set myMin = myRange
Else
If myRange < minValue Then
minValue = myRange
Set myMin = myRange
End If
End If
End If
Next myRange
If Not myMin Is Nothing Then
myMin = "DELETED!"
End If
End Sub
The procedure below will delete the lowest value in a selection, provided that the selection comprises more than one cell. It ignores all but the first column of the selection.
Sub DelMin()
' 05 Jan 2019
Dim Arr As Variant, i As Integer
Dim Mm As Variant, m As Integer
With Selection
If .Cells.Count > 1 Then
Arr = .Value
For i = 1 To UBound(Arr)
If Not IsEmpty(Arr(i, 1)) Then
If IsEmpty(Mm) Or (Arr(i, 1) < Mm) Then
Mm = Arr(i, 1)
m = i
End If
End If
Next i
.Cells(m, 1).ClearContents
End If
End With End Sub
I'm trying to write a macro in Excel to sum by font colour. A colleague has suggested I use this article for help: ExtendOffice. However, it always gives a syntax error, and I'm not sure why.
The code is:
Public Function SumByColor(pRange1 As Range, pRange2 As Range) As Double
Application.Volatile
Dim rng As Range
Dim xTotal As Double
xTotal = 0
For Each rng In pRange1
If rng.Font.Color = pRange2.Font.Color Then
xTotal = xTotal + rng.Value
End If
Next
SumByColor = xTotal
End Function
The only reason you might get an error, is if one of the cells inside pRange1 has a String or some other non-numeric value.
You can modify your code by adding If IsNumeric(rng.Value) Then.
Modifed Code
Public Function SumByColor(pRange1 As Range, pRange2 As Range) As Double
Application.Volatile
Dim rng As Range
Dim xTotal As Double
xTotal = 0
For Each rng In pRange1
If rng.Font.Color = pRange2.Font.Color Then
If IsNumeric(rng.Value) Then ' <-- the only thing which might give you an error, if you have a String inside one of the cells
xTotal = xTotal + rng.Value
End If
End If
Next rng
SumByColor = xTotal
End Function
How it is being called from a sheet's cell:
Note: if you change the font's color of one of the cells, you will need to refresh the value in the cell by pressing {Enter} on the cell with the formula again.
This works good for me. Maybe you simply called it in the wrong manner.
For example, if I have data on the "D" column with some fonts in it, this code would work for me:
Public Function SumByColor(pRange1 As Range, pRange2 As Range) As Double
Application.Volatile
Dim rng As Range
Dim xTotal As Double
xTotal = 0
For Each rng In pRange1
If rng.Font.Color = pRange2.Font.Color Then
xTotal = xTotal + rng.Value
End If
Next
SumByColor = xTotal
MsgBox (SumByColor)
End Function
Sub count_colors()
Call SumByColor(Range("D2", "D" & ActiveSheet.UsedRange.Rows.Count), Range("D2"))
End Sub
(I ignored D1 since it's a header for me. You can change to anything you like)
I do have to agree with Rory, however, that using fonts as data separators is not a good idea
apologies if this has already been answered although I have searched and search with no luck. in a nutshell im trying to change the cell colour if that cell value does not match a value in a named range.
I have tried a number of methods although none are working for me , any help from the vba gurus would be greatly appreciated.
essentially I have a list of values on sheet1(Create) G2:G5000 that I need to know when they don't match value on sheet2(lists) S2:S64 <--this has a named range of Make.
please see a copy of my current code below
Sub testMake()
Dim MkData As Range, MkVal As Range
Dim MKArray As Variant
Set MkData = Worksheets("Create").Range("G2:G5000")
Set MkVal = Worksheets("Lists").Range("Make")
For Each MyCell In MkData
If MyCell.Value <> Range("MkVal") Then
MyCell.Interior.ColorIndex = 6
Else
MyCell.Interior.ColorIndex = xlNone
End If
Next
End Sub
Thanks you all for any help in advance, I have been looking at this for a few days now and seem to be no closer than when I started.
While I would use conditional formatting you could slightly adapt your code as below to do this programatically:
Sub testMake()
Dim MkData As Range
Dim MkVal As Range
Dim MKArray As Variant
Dim lngRow As Long
Dim rng1 As Range
Dim rng2 As Range
MKArray = Worksheets("Create").Range("G2:G5000").Value2
Set rng1 = Worksheets("Create").Range("G2")
Set MkVal = Range("Make")
For lngRow = 1 To UBound(MKArray)
If IsError(Application.Match(MKArray(lngRow, 1), MkVal, 0)) Then
If Not rng2 Is Nothing Then
Set rng2 = Union(rng2, rng1.Offset(lngRow - 1, 0))
Else
Set rng2 = rng1.Offset(lngRow - 1, 0)
End If
End If
Next
If Not rng2 Is Nothing Then rng2.Interior.ColorIndex = 6
End Sub
You could be using Worksheet function Vlookup to compare between the two ranges:
Sub testMake()
Dim MkData As Range, MkVal As Range
Dim MKArray As Variant
Dim result As Variant
Set MkData = Worksheets("Create").Range("G2:G5000")
Set MkVal = Worksheets("Lists").Range("Make")
For Each MyCell In MkData
On Error Resume Next
result = Application.WorksheetFunction.VLookup(MyCell, MkVal, 1, False)
If Err <> 0 Then
result = CVErr(xlErrNA)
End If
If Not IsError(result) Then
MyCell.Interior.ColorIndex = xlNone
Else
MyCell.Interior.ColorIndex = 6
End If
Next
End Sub