Incrementing the row in a loop using a variable in VBA - excel

I am trying to search down a column of an excel sheet for identical text which is an argument of the function.
Function getRow(callerID As String) As Integer
Dim CalcRow As Integer
Dim CurrRow As Integer
Dim CurrCol As Integer
Dim SearchSheet As Worksheet
'Define variables
Set SearchSheet = ThisWorkbook.Worksheets("Calculations")
Set CellSearch = SearchSheet.Cells(CurrRow,CurrCol)
CalcRow = 2
CurrRow = 2
CurrCol = 16
Do Until CellSearch.Value = ""
If callerID = CellSearch.Value Then
Exit Do
Else
CurrRow = CurrRow + 1
CalcRow = CalcRow + 1
End If
Loop
'set return value
getRow = CalcRow
End Function
It keeps saying this is an error: Set CellSearch = SearchSheet.Cells(CurrRow,CurrCol) when I try to refer to it as a range.
I've tried referring to the range in other ways --I just want to increment the row by 1 until each cell in that column with a value is searched.
I'm very new to VBA so I've had some trouble with referring to cells without using ActiveCell. I don't want to use ActiveCell for this.

Get the Worksheet Row of the First Occurrence of a String in a Column
To allow to find other data types (Numbers, Dates, Booleans...) you only have to change
callerID As Variant
Note that Application.Match is case-insensitive i.e. MYSTRING = mystring. Also, it is handled differently than WorksheetFunction.Match i.e. its result can be tested with IsError or IsNumeric while the WorksheetFunction version will raise an error if no match is found.
Range.Resize Property
Keep in mind that the Range.Find method is unreliable if the worksheet is filtered.
The Code
Option Explicit
Function getRow(callerID As String) As Long
' Define the First Cell
With ThisWorkbook.Worksheets("Calculations").Range("P2")
' Calculate the Row Offset which is utilized with resize
' and when writing the result.
Dim RowOffset As Long: RowOffset = .Row - 1
' Declare a range variable.
Dim rg As Range
' Attempt to define the Last Non-Empty Cell.
Set rg = .Resize(.Worksheet.Rows.Count - RowOffset) _
.Find("*", , xlFormulas, , , xlPrevious)
' Validate the Last Non-Empty Cell.
If Not rg Is Nothing Then
' Define the Column Range, the range from the First Cell
' to the Last Non-Empty Cell in the worksheet column.
Set rg = .Resize(rg.Row - RowOffset)
' Attempt to find the Index (position) of the Caller ID
' in the Column Range.
Dim cIndex As Variant: cIndex = Application.Match(callerID, rg, 0)
' Validate the Index i.e. check if the Caller ID was found.
If IsNumeric(cIndex) Then
' Write the result. Note that the Index is the position
' in the Column Range, so to return the position (row)
' in the worksheet, the Row Offset has to be added.
getRow = cIndex + RowOffset
'Else ' Caller ID was not found (cIndex is an error value).
End If
'Else ' The range from the First Cell to the bottom-most cell
' of the worksheet column is empty.
End If
End With
End Function

Related

VBA for excel error of type mismatch error centering around "For k= rangeValues(0) To rangeValues(1)"

