Sum up value with different keys - excel

Please refer to the attached picture to have a better idea.
I have multiple rows in my worksheet with a group name and a lot of values. Each group shows multiple times in my table. Now I would like to sum values for every group and return them. What is the most efficient way to do this?
Now I have the code to store each row's total value to an array and sum it up like below:
Dim arr() as variant
Dim n as integer
Dim sum as variant
For n = firstrow to lastrow 'assume firstrow and lastrow are known numbers
arr = Range(Cells(n, 3),Cells(n,column.count)).Value
sum = Workbookfunction.sum(arr)
Next n
Any thoughts will be quite helpful!

Use SUMPRODUCT:
=SUMPRODUCT(($A$7:$A$18=A1)*($B$7:$G$18))

A VBA Array Version
Before using this code adjust the data in the customize section to fit your needs.
The commented blocks starting with ' str1 = " are used for debugging. You can delete them or uncomment them to see some 'subtotals' in the immediate window.
Option Explicit
Sub SumGroups()
'-- Customize BEGIN --------------------
Const cStrG As String = "B2" 'First cell of the group section
Const cStrD As String = "B15" 'First cell of the data section
'-- Customize END ----------------------
Dim oRng As Range
Dim oRngResults As Range
Dim arrNames As Variant
Dim arrData As Variant
Dim arrResults As Variant
Dim loNames As Long
Dim loData As Long
Dim iDataCol As Integer
Dim dblResults As Double
'Debug
Dim lo1 As Long
Dim i1 As Integer
Dim str1 As String
Dim str2 As String
Dim dTime As Double
' 'Determine the group names range using the first cell of the data section.
' Set oRng = Range(cStrG).Resize(Range(cStrD).Rows.End(xlUp).Row - 1, 1)
'Determine the group names range using the last cell of the group section.
Set oRng = Range(cStrG).Resize(Range(cStrG).Rows.End(xlDown).Row - 1, 1)
'Determine the range of the results
Set oRngResults = oRng.Offset(0, 1)
'Paste the group names range into an array
arrNames = oRng
' str1 = "arrNames:"
' For lo1 = LBound(arrNames) To UBound(arrNames)
' str1 = str1 & vbCrLf & lo1 & ". " & Chr(9) & arrNames(lo1, 1)
' Next
' Debug.Print str1
'Determine the data range using resize NOT finished.
' Set oRng = Range(cStrD).Resize(Cells(Cells.Rows.Count, _
Range(cStrD).Column).End(xlUp).Row - Range(cStrD).Row + 1, 1)
'Determine the data range not using resize.
Set oRng = Range(Cells(Range(cStrD).Row, Range(cStrD).Column), _
Cells(Cells(Cells.Rows.Count, Range(cStrD).Column).End(xlUp).Row, _
Cells(Range(cStrD).Row, Cells.Columns.Count).End(xlToLeft).Column))
'Paste the data range into an array
arrData = oRng
Set oRng = Nothing 'Release object variable
' str1 = "arrData:"
' For lo1 = LBound(arrData) To UBound(arrData)
' str2 = ""
' For i1 = LBound(arrData, 2) To UBound(arrData, 2)
' str2 = str2 & Chr(9) & arrData(lo1, i1)
' Next
' str1 = str1 & vbCrLf & lo1 & "." & str2
' Next
' Debug.Print str1
arrResults = oRngResults
For loNames = LBound(arrNames) To UBound(arrNames)
dblResults = 0
For loData = LBound(arrData) To UBound(arrData)
If arrNames(loNames, 1) = arrData(loData, 1) Then
For iDataCol = LBound(arrData, 2) + 1 To UBound(arrData, 2)
dblResults = dblResults + arrData(loData, iDataCol)
Next
End If
Next
arrResults(loNames, 1) = dblResults
Next
' str1 = "arrResults:"
' For lo1 = LBound(arrResults) To UBound(arrResults)
' str1 = str1 & vbCrLf & lo1 & ". " & Chr(9) & arrResults(lo1, 1)
' Next
' Debug.Print str1
oRngResults = arrResults
Set oRngResults = Nothing 'Release object variable
End Sub
At 50000 rows it calculates in less than a second. The determination of the ranges gave me quite some grief, but I still think they could probably be improved. Would appreciate some feedback regarding the ranges.