Ok, admittedly I am trying this with chatgpt and going in circles. Just trying to solve a work problem, and I am not a programmer. I need to handle data that is numeric and alphanumeric and also in ranges. it appears as such: TU1000-TU1005,23000,2400-2500 etc... I am working with data in an excel document and trying to use VBA to do so. I am trying copy a single selected cells contents, and break it down vertically onto a another sheet. the contents may be numeric or alphanumeric, I am getting suggestions about perhaps using a variable arrary from chatgpt. But for all I know this is way off base.
This is what it has come up with after a myriad of attempts:
Sub CopyAndPasteValue()
Dim sourceRange As Range
Dim targetRange As Range
Dim cell As Range
Dim value As Variant
Dim uniqueValues As New Collection
Dim uniqueValuesArray() As Variant ' declare an array variable
Dim i As Long, j As Long, k As Long
Dim sourceArray() As String ' declare sourceArray as a string array
Dim RowCount As Long ' declare RowCount as a Long variable
' Set the source range to the selected cells in the CMP update requests sheet
Set sourceRange = Selection
' If the source range is a single cell, split the cell value into an array
If sourceRange.Cells.Count = 1 Then
sourceArray = Split(sourceRange.value, ",")
RowCount = UBound(sourceArray) - LBound(sourceArray) + 1
Set sourceRange = sourceRange.Resize(RowCount, 1)
End If
' Clear contents of previous data in the index and match sheet
Sheets("index and match sheet").Range("A2:A" & Rows.Count).ClearContents
' Set the target range to cell A2 in the index and match sheet
Set targetRange = Sheets("index and match sheet").Range("A2")
' Loop through each cell in the source range
For Each cell In sourceRange
' Split the cell value by comma and loop through resulting values
If Len(cell.value) > 0 Then
For i = 0 To UBound(Split(cell.value, ","))
value = Trim(Split(cell.value, ",")(i))
' Check if value contains a dash
If InStr(value, "-") > 0 Then
' Split the value by dash
Dim rangeValues() As String
rangeValues = Split(value, "-")
If IsNumeric(rangeValues(0)) And IsNumeric(rangeValues(1)) Then
For k = CLng(rangeValues(0)) To CLng(rangeValues(1))
' Add the value to the unique values collection if it is not already present
On Error Resume Next
uniqueValues.Add CStr(k), CStr(k)
On Error GoTo 0
Next k
Else
For k = rangeValues(0) To rangeValues(1)
'likely I need this to be a variant array which is an array declared as having a variant data type'
' Add the value to the unique values collection if it is not already present
On Error Resume Next
uniqueValues.Add CStr(k), CStr(k)
On Error GoTo 0
Next k
End If
Else
' Add the value to the unique values collection if it is not already present
On Error Resume Next
uniqueValues.Add value, value
On Error GoTo 0
End If
Next i
End If
Next cell
' Convert the collection to an array
ReDim uniqueValuesArray(0 To uniqueValues.Count - 1)
For i = 1 To uniqueValues.Count
uniqueValuesArray(i - 1) = uniqueValues(i)
Next i
' Loop through uniqueValues array and paste each value to the target range in the index and match sheet
For j = 0 To UBound(uniqueValuesArray)
targetRange.value = uniqueValuesArray(j)
Set targetRange = targetRange.Offset(1, 0)
Next j
' Copy range D1:D141 to range E1:E141 using the Value property
Sheets("index and match sheet").Range("E1:E141").value = Sheets("index and match sheet").Range("D1:D141").value
End Sub

Assign Different Formulas To Cells According By Criteria

I have written a code in which I am trying to use two different formulas with a set of conditions like if we take RUZ currency into consideration. where we have tenors between (SW- 1Y), the formula should be =1/(1/R208C[-5]+RC12/10000) and for the rest of the tenors (2Y, 3Y,5Y) the formula should be =1*RC[-5]. this condition is only applicable on RUZ ccy, for the rest, one formula per ccy(currency) will be used for all their respective tenors.
the formula is placed in column P,
tenors are placed in column B
Sub Get_vpl()
' Define Constants.
Const wsName As String = "DS"
Const FirstRow As Long = 5
Const srcCol As String = "A"
Const tgtCol As String = "P"
Dim Criteria As Variant
Dim Formulas As Variant
Criteria = Array("RUB", "TRY", "TWD", "UAH", "UYU", "VND") ' add more...
Formulas = Array( "=1/(1/R208C[-5]+RC12/10000)", "=1*RC[-5]", "=1/(1/R232C[-5]+RC12/1)", "=1*RC[-5]", "=1*RC[-5]", "=1*RC[-5]") ' add more...
' Define the Source Column Range.
' Define workbook.
Dim wb As Workbook
Set wb = ThisWorkbook
' Define worksheet.
Dim ws As Worksheet
Set ws = wb.Worksheets(wsName)
' Calculate Last Non-Empty Row.
Dim LastRow As Long
LastRow = ws.Cells(ws.Rows.Count, srcCol).End(xlUp).Row
' Define Source Column Range.
Dim rng As Range
Set rng = ws.Range(ws.Cells(FirstRow, srcCol), ws.Cells(LastRow, srcCol))
' Prepare to write to Target Column Range.
' Calculate Column Offset.
Dim ColOffset As Long
ColOffset = ws.Columns(tgtCol).Column - ws.Columns(srcCol).Column
' Declare variables.
Dim CurPos As Variant ' Current Position
Dim cel As Range ' Current Cell Range
' Write formulas to Target Column Range.
Application.ScreenUpdating = False
' Iterate the cell ranges in Source Range.
For Each cel In rng.Cells
' Check if Current Cell Range in Source Column Range is not empty.
If Not IsEmpty(cel) Then
' Try to find the value in Current Cell Range in Criteria Array
' and write the position to Current Position
CurPos = Application.Match(cel, Criteria, 0)
' Check if value in Current Cell Range has been found
' in Criteria Array.
If Not IsError(CurPos) Then
' Write formula from Formulas Array to current Target Cell
' Range, using Current Position in Criteria Array.
cel.Offset(, ColOffset).Formula = _
Application.Index(Formulas, CurPos)
End If
End If
Next cel
Application.ScreenUpdating = True
End Sub
I have done more than intended to your code because I had so much difficulty understanding what you need. However, I'm rather pleased with the result and hope you will be, too. Note that I never ran the code and it may, therefore, contain minor bugs or typos which I shall be happy to rectify if you point them out.
Option Explicit
Enum Nws ' worksheet navigation
NwsFirstRow = 5
NwsCcy = 1 ' Columns: A = Currency
NwsTenor ' B = Tenor
NwsTarget = 16 ' P = Target
End Enum
Sub Get_vpl()
' 116
' Define Constants.
Const wsName As String = "DS"
' Declare variables.
Dim Wb As Workbook
Dim Ws As Worksheet
Dim CcyIdx As Integer ' return value from CurrencyIndex()
Dim R As Long ' loop counter: rows
Set Wb = ThisWorkbook
Set Ws = Wb.Worksheets(wsName)
Application.ScreenUpdating = False
With Ws
' this syntax is easier because you need the row number R
For R = NwsFirstRow To .Cells(.Rows.Count, NwsCcy).End(xlUp).Row
CcyIdx = CurrencyIndex(.Cells(R, NwsCcy).Value)
If CcyIdx >= 0 Then
.Cells(R, NwsTarget).Formula = ChooseFormula(CcyIdx, .Cells(R, NwsTenor).Value)
End If
Next R
End With
Application.ScreenUpdating = True
End Sub
Private Function ChooseFormula(ByVal CcyIdx As Integer, _
ByVal Tenor As String) As String
' 116
' return the formula specified by Idx or Formula(0)
Dim Idx As Integer
Dim Formula(2) As String
' the advantage of the syntax you chose is that the array
' is dimensioned automatically.
' Here the advantage is clarity.
Formula(0) = "=1*RC[-5]"
Formula(1) = "=1/(1/R208C[-5]+RC12/10000)"
Formula(2) = "=1/(1/R232C[-5]+RC12/1)"
If CcyIdx = 0 Then
If InStr("1Y,2Y,3Y,5Y", Tenor) Then Idx = 1
End If
ChooseFormula = Formula(Idx)
End Function
Private Function CurrencyIndex(ByVal Currcy As String) As Integer
' 116
' return -1 if not found or blank
Dim Ccy() As String ' list of currencies
Dim i As Integer
' I added "RUZ" in position 0 (change to suit and match in ChooseFormula())
' this syntax uses less space but doesn't support MATCH()
Ccy = Split("RUZ RUB TRY TWD UAH UYU VND") ' add more...
If Len(Trim(Currcy)) Then
For i = UBound(Ccy) To 0 Step -1
If StrComp(Currcy, Ccy(i), vbTextCompare) = 0 Then Exit For
Next i
Else
i = -1
End If
CurrencyIndex = i
End Function
I found your Criteria rather useless in this context. Perhaps that's why I gave it a task. The function CurrencyIndex() returns the index number of the current currency and uses this number thereafter in place of the actual currency code. For this purpose I added "RUZ" to your array. I have it in first position but any other number will do as well.
Please look at the function ChooseFormula(). It seems you have only 3 formulas. I assigned the index 0 to the most common one and made that the default. For the rest of it, the CcyIdx is passed to the function as an argument and if that index = 0 it identifies "RUZ" and gives it special treatment. I'm not sure that the treatment I assigned is 100% correct or workable but I think the code is simple and you should be able to modify it as required. Observe that the function won't ever return Formula(2) in its present state but you can modify it easily to accommodate all kinds of conditions and many more possible formulas. Let me know if you need any help with that.

VBA copy UsedRange of a column based on column header name