I've rewritten the code to use instead of yours. It adds up all the rows between two row indexes, as long as the first cell in each row has a value of "Group A".
Dim firstRow As Integer
Dim lastRow As Integer
Dim currentSum As Integer
Dim currentGroup As String
'Change firstRow and lastRow to the row indexes of the cells you're adding
firstRow = 10
lastRow = 13
currentSum = 0
currentGroup = "Group A"
For n = firstRow To lastRow
If Cells(n, 1).Value = currentGroup Then
currentSum = currentSum + Application.sum(Range(Cells(n, 1), Cells(n, 50)))
End If
'Put the cell name of where you want the value, instead of B3
Range("B3").Value = currentSum
'Change currentGroup to the next group here
Next n

Related

Copy multiple cells linked to a keyword from one column into one cell

I am trying to copy cell values linked to keywords from multiple worksheets into one cell in an overview worksheet. The code works if the keyword only appears once on a worksheet, but if the keyword appears multiple times it only copy and pastes the cell value in the row where the keyword appears first.
Code created by my predecessor.
Public Sub refresh_previous_occupation()
Dim WSUE As Worksheet
Dim ws As Worksheet
Dim rng As Range
Dim str As String
Dim i As Integer
Dim n As Integer
Dim finalrow As Integer
Dim finalrow_ue As Integer
Dim wsarr(6) As Variant
'Array with worksheets that shouldn't be searched
wsarr(0) = Tabelle1.Name
wsarr(1) = Tabelle2.Name
wsarr(2) = Tabelle3.Name
wsarr(3) = Tabelle15.Name
wsarr(4) = Tabelle17.Name
wsarr(5) = Tabelle16.Name
wsarr(6) = Tabelle19.Name
Set WSUE = ThisWorkbook.Worksheets("Übersicht")
finalrow_ue = WSUE.Cells(Rows.Count, 1).End(xlUp).Row
'Search for all keywords in the overview worksheet
For i = 7 To finalrow_ue
str = "" 'reset string variable
For n = 1 To ThisWorkbook.Worksheets.Count 'look through all worksheets
Set ws = ThisWorkbook.Worksheets(n)
If isinarray(ws.Name, wsarr) = False And ws.Visible = xlSheetVisible Then 'check if worksheet is in the array with worksheets that shouldn't be searched an if the worksheet is visible
Set rng = ws.Range("A7:A100").Find(what:=WSUE.Cells(i, 1), LookIn:=xlValues) 'Search for the current keyword on worksheet
If Not rng Is Nothing Then
If str = "" Then 'check if string variable is filled already
If Not rng.Offset(0, 1) = "" Then
str = rng.Offset(0, 1).value & " (" & ws.Name & ")" 'add cell value to string variable
End If
Else
If Not rng.Offset(0, 1) = "" Then
str = str & "; " & vbCrLf & rng.Offset(0, 1).value & " (" & ws.Name & ")" 'add cell value to string variable
End If
End If
End If
End If
Next n
WSUE.Cells(i, 2) = str 'Add string variable value to overview
Next i
End Sub
Is it possible to add a loop to search through the worksheets again to find every instance of the keyword or am I going to have to find a new way to solve my problem?
Your search range is relatively small, so a simple loop over the cells should be fine - no real need for Find():
Public Sub refresh_previous_occupation()
Dim WSUE As Worksheet
Dim ws As Worksheet
Dim str As String
Dim i As Integer
Dim finalrow As Integer
Dim finalrow_ue As Integer
Dim wsarr As Variant, f, s, c As Range
'Array with worksheets that shouldn't be searched
wsarr = Array(Tabelle1.Name, Tabelle2.Name, Tabelle15.Name, _
Tabelle16.Name, Tabelle19.Name)
Set WSUE = ThisWorkbook.Worksheets("Übersicht")
finalrow_ue = WSUE.Cells(Rows.Count, 1).End(xlUp).Row
'Search for all keywords in the overview worksheet
For i = 7 To finalrow_ue
f = WSUE.Cells(i, 1) 'looking for this
str = "" 'reset string variable
For Each ws In ThisWorkbook.Worksheets
'check sheet not in list to ignore
If IsError(Application.Match(ws.Name, wsarr, 0)) Then
'search range is small, so a simple loop is fine here...
For Each c In ws.Range("A7:A100").Cells
If c.Value = f Then
s = c.Offset(0, 1).Value
If Len(s) > 0 Then
If Len(str) > 0 Then str = str & vbLf 'add new line if needed
str = str & s & " (" & ws.Name & "," & c.Address(0, 0) & ")"
End If
End If
Next c
End If
Next ws
WSUE.Cells(i, 2) = str 'Add string variable value to overview
Next i
End Sub