I am trying to copy data from one worksheet to another based on the column-name. In the source worksheet, the data starts at A1. In the destination worksheet, the data should be pasted at row A11 and not A1. If I used EntireColumn.Copy I get an error about the source and destination copy area not being the same. I came across the UsedRange property but I am unbale to apply it to my scenario
For Each columnName In allColumns
'call a function to get the column to copy
If columnToCopy > 0 Then
columnName.Offset(1, 0).EntireColumn.Copy Destination:=ws2.Cells(11, columnToCopy)
End If
Next
In the above snippet, In dont want to use 'EntireColumn'. I only want the columns that have data. The variable columnName is for example 'Person ID'
What is the best way to do this?
Thanks.
This would be a typical approach:
For Each ColumnName In allColumns
If columnToCopy > 0 Then
With ColumnName.Parent
.Range(ColumnName.Offset(1, 0), .Cells(.Rows.Count, ColumnName.Column).End(xlUp)).Copy _
Destination:=ws2.Cells(11, columnToCopy)
End With
End If
Next
Assumes allColumns is a collection of single-cell ranges/column headers.
Copy/Paste Column
There is not enough information to give an accurate answer so here is a scenario you might consider studying.
The Code
Option Explicit
Sub TESTdetermineColumnNumber()
' Define constants. Should be more.
' Define Criteria.
Const Criteria As String = "Total"
' Define Header Row.
Const hRow As Long = 1
' Define Copy Range (Column Range)
' Define Source Worksheet.
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("Sheet1")
' Define Header Row Range.
Dim RowRange As Range
Set RowRange = ws.Rows(hRow)
' Determine Column Number.
Dim ColumnNumber As Long
ColumnNumber = determineColumnNumber(RowRange, Criteria)
' Validate Column Number.
If ColumnNumber = 0 Then
Exit Sub
End If
' Determine Last Row.
Dim LastRow As Long
LastRow = ws.Cells(ws.Rows.Count, ColumnNumber).End(xlUp).Row
' Define First Data Row Number.
Dim FirstRow As Long
FirstRow = hRow + 1
' Define Column Range.
Dim ColumnRange As Range
Set ColumnRange = ws.Cells(FirstRow, ColumnNumber) _
.Resize(LastRow - FirstRow + 1)
' Define Paste Range.
' Define Destination Worksheet.
Dim ws2 As Worksheet
Set ws2 = ThisWorkbook.Worksheets("Sheet2")
' Define Destination Column.
Dim columnToCopy As Long
columnToCopy = 2
' Define Paste Range.
Dim PasteRange As Range
Set PasteRange = ws2.Cells(11, columnToCopy)
' Copy/Paste.
' Copy values, formulas and formats.
ColumnRange.Copy Destination:=PasteRange
' It is more efficient if you need only values to use the following:
PasteRange.Resize(ColumnRange.Rows.Count).Value = ColumnRange.Value
End Sub
Function determineColumnNumber(RowRange As Range, _
Criteria As String) _
As Long
Dim Temp As Variant
Temp = Application.Match(Criteria, RowRange, 0)
If Not IsError(Temp) Then
determineColumnNumber = Temp
End If
End Function

VBA Excel: Trying to name dynamic ranges organized into rows

I have data organized into rows and in column B I have data titles. I want to select the data after the titles and then give them range names based on that title. I was able to code a solution that could name column ranges dynamically this way, but when altering it to name the rows of data I run into a 1004 error, specifically at the rng.CreateNames point.
Sub RowNames()
Dim ws As Worksheet, firstCol As Long, lastCol As Long, rowNum As Long, r As Integer, n As Integer, rng As Range, rngName As Range
Set ws = ThisWorkbook.Sheets("MonthlySales")
Set rng = ws.Range("B2:N41")
For n = 1 To rng.Rows.Count
For r = rng.Columns.Count To 1 Step -1
rowNum = rng.Rows(n).Row
firstCol = rng.Columns(1).Column
lastCol = rng.Columns(r).Column
If Cells(firstCol, rowNum).Value <> "" Then
Set rngName = Range(Cells(firstCol, rowNum), Cells(lastCol, rowNum))
rngName.CreateNames Left:=True
Exit For
End If
Next r
Next n
End Sub
Naming Row Ranges
Range.CreateNames Method
Frankly, never heard of it. Basically, in this case, you take a range and write different names in its first column and when you loop through the rows, for each row you write something like Range("A1:D1").CreateNames Left:=True to create a named range whose name is the value in A1 and it will refer to the range B1:D1.
To mix it up, this example (I think OP also) assumes that there might be blank cells in the first column, and the number of cells in each row range may vary. Each row range will be checked backwards for a value which will define its size.
The Code
Option Explicit
Sub RowNames()
' Define worksheet.
Dim ws As Worksheet
Set ws = ThisWorkbook.Sheets("MonthlySales")
' Define Source Range.
Dim rng As Range
Set rng = ws.Range("B2:N41")
' Define Columns Count.
Dim ColumnsCount As Long
ColumnsCount = rng.Columns.Count
Dim RowRange As Range ' Current Row Range
Dim r As Long ' Source Range Rows Counter
Dim c As Long ' Source Range / Current Row Range Columns Counter
' Loop through rows of Source Range.
For r = 1 To rng.Rows.Count
' Create a reference to the current Row Range.
Set RowRange = rng.Rows(r)
' Check if first cell of current Row Range contains a value,
' making it a possible candidate for a defined name.
If RowRange.Cells(1).Value <> "" Then
' Loop through cells (columns) of current Row Range backwards.
For c = ColumnsCount To 2 Step -1
' Check if current cell in current Row Range contains a value.
If RowRange.Cells(c) <> "" Then
' Create a named range from value in first cell. The range
' is defined from the second cell to to current cell
' in current Row Range.
RowRange.Cells(1).Resize(, c).CreateNames Left:=True
' Exit loop, we got what we came for (the named range).
Exit For
End If
Next c
End If
Next r
End Sub

Copying data from cells and adding it up Excel visual basic

I would like to make a macro that copies numbers that fall under the same category and add them up separately for each category. For instance Cells in column c contain the name of the product than 4 columns to the right is the number of sold products. I would like to add up all the entries in the number of sold products that fall under the same product together for each product and write it out to a predefined cell. So far I have come up with this
Sub find()
Dim XXX As Range
Dim myTotal As Long
Dim name As String
Dim name2 As String
name = Range("C2")
For Each XXX In Range("C2:C99999")
name2 = ActiveCell.Value
If name <> name2 Then
Dim aa As Integer
aa = 1
Cells(aa, 8).Value = name
Cells(aa, 9).Value = myTotal
name = name2
myTotal = 0
aa = aa + 1
End If
If InStr(XXX.Value, name2) > 0 Then
myTotal = myTotal + XXX.Offset(0, 4).Value
End If
Next XXX
End Sub
Any tips or guidelines would be appreciated and I hope the explanation makes sense.
Here's a faster basic approach:
Sub find()
Dim dict As Object, names, nums, r As Long
Dim sht As Worksheet
Set sht = ActiveSheet
Set dict = CreateObject("scripting.dictionary")
names = Range("C2:C99999").Value
nums = Range("C2:C99999").Offset(0, 4).Value
For r = 1 To UBound(names)
dict(names(r, 1)) = dict(names(r, 1)) + nums(r, 1)
Next r
WriteCounts dict, sht.Range("J1")
End Sub
Sub WriteCounts(dict As Object, rngStart As Range)
Dim k
For Each k In dict.keys
rngStart.Value = k
rngStart.Offset(0, 1).Value = dict(k)
Set rngStart = rngStart.Offset(1, 0)
Next k
End Sub
The Dictionary 'SumIf' Feature
VBA Dictionary Solution
Credits to Tim Williams and
his
solution.
Why would OP want a VBA solution when there is a perfectly good Excel
solution? When there are tens of thousands of records and as many or
many times more formulas, the workbook tends to get slow. So by adding
the SUMIF formula we're adding another bunch of them slowing down
even more. And we don't know the unique values, which we could find
using another seriously slowing down formula.
So VBA will do this in a split second, or will it? I created a new
worksheet with 60000 records and with 1000 unique ones to try to
prove it.
SumIf Solution: The first idea was to adjust all the ranges, get the unique values using Advanced Filter and then use
Worksheetfunction.SumIf. SumIf took its time, 17s, and when I
added some formulas it went above 20s.
Array Loop Solution: This one was again using Advanced Filter but this time the idea was to put everything into arrays and loop
through them and adding the values to another array one by one. This
time the loop took its time. After some tweaking it went down to 13s
and stayed there even after adding formulas.
Advanced Filter did copy the unique values in less than 0.2s into
the appropriate range, but the rest was taking too long.
Dictionary Solution: Tim Williams' solution did initially do all this in 2.5s. How is that possible I thought, Advanced Filter is the god
of unique values. Well, it isn't, or at best it is only one of them. I saw
this line in a loop in the code: dict(names(r, 1)) = dict(names(r, 1)) + nums(r, 1). It seemed like it was doing the heavy lifting in a split second which forced me to investigate (Dictionary Object (Microsoft), Excel VBA Dictionary: A Complete Guide (Paul Kelly) and produce a
solution.
The Code
Sub SumIfToTarget3() ' Array Dictionary ... 0.2-0.3s
' Name
Const cNsht As Variant = "Sheet2" ' Name Worksheet Name/Index
Const cNrow As Long = 1 ' Name First Row Number
Const cNcol As Long = 3 ' Name Column Number
Const cVcol As Long = 7 ' Value Column Number
' Target
Const cTsht As Variant = "Sheet2" ' Target Worksheet Name/Index
Const cTrow As Long = 1 ' Target First Row Number
Const cUcol As Long = 8 ' Unique Column Number
Const cUnique As String = "Unique" ' Unique Column Header
Const cSumIf As String = "Total" ' SumIf Column Header
' Create a reference to the Dictionary Object.
'*******************************************************
' Early Binding (0.1s Faster) *
' You have to go to Tools>References and check (create *
' a reference to) "Microsoft Scripting Runtime" . *
' Dim dict As New Dictionary ' *
'*******************************************************
'**************************************************
' Late Binding (0.1s Slower) *
' You don't need to create a reference. *
Dim dict As Object ' *
Set dict = CreateObject("Scripting.Dictionary") ' *
'**************************************************
Dim dk As Variant ' Dictionary 'Counter' (For Each Control Variable)
Dim CurV As Variant ' Current Value
Dim rngN As Range ' Name Column Range, Last Used Cell in Name Column,
' Name Range with Headers, Name Range
Dim rngV As Range ' Value Range
Dim rngT As Range ' Target Columns Range, Target Range
Dim vntN As Variant ' Name Array
Dim vntV As Variant ' Value Array
Dim vntT As Variant ' Target Array
Dim i As Long ' Name/Value Array Element (Row) Counter,
' Target Array Row Counter, Target Array Rows Count
' (Dictionary Items Count)
' Speed up.
With Application
.ScreenUpdating = False
.Calculation = xlCalculationManual
End With
' Handle Errors.
On Error GoTo ErrorHandler
' In Unique Column
With ThisWorkbook.Worksheets(cTsht).Columns(cUcol)
' Create a reference to Target Columns Range (rngT) i.e. the range in
' Unique Column (cUcol) from Target First Row (cTrow) to the bottom row
' of Target Worksheet (cTsht), resized by a column for SumIf Column (2).
Set rngT = .Resize(.Rows.Count - cTrow + 1, 2).Offset(cTrow - 1)
End With
' Clear contents of Target Columns Range (rngT).
rngT.ClearContents
' Write Unique Column Header to 1st Cell of Target Columns Range.
rngT.Cells(1) = cUnique
' Write SumIf Column Header to 2nd Cell of Target Columns Range.
rngT.Cells(2) = cSumIf
' In Name Column
With ThisWorkbook.Worksheets(cNsht).Columns(cNcol)
' Calculate Last Used Cell in Name Column.
Set rngN = .find("*", , xlFormulas, , xlByColumns, xlPrevious)
' Calculate Name Range with headers.
Set rngN = .Cells(cNrow).Resize(rngN.Row - cNrow + 1)
End With
' Calculate Name Range (without headers).
Set rngN = rngN.Resize(rngN.Rows.Count - 1).Offset(1)
' Copy Name Range (rngN) to Name Array (vntN).
vntN = rngN
' Calculate Value Range (without headers).
Set rngV = rngN.Offset(, cVcol - cNcol)
' Copy Value Range (rngV) to Value Array (vntV).
vntV = rngV
' Loop through elements (rows) of Name Array.
For i = 1 To UBound(vntN)
' Write element in current row (i) of Value Array (vntV) to Current
' Value.
CurV = vntV(i, 1)
' Check if Current Value (CurV) is NOT a number.
If Not IsNumeric(CurV) Then
' Assign 0 to Current Value.
CurV = 0
End If
' Add current element (row) in Name Array (vntN) and Current Value
' to the Dictionary. If the key to be added is new (not existing),
' the new key and the item will be added. But if the key exists, then
' the existing item will be increased by the value of the new item.
' This could be called "The Dictionary SumIf Feature".
dict(vntN(i, 1)) = dict(vntN(i, 1)) + CurV
Next
' Reset Name/Value Array Element (Row) Counter to be used as
' Target Array Row Counter.
i = 0
' Resize Target Array to the number of items in the Dictionary.
ReDim vntT(1 To dict.Count, 1 To 2)
' Loop through each Key (Item) in the Dictionary.
For Each dk In dict.Keys
' Increase Target Array Row Counter (count Target Array Row).
i = i + 1
' Write current Dictionary Key to element in current (row) and
' 1st column (Unique) of Target Array.
vntT(i, 1) = dk
' Write current Dictionary Item to element in current (row) and
' 2nd column (SumIf) of Target Array.
vntT(i, 2) = dict(dk)
Next
' Calculate Target Range (rngT) from second row (2) of Target Columns
' Range (rngT) resized by Target Array Rows Count (i).
Set rngT = rngT.Rows(2).Resize(i)
' Copy Target Array (vntT) to Target Range (rngT).
rngT = vntT
ProcedureExit:
' Speed down.
With Application
.Calculation = xlCalculationAutomatic
.ScreenUpdating = True
End With
Exit Sub
ErrorHandler:
MsgBox "An unexpected error occurred. Error '" & Err.Number & "': " _
& Err.Description, vbCritical, "Error"
GoTo ProcedureExit
End Sub
SUMIF?! An Excel Solution
This is more a question than an answer:
Could this be regarded as a simplified visual presentation of what you are trying to achieve?
You can use the following formula in cell I2:
=SUMIF(C$2:C$16,H2,G$2:G$16)
Adjust the ranges and copy down.
Advanced Filter Array Loop Solution
Sub SumIfToUnique2() ' Advanced Filter & Loop through Arrays, Add ... 13s
' Name
Const cNsht As Variant = "Sheet2" ' Name Worksheet Name/Index
Const cNrow As Long = 1 ' Name First Row Number
Const cNcol As Long = 3 ' Name Column Number
Const cVcol As Long = 7 ' Value Column Number
' Unique
Const cUsht As Variant = "Sheet2" ' Unique Worksheet Name/Index
Const cUrow As Long = 1 ' Unique First Row Number
Const cUcol As Long = 8 ' Unique Column Number
Const cSumIf As String = "Total" ' SumIf Column Header
Const cUnique As String = "Unique" ' Unique Column Header
Dim rngN As Range ' Name Column Range, Last Used Cell in Name Column,
' Name Range with Headers, Name Range
Dim rngV As Range ' Value Range
Dim rngU As Range ' Unique Column Range, Last Used Cell in Unique Column,
' Unique Range
Dim vntN As Variant ' Name Array
Dim vntV As Variant ' Value Array
Dim vntU As Variant ' Unique Array
Dim vntS As Variant ' SumIf Array
Dim i As Long ' Name/Value Array Row Counter
Dim k As Long ' Unique/SumIf Array Row Counter
Dim strN As String ' Current Name (in Name Array)
' Speed up.
With Application
.ScreenUpdating = False
.Calculation = xlCalculationManual
End With
' Handle Errors.
On Error GoTo ErrorHandler
' In Name Column
With ThisWorkbook.Worksheets(cNsht).Columns(cNcol)
' Create a reference to Name Column Range (rngN) i.e. the range in
' Name Column (cNcol) from Name First Row (cNrow) to the bottom row
' of Name Worksheet (cNsht).
Set rngN = .Resize(.Rows.Count - cNrow + 1).Offset(cNrow - 1)
End With
' In Unique Column
With ThisWorkbook.Worksheets(cUsht).Columns(cUcol)
' Create a reference to Unique Column Range (rngU) i.e. the range in
' Unique Column (cUcol) from Unique First Row (cUrow) to the bottom row
' of Unique Worksheet (cUsht).
Set rngU = .Resize(.Rows.Count - cUrow + 1).Offset(cUrow - 1)
End With
' Clear contents of Unique Column Range (rngU).
rngU.ClearContents
' Calculate SumIf Column Range.
' Clear contents of SumIf Column Range.
rngU.Offset(, 1).ClearContents
' Write unique values from Name Column Range (rngN), starting with the
' header (aka title), to Unique Column Range (rngU), starting in its
' First Row (1).
rngN.AdvancedFilter xlFilterCopy, , rngU.Resize(1), True
' Calculate Unique Header Cell Range.
' Write Unique Column Header to Unique Header Cell Range.
rngU.Resize(1) = cUnique
' Calculate SumIf Header Cell Range.
' Write SumIf Column Header to SumIf Header Cell Range.
rngU.Resize(1).Offset(, 1) = cSumIf
' In Name Column
With ThisWorkbook.Worksheets(cNsht).Columns(cNcol)
' Calculate Last Used Cell in Name Column.
Set rngN = .find("*", , xlFormulas, , xlByColumns, xlPrevious)
' Calculate Name Range with headers.
Set rngN = .Cells(cNrow).Resize(rngN.Row - cNrow + 1)
End With
' Calculate Name Range (without headers).
Set rngN = rngN.Resize(rngN.Rows.Count - 1).Offset(1)
' Copy Name Range (rngN) to Name Array (vntN).
vntN = rngN
' Calculate Value Range (without headers).
Set rngV = rngN.Offset(, cVcol - cNcol)
' Copy Value Range (rngV) to Value Array (vntV).
vntV = rngV
' In Unique Column
With ThisWorkbook.Worksheets(cUsht).Columns(cUcol)
' Calculate Last Used Cell in Unique Column.
Set rngU = .find("*", , xlFormulas, , xlByColumns, xlPrevious)
' Calculate Unique Range with headers.
Set rngU = .Cells(cUrow).Resize(rngU.Row - cUrow + 1)
End With
' Calculate Unique Range (without headers).
Set rngU = rngU.Resize(rngU.Rows.Count - 1).Offset(1)
' Copy Unique Range (rngU) to Unique Array (vntU).
vntU = rngU
' Resize SumIf Array to size of Unique Array.
ReDim vntS(1 To UBound(vntU), 1 To 1)
' Loop through elements (rows) of Name Array.
For i = 1 To UBound(vntN)
' Write current value in Name Array (vntN) to Current Name (strN).
strN = vntN(i, 1)
' Loop through elements (rows) of Unique/SumIf Array.
For k = 1 To UBound(vntU)
If vntU(k, 1) = strN Then
vntS(k, 1) = vntS(k, 1) + vntV(i, 1)
Exit For
End If
Next
Next
' Calculate SumIf Range (from Unique Range (rngU)).
' Copy SumIf Array to SumIf Range.
rngU.Offset(, 1) = vntS
ProcedureExit:
' Speed down.
With Application
.Calculation = xlCalculationAutomatic
.ScreenUpdating = True
End With
Exit Sub
ErrorHandler:
MsgBox "An unexpected error occurred. Error '" & Err.Number & "': " _
& Err.Description, vbCritical, "Error"
GoTo ProcedureExit
End Sub
Advanced Filter SumIf Solution
Sub SumIfToUnique1() ' Advanced Filter & SumIf on Ranges ... 17-22s
' Name
Const cNsht As Variant = "Sheet2" ' Name Worksheet Name/Index
Const cNrow As Long = 1 ' Name First Row Number
Const cNcol As Long = 3 ' Name Column Number
Const cVcol As Long = 7 ' Value Column Number
' Unique
Const cUsht As Variant = "Sheet2" ' Unique Worksheet Name/Index
Const cUrow As Long = 1 ' Unique First Row Number
Const cUcol As Long = 8 ' Unique Column Number
Const cSumIf As String = "Total" ' SumIf Column Header
Const cUnique As String = "Unique" ' Unique Column Header
Dim rngN As Range ' Name Column Range, Last Used Cell in Name Column,
' Name Range with Headers, Name Range
Dim rngV As Range ' Value Range
Dim rngU As Range ' Unique Column Range, Last Used Cell in Unique Column,
' Unique Range
Dim vntU As Variant ' Unique Array
Dim vntS As Variant ' SumIf Array
Dim i As Long ' Unique Array Row Counter
' Speed up.
With Application
.ScreenUpdating = False
.Calculation = xlCalculationManual
End With
' Handle Errors.
On Error GoTo ErrorHandler
' In Name Column
With ThisWorkbook.Worksheets(cNsht).Columns(cNcol)
' Create a reference to Name Column Range (rngN) i.e. the range in
' Name Column (cNcol) from Name First Row (cNrow) to the bottom row
' of Name Worksheet (cNsht).
Set rngN = .Resize(.Rows.Count - cNrow + 1).Offset(cNrow - 1)
End With
' In Unique Column
With ThisWorkbook.Worksheets(cUsht).Columns(cUcol)
' Create a reference to Unique Column Range (rngU) i.e. the range in
' Unique Column (cUcol) from Unique First Row (cUrow) to the bottom row
' of Unique Worksheet (cUsht).
Set rngU = .Resize(.Rows.Count - cUrow + 1).Offset(cUrow - 1)
End With
' Clear contents of Unique Column Range (rngU).
rngU.ClearContents
' Calculate SumIf Column Range.
' Clear contents of SumIf Column Range.
rngU.Offset(, 1).ClearContents
' Write unique values from Name Column Range (rngN), starting with the
' header (aka title), to Unique Column Range (rngU), starting in its
' First Row (1).
rngN.AdvancedFilter xlFilterCopy, , rngU.Resize(1), True
' Calculate Unique Header Cell Range.
' Write Unique Column Header to Unique Header Cell Range.
rngU.Resize(1) = cUnique
' Calculate SumIf Header Cell Range.
' Write SumIf Column Header to SumIf Header Cell Range.
rngU.Resize(1).Offset(, 1) = cSumIf
' In Name Column
With ThisWorkbook.Worksheets(cNsht).Columns(cNcol)
' Calculate Last Used Cell in Name Column.
Set rngN = .find("*", , xlFormulas, , xlByColumns, xlPrevious)
' Calculate Name Range with headers.
Set rngN = .Cells(cNrow).Resize(rngN.Row - cNrow + 1)
End With
' Calculate Name Range (without headers).
Set rngN = rngN.Resize(rngN.Rows.Count - 1).Offset(1)
' Calculate Value Range (without headers).
Set rngV = rngN.Offset(, cVcol - cNcol)
' In Unique Column
With ThisWorkbook.Worksheets(cUsht).Columns(cUcol)
' Calculate Last Used Cell in Unique Column.
Set rngU = .find("*", , xlFormulas, , xlByColumns, xlPrevious)
' Calculate Unique Range with headers.
Set rngU = .Cells(cUrow).Resize(rngU.Row - cUrow + 1)
End With
' Calculate Unique Range (without headers).
Set rngU = rngU.Resize(rngU.Rows.Count - 1).Offset(1)
' Copy Unique Range to Unique Array.
vntU = rngU
' Resize SumIf Array to size of Unique Array.
ReDim vntS(1 To UBound(vntU), 1 To 1)
' Loop through elements (rows) of SumIf/Unique Array.
For i = 1 To UBound(vntS)
' Write result of SumIf funtion to current element (row) of SumIf Array.
vntS(i, 1) = WorksheetFunction.SumIf(rngN, vntU(i, 1), rngV)
Next
' Calculate SumIf Range (from Unique Range (rngU)).
' Copy SumIf Array to SumIf Range.
rngU.Offset(, 1) = vntS
ProcedureExit:
' Speed down.
With Application
.Calculation = xlCalculationAutomatic
.ScreenUpdating = True
End With
Exit Sub
ErrorHandler:
MsgBox "An unexpected error occurred. Error '" & Err.Number & "': " _
& Err.Description, vbCritical, "Error"
GoTo ProcedureExit
End Sub

Resources