Storing a Dynamic Range in Range variable

I am trying to get unique values from dynamic F column and store it in an array. I am getting "Object Required error for my code while setting Selection variable to a dynamic range. Please help.
Sub UniqueFilter()
Dim tmp As String
Dim arr() As String
Dim Selection As Range
Dim lrow As Long
Dim str As String
Dim cell As Range
Dim sht As Worksheet
Set sht = ThisWorkbook.Worksheets("14Feb19")
sht.Activate
'Set Selection = sht.Range(sht.Cells(1, 6), sht.Cells(Rows.Count, 6).End (xlUp)).Select
lrow = shData.Range("F" & Rows.Count).End(xlUp).Row
Set Selection = sht.Range("F2:F" & lrow).Select
If Not Selection Is Nothing Then
For Each cell In Selection
If (cell <> "") And (InStr(tmp, cell) = 0) Then
tmp = tmp & cell & "|"
End If
Next cell
End If
If Len(tmp) > 0 Then tmp = Left(tmp, Len(tmp) - 1)
arr = Split(tmp, "|")
End Sub
You can achieve your goal without having to use Selection at all.
Just copy the range content and transpose it into an array:
Sub UniqueFilter()
Dim arr() As String
Dim tmp As Variant
Dim lrow As Long
Dim sht As Worksheet
Dim index As Integer
Dim count As Integer
Set sht = ThisWorkbook.Worksheets("14Feb19")
sht.Activate
lrow = sht.Range("F" & Rows.count).End(xlUp).Row
'Copying and trasposing selected Range
tmp = Application.Transpose(sht.Range("F2:F" & lrow).Value)
'Cleaning from temp array all empty values
count = 1
For index = 1 To UBound(tmp, 1) - LBound(tmp, 1) + 1
ReDim Preserve arr(1 To count)
If tmp(index) <> "" Then
arr(count) = tmp(index)
count = count + 1
End If
Next
End Sub
(special thanks to #Nathan_Sav, who helped simplifying the code)

get data at runtime

I have to capture sheet row into a 2d array. I am using the following code
Code :
Sub multiarr()
Dim str As String 'String Which i am looking for
Dim result() As String 'Stores Splitted Substring
Dim r As Integer ' Row Counter of 2d array
Dim c As Integer ' Column Counter of 2d Array
Dim valarr() As String ' Initial Declaration of Array
'Row and Column Initialization
r = 0
c = 0
'Calculate Last Row and Last Column of Sheet
mylr = Cells(Rows.Count, 1).End(xlUp).Row
lcol = Cells(1, Columns.Count).End(xlToLeft).Column
'Initialize the Array according to Sheet Dimentions
ReDim valarr(mylr - 2, lcol - 1) 'Declare Array to be of size of Sheet
str = "M1" ' -> This i am interested in.Only these records will be populated
For y = 0 To UBound(valarr) 'iterate through rows of array
For x = 2 To mylr 'iterate through rows of sheet
result = Split(Cells(x, 1), "#") ' Split the Record
If result(0) = str Then 'Check for the Condition
'Array Filling Logic
For c = 1 To lcol
' C-1 because column index starts from 0
valarr(y, c - 1) = Cells(x, c)
Next c
End If
Next x
Next y
End Sub
But this code is incorrectly filling. What is the problem?
Please refer sample image of worksheet
Thanks in advance
Please see the bellow, hope it helps
Sub multiarr()
Dim str As String 'String Which i am looking for
Dim result() As String 'Stores Splitted Substring
Dim r As Integer ' Row Counter of 2d array
Dim c As Integer ' Column Counter of 2d Array
Dim valarr() As String ' Initial Declaration of Array
Dim mylr As Long, lcol As Long 'lastrow / lastcol
'I recommend declaring the workbook/worksheet and declaring the ranges accordingly
'Without doing so, any range refence bellow is explicit to the ActiveSheet
Dim arrValues As Variant
Dim cnt As Long, cnt2 As Long
'Row and Column Initialization
r = 1
c = 1
'Calculate Last Row and Last Column of Sheet
mylr = Cells(Rows.Count, 1).End(xlUp).row
lcol = Cells(1, Columns.Count).End(xlToLeft).column
arrValues = Range(Cells(r, c), Cells(mylr, lcol))
str = "M1" ' -> This i am interested in.Only these records will be populated
For y = LBound(arrValues) To UBound(arrValues) 'Iterate through values
If Left(arrValues(y, 1), 2) = str Then 'Check if the correct value exists
cnt = cnt + 1 'Count the number of occurences
End If
Next y
'Initialize the Array according to Results Dimentions
ReDim valarr(1 To cnt, 1 To lcol) 'Declare Array to be of size of Sheet
cnt2 = 1 'Start at one to match the array of the values, but... feel free to change
For y = LBound(arrValues) To UBound(arrValues) 'Iterate through array rows
If Left(arrValues(y, 1), 2) = str Then 'Check if the correct value exists
For z = LBound(arrValues, 2) To UBound(arrValues, 2) 'Iterate through array columns
valarr(cnt2, z) = arrValues(y, z) 'Add to the arr only correct values
Next z
cnt2 = cnt2 + 1 'If value find, we increase the counter
End If
Next y
End Sub
this answer only addresses the issue of getting a range into a 2-D array, not the processing of the elements.
This code is a pretty efficient method:
Sub multiarr()
Dim str As String 'String Which i am looking for
Dim result() As String 'Stores Splitted Substring
Dim r As Integer ' Row Counter of 2d array
Dim c As Integer ' Column Counter of 2d Array
Dim valarr()
valarr = Range("A1").CurrentRegion
MsgBox LBound(valarr, 1) & "-" & UBound(valarr, 1) & vbCrLf & LBound(valarr, 2) & "-" & UBound(valarr, 2)
End Sub
If you can't adapt the approach to your needs, ignore this answer.
Use auto filter (see comments in code):
Sub multiarr()
Dim rng As Range, rngData As Range, rngFilter As Range
'// Full range
Set rng = Range("A1").CurrentRegion
'// Range without a header
With rng
Set rngData = .Offset(1).Resize(.Rows.Count - 1)
End With
rng.AutoFilter Field:=1, Criteria1:="M1*"
'// Error handling in case if no rows will be filtered
On Error Resume Next
Set rngFilter = rngData.SpecialCells(xlCellTypeVisible)
If Err = 0 Then
'// Do something with your range.
'// Do not forget to use Areas,
'// since rngFilter can be non-contiguous:
'// Dim cell As Range, rngRow As Range, rngArea As Range
'// For Each rngArea in rngFilter.Areas
'// For Each cell in rngArea
'// 'Or For Each rngRow in rngArea.Rows
'// // Do something...
'// Next
'// Next
End If
On Error GoTo 0
End Sub

Select the rows (A:J) if column E contains more than 4 Cells with value. Next selective print the selected Rows. repeat process till last value

Select the rows (A:J) if column E contains more than 4 Cells with value. Next is to selective print the selected Rows. Then it need to repeat the process until the last filled cell. Have been searching for a macro to get tenter link description herehis done for weeks but sadly to no avail.
Hope you all can assist me on this.
After being select as such i believe i could just proceed with printing under "printing selection" setting
This is one of the printed result of the 2 selected row
Added code from comments
Sub EnquiryPrep()
Dim x As Integer
Dim rng As Range
With ActiveSheet
LR = .Range("a" & Rows.Count).End(xlUp).Row
For Each cell In .Range("e7:e" & LR)
If cell.Value <> "" Then
If rng Is Nothing Then
Set rng = cell.Offset(, -4).Resize(, 10)
Else
Set rng = Union(rng, cell.Offset(, -4).Resize(, 10))
End If
End If
Next cell
rng.Select
End With
End Sub
Put this into your ThisWorkbook Code
WHAT IS OUTPUT FROM DEBUG.PRINT statments?? Cut/Paste from Immediate
window below code
Try:
Sub PrintValidRows()
Const SHEET_NUM As Integer = 1 ' Which Sheet to Use
Const CHECK_COL As Integer = 5 ' Column E
Const START_ROW As Integer = 8
Const MIN_FILLED As Integer = 5 ' Min number required for print
Const LAST_COL As String = "H" ' Last column to print
Dim lastCellBlank As Boolean
Dim lngRow As Long
Dim lngLastRow As Long
Dim lngStartRow As Long
Dim intNumFilled As Integer
Dim strRange As String
Dim strPrintRange As String
Dim ws As Worksheet
Set ws = Sheets(SHEET_NUM)
ws.Activate
ws.Cells(1, 1).Select
intNumFilled = 0
' Get last row of data
lngLastRow = ActiveCell.SpecialCells(xlLastCell).Row
Debug.Print "Last Row: "; lngLastRow
lngStartRow = START_ROW
For lngRow = START_ROW To lngLastRow
If IsEmpty(Cells(lngRow, CHECK_COL)) Then
If intNumFilled >= MIN_FILLED Then
strRange = "A" & lngStartRow & ":" & LAST_COL & lngRow - 1
Debug.Print "Adding Range: " & strRange
If lngStartRow = START_ROW Then ' first range
strPrintRange = strRange
Else
strPrintRange = strPrintRange & "," & strRange
End If
End If
' Reset Filled Cell Counter
intNumFilled = 0
' Reset StartRow to next row
lngStartRow = lngRow + 1
Else
intNumFilled = intNumFilled + 1
End If
Next lngRow
' Check for last set of data
If intNumFilled >= MIN_FILLED Then
strRange = "A" & lngStartRow & ":" & LAST_COL & lngRow - 1
Debug.Print "Adding Range: " & strRange
If lngStartRow = START_ROW Then ' first range
strPrintRange = strRange
Else
strPrintRange = strPrintRange & "," & strRange
End If
End If
' Show Print Range in Immediate Window
Debug.Print "Print Range: " & strPrintRange
If strPrintRange <> "" Then
Range(strPrintRange).Select
End If
' You can record a macro to get it to printout exactly what how want
' REMOVE THIS TO TEST HIGHLIGHTING
'Application.Selection.PrintOut Copies:=1, Collate:=True, IgnorePrintAreas:=False
End Sub

Returning Address of Max Cell in Vba

Don't know much about Excel vba. How can I return the location of the cell in the range that is the maximum value (e.g., "MaxVal")?
Sub FillSched()
Dim LTrig As Long
Dim i As Integer
Dim MaxVal As Double
Dim WorkRange As Range
Worksheets("Inputs").Activate
LTrig = Range("Trigger").Value
Worksheets("Daily").Activate
For i = 0 To 5
If Range("AggInvStart").Offset(i, 0).Value > LTrig Then
Set WorkRange = Range("M" & i + 5 & ":" & "O" & i + 5)
MaxVal = WorksheetFunction.Max(WorkRange)
End If
Next i
End Sub
Thank you in advance.
In your code MaxValue is just a calculated value not a range.
You would need to get the position of the WorkRange that contains that MaxValue
Sub FillSched()
Dim LTrig As Long
Dim i As Integer
Dim MaxVal As Double
Dim WorkRange As Range
Dim col Long
Dim rw As Long
Worksheets("Inputs").Activate
LTrig = Range("Trigger").Value
Worksheets("Daily").Activate
For i = 0 To 5
If Range("AggInvStart").Offset(i, 0).Value > LTrig Then
Set WorkRange = Range("M" & i + 5 & ":" & "O" & i + 5)
MaxVal = WorksheetFunction.Max(WorkRange)
rw = WorkRange.Row
col = WorkRange.Column
End If
Next i
MsgBox "MaxValue is in Row: " & rw & ", Column: " & col
End Sub
Sub FillSched()
Dim rngSearch As Range, WorkRange As Range
Dim MaxVal as Double, lCol as Long, lRow as Long, sAddress as String
With WorksheetFunction
Set rngSearch = Range("AggInvStart").Resize(6, 1) ' Define search range
MaxVal = .Max(rngSearch.Value2) ' Get its max value
If MaxVal <= Range("Trigger").Value2 Then _ ' Use this if you do not want to
Exit Sub ' find MaxVal when is <= Range("Trigger")
lCol = Range("AggInvStart")(1,1).Column ' Get first column
lRow = Range("AggInvStart")(1,1).Row - 1 ' Get one row before first
Set WorkRange = Cells(lRow + .Match(MaxVal, rngSearch, 0), lCol) ' Get its location
sAddress = WorkRange.Address ' Get Cell Address
lRow = WorkRange.Row ' Get Row No
lCol = WorkRange.Column ' Get Column No
End With
End Sub
I hope this helps!
PS: I think that in your original code, you want LTrig to be Double. Also, it has to be initialized to a very small number (the code as it stands will fail if the max value is negative because the Dim statement initializes LTrig to zero.)

Resources