CountIf results in "0" - excel

I need help for one part of my complete code. The code below should count the rows that meet the defined conditions for every month. The problem is that I only get "0" as result. I cant figure out why? Is it maybe because of my date format? My source wb contains dates in a format like "01.01.2019".
The code should also only count rows from the first of any month. Meaning: 01.01., 01.02.,01.03. etc.
My master workbook(wb) has an user form with file explorer function, where I can choose my source workbook(wbSource) and apply the macro on it. As seen in the complete code below.
Any help would be appreciated.
Dim x As Long
For x = 1 To 12
ws.Cells(8, 1 + x) = _
Application.WorksheetFunction.CountIfs(wsSource.Columns(8), Format(x, "00") & "." & MonthName(x), _
wsSource.Columns(30), "<=" & 50)
ws.Cells(9, 1 + x) = _
Application.WorksheetFunction.CountIfs(wsSource.Columns(8), Format(x, "00") & "." & MonthName(x), _
wsSource.Columns(30), ">" & 50, wsSource.Columns(30), "<=" & 100)
ws.Cells(10, 1 + x) = _
Application.WorksheetFunction.CountIfs(wsSource.Columns(8), Format(x, "00") & "." & MonthName(x), _
wsSource.Columns(30), ">" & 100)
Next x
Complete Code
Private Sub CommandButton2_Click() ' update averages
Const YEAR = 2019
' open source workbook
Dim fname As String, wbSource As Workbook, wsSource As Worksheet
fname = Me.TextBox1.Text
If Len(fname) = 0 Then
MsgBox "No file selected", vbCritical, "Error"
Exit Sub
End If
Set wbSource = Workbooks.Open(fname, False, True) ' no link update, read only
Set wsSource = wbSource.Sheets("Sheet1") ' change to suit
Dim wb As Workbook, ws As Worksheet
Set wb = ThisWorkbook
Set ws = wb.Sheets("Table 2") '
' scan down source workbook calc average
Dim iRow As Long, lastRow As Long
Dim sMth As String, iMth As Long
Dim count(12) As Long, sum(12) As Long
lastRow = wsSource.Cells(Rows.count, 1).End(xlUp).Row
For iRow = 1 To lastRow
If IsDate(wsSource.Cells(iRow, 8)) _
And IsNumeric(wsSource.Cells(iRow, 30)) Then
iMth = Month(wsSource.Cells(iRow, 8)) ' col H
sum(iMth) = sum(iMth) + wsSource.Cells(iRow, 30) ' Col AD
count(iMth) = count(iMth) + 1 '
End If
Next
' counting the rows
Dim x As Long
For x = 1 To 12
ws.Cells(8, 1 + x) = _
Application.WorksheetFunction.CountIfs(wsSource.Columns(8), Format(x, "00") & "." & MonthName(x), _
wsSource.Columns(30), "<=" & 50)
ws.Cells(9, 1 + x) = _
Application.WorksheetFunction.CountIfs(wsSource.Columns(8), Format(x, "00") & "." & MonthName(x), _
wsSource.Columns(30), ">" & 50, wsSource.Columns(30), "<=" & 100)
ws.Cells(10, 1 + x) = _
Application.WorksheetFunction.CountIfs(wsSource.Columns(8), Format(x, "00") & "." & MonthName(x), _
wsSource.Columns(30), ">" & 100)
Next x
' close source worbook no save
wbSource.Close False
' update Table 2 with averages
With ws.Range("A3")
For iMth = 1 To 12
.Offset(0, iMth - 1) = MonthName(iMth) & " " & YEAR
If count(iMth) > 0 Then
.Offset(1, iMth - 1) = sum(iMth) / count(iMth)
.Offset(1, iMth - 1).NumberFormat = "0.0"
End If
Next
End With
Dim msg As String
msg = iRow - 1 & " rows scanned in " & TextBox1.Text
MsgBox msg, vbInformation, "Table 2 updated"
End Sub
Result-Sheet in Wb - as you see there are only zeroes displayed in the number of rows table
Example of wb.Source - Columns H(8) and AD(30) are the only relevant one for the ounting of the rows. In the example belows the last row should not be counted as the date does not match the condition ( its not the first of the month).

Yes, the problem is from the way you handle the dates. Your data has true dates. They aren't random numbers but a count of days since Jan 1, 1900. They uniquely identify every day for the past 120 years, and the system will continue to identify every day for the next 120 years, too. I believe the code below will do, more or less, what you intended to do.
Sub WriteCountIf()
' 025
' The YearCell must contain a year number like 2019.
Const YearCell As String = "A1" 'change to suit
Dim DateRng As Range ' the range to search for dates in
Dim DaysRng As Range ' the range to search for days in
Dim StartDate As Date ' first day to include in count
Dim EndDate As Date ' last day to include in count
Dim C As Long ' the column to write to (= month)
With ActiveSheet ' better to define the sheet by name
' make sure your result range doesn't overlap the search ranges
Set DateRng = .Range(.Cells(11, 8), .Cells(.Rows.Count, 8).End(xlUp))
Set DaysRng = .Range(.Cells(11, 30), .Cells(.Rows.Count, 30).End(xlUp))
For C = 1 To 12
StartDate = DateSerial(.Range(YearCell).Value, C, 1)
EndDate = DateSerial(Year(StartDate), C + 1, 0)
.Cells(8, 1 + C).Value = Application.WorksheetFunction. _
CountIfs(DateRng, ">=" & StartDate, _
DateRng, "<=" & EndDate, _
DaysRng, "<=" & 50)
.Cells(9, 1 + C).Value = Application.WorksheetFunction. _
CountIfs(DateRng, ">=" & StartDate, _
DateRng, "<=" & EndDate, _
DaysRng, ">" & 50, _
DaysRng, "<=" & 100)
.Cells(10, 1 + C).Value = Application.WorksheetFunction. _
CountIfs(DateRng, ">=" & StartDate, _
DateRng, "<=" & EndDate, _
DaysRng, ">" & 100)
Next C
End With
End Sub
It seems that your data only show day and month. That's a matter of display (cell formatting). The underlying dates include the year. Therefore you need a year when you search them. I have added a year in A1. You can move that cell to anywhere or hard-program a year into the code.
Your definition of search ranges, just by columns, is insufficient, and it overlaps with the cells into which you want to write the counts. I have assumed a start row and my code finds the end of each column. Please try it out.

In the code below the counting procedure is converted into a function that returns its result as an array. This is called by the main procedure and written to the worksheet. I couldn't test the code en total but the part described above works as advertised.
Private Sub CommandButton2_Click() ' update averages
' "Year" is the name of a VBA function.
' To use the same word here as a variable name is asking for trouble.
Const Year As Integer = 2019
Dim wbSource As Workbook, wsSource As Worksheet
Dim Wb As Workbook, Ws As Worksheet
Dim fname As String
fname = Me.TextBox1.Text
If Len(fname) = 0 Then
MsgBox "No file selected", vbCritical, "Error"
Exit Sub
End If
Set Wb = ThisWorkbook
Set Ws = Wb.Sheets("Table 2") '
' open source workbook
Set wbSource = Workbooks.Open(fname, False, True) ' no link update, read only
Set wsSource = wbSource.Sheets("Sheet1") ' change to suit
Dim iRow As Long, lastRow As Long
Dim sMth As String, iMth As Long
Dim count(12) As Long, sum(12) As Long ' Confusing: Count is a VBA method
' scan down source workbook calc average
' observe how .Rows.count is written with lower case c
' because of your declaration of "count" in that way
lastRow = wsSource.Cells(wsSource.Rows.count, 1).End(xlUp).Row
For iRow = 1 To lastRow
If IsDate(wsSource.Cells(iRow, 8)) And _
IsNumeric(wsSource.Cells(iRow, 30)) Then
iMth = Month(wsSource.Cells(iRow, 8)) ' col H
sum(iMth) = sum(iMth) + wsSource.Cells(iRow, 30) ' Col AD
count(iMth) = count(iMth) + 1
End If
Next
' counting the rows
Ws.Cells(8, 2).Resize(3, 12).Value = RowsCount(Year, wsSource)
' close source worbook no save
wbSource.Close False
' update Table 2 with averages
With Ws.Range("A3")
For iMth = 1 To 12
.Offset(0, iMth - 1) = MonthName(iMth) & " " & Year
If count(iMth) > 0 Then
.Offset(1, iMth - 1) = sum(iMth) / count(iMth)
.Offset(1, iMth - 1).NumberFormat = "0.0"
End If
Next
End With
Dim msg As String
msg = iRow - 1 & " rows scanned in " & TextBox1.Text
MsgBox msg, vbInformation, "Table 2 updated"
End Sub
Private Function RowsCount(ByVal RefYear As Integer, _
Ws As Worksheet) As Variant
' 025
Dim Fun As Variant ' Function return value
Dim DateRng As Range ' the range to search for dates in
Dim DaysRng As Range ' the range to search for days in
Dim StartDate As Date ' first day to include in count
Dim EndDate As Date ' last day to include in count
Dim C As Long ' the column to write to (= month)
With Ws
' make sure your result range doesn't overlap the search ranges
Set DateRng = .Range(.Cells(11, 8), .Cells(.Rows.count, 8).End(xlUp))
Set DaysRng = .Range(.Cells(11, 30), .Cells(.Rows.count, 30).End(xlUp))
End With
ReDim Fun(1 To 3, 1 To 12)
For C = 1 To 12
StartDate = DateSerial(RefYear, C, 1)
EndDate = DateSerial(Year(StartDate), C + 1, 0)
Fun(1, C) = Application.WorksheetFunction. _
CountIfs(DateRng, ">=" & StartDate, _
DateRng, "<=" & EndDate, _
DaysRng, "<=" & 50)
Fun(2, C) = Application.WorksheetFunction. _
CountIfs(DateRng, ">=" & StartDate, _
DateRng, "<=" & EndDate, _
DaysRng, ">" & 50, _
DaysRng, "<=" & 100)
Fun(3, C) = Application.WorksheetFunction. _
CountIfs(DateRng, ">=" & StartDate, _
DateRng, "<=" & EndDate, _
DaysRng, ">" & 100)
Next C
RowsCount = Fun
End Function
Capitalization is very important in VBA. As the example of your use of the Count word demonstrates VBA will try to adapt to your style. Therefore, if you have no system you will destroy VBA's. The theory I follow is to use caps and smalls on all variable names. Then I type using only lower case. VBA will correct my typing and this serves as a spell checker, helping to avoid typos. There are a few exceptions to this but that's the general rule I follow.
It's the same with the choice of variable names. Generally I avoid using words VBA uses. That prevents confusion for me (never mind VBA). But if I do use VBA's words I follow the existing capitalization.

Related

Range Macro Not Functioning

.
The following macro (CollectProjectItems) functions as designed. Applying the same logic, with a change in the Range, in the macro (CollectContractorItems) does not function as desired.
I am presuming the error is something I've overlooked and of course ... for the life of me ... I cannot identify my error.
Need a fresh set of eyes.
Thank you ahead of time.
Sub UpdateCharts()
CollectProjectItems
CollectContractorItems
End Sub
Sub CollectProjectItems()
On Error Resume Next
MyDate = Format(Date, "mmm") & "-" & Right(Year(Date), 2)
For Each cl In Range("A3", Range("A" & Rows.Count).End(xlUp))
wproj = Application.Match(cl.Value, Columns(10), 0)
If IsNumeric(wproj) Then
MyMonth = Application.Match(MyDate, Rows(wproj + 1), 0)
Cells(wproj + 2, MyMonth) = cl.Offset(, 1)
Cells(wproj + 3, MyMonth) = cl.Offset(, 2)
End If
Next
End Sub
Sub CollectContractorItems()
On Error Resume Next
MyDate = Format(Date, "mmm") & "-" & Right(Year(Date), 2)
For Each cl In Range("E3", Range("E" & Rows.Count).End(xlUp))
wproj = Application.Match(cl.Value, Columns(25), 0)
If IsNumeric(wproj) Then
MyMonth = Application.Match(MyDate, Rows(wproj + 1), 0)
Cells(wproj + 2, MyMonth) = cl.Offset(, 1)
Cells(wproj + 3, MyMonth) = cl.Offset(, 2)
End If
Next
End Sub
The second macro does not complete the required edits in Col AG. It duplicates the same edit as the first macro for Col R.
I don't understand how to change the second macro so it affects edits in Cols Z:AK .
???
Download example workbook : Macro Error
Like this:
Sub CollectContractorItems()
Const COL_CONTRACTORS As Long = 25
Dim MyDate As String, cl As Range, ws As Worksheet, wproj, MyMonth
Dim rngDates As Range, dtCol As Long
Set ws = ActiveSheet 'or some specific sheet
MyDate = Format(Date, "mmm") & "-" & Right(Year(Date), 2)
For Each cl In ws.Range("E3:E" & ws.Cells(ws.Rows.Count, "E").End(xlUp).Row).Cells
wproj = Application.Match(cl.Value, ws.Columns(COL_CONTRACTORS), 0)
If Not IsError(wproj) Then
'get the range with dates
Set rngDates = ws.Cells(wproj, COL_CONTRACTORS).Offset(1, 1).Resize(1, 12)
MyMonth = Application.Match(MyDate, rngDates, 0) 'search only in the specific range
If Not IsError(MyMonth) Then
dtCol = rngDates.Cells(MyMonth).Column 'get the column number
ws.Cells(wproj + 2, dtCol) = cl.Offset(, 1)
ws.Cells(wproj + 3, dtCol) = cl.Offset(, 2)
End If
End If
Next
End Sub

Why is this CountIf function returning "0"?

The actual situation
I have a master workbook (Wb) with a user form - file explorer (Screenshot 1) and a Sheet2 for the results (Screenshot 2)
I have a source workbook, which has a huge amount of data(14k rows) and is quite frankly a mess.. For my calculation only 2 columns of the source workbook are relevant: column 8 (True Dates) and colum 30 (numerical values).
Important note regarding column 8 : only the rows with the first of every month is relevant for me. Meaning: 01.01., 01.02.,01.03.,01.04. etc. Every other row should be ignored.
I use the Wb to search for the source workbook(wb.Source) to apply the following calculations:
Task 1: calculating the avarage value if certain conditions are met
Task 2: counting rows if the following conditions are met:
If a row in ws.Source has date in Column 8 = "01.01" and numerical value in Column 30 is < 50 then count and enter the result in cell B8 in Sheet 2 of Wb
If a row in ws.Source has date in Column 8= "01.01" and Column 30 is > 50 and < 100 then count and enter the result in cell B9 in Sheet 2 of Wb
If a row in ws.Source has date in Column 8= "01.01" and Column 30 is > 100 then count and enter the result in cell B10 in Sheet 2 of Wb
This should be applied for every month.
The results are then added to my Wb Sheet2.
My problems/questions
1. Regarding Task 2: My CountIf function results in "0" for every month and I don't know why? See attached screenshots from my result sheet of Wb(Screenshot 2) and an example of rows of my wb.Source.(Screenshot 3)
2. Regarding Task 1: the calculation works so far, but how can I make sure that only the rows with the first of every month are considered and everything else ignored?
3. Is it possible to restrict the scope of the calculation in the sense that only the past 12 months are calculated, starting from a certain date (counting back). Meaning: To set a Date (Month/Year) in my user form and consider only the rows of the past 12 months for the calculation? If no date is set then "today" should be the starting point. I think the Const = YEAR has to be removed from the code, as there is definitely the possibility that the wb.Source contains rows from different years.
I would appreciate any help!
Private Sub CommandButton1_Click() ' select file
Dim fname As Variant
With Me
fname = Application.GetOpenFilename("Excel File(s) (*.xls*),*.xls*", , "Select FIle", , False)
If fname <> "False" Then .TextBox1.Text = fname
End With
End Sub
'calculate avarage
Private Sub CommandButton2_Click() ' update averages
Const YEAR = 2019
' open source workbook
Dim fname As String, wbSource As Workbook, wsSource As Worksheet
fname = Me.TextBox1.Text
If Len(fname) = 0 Then
MsgBox "No file selected", vbCritical, "Error"
Exit Sub
End If
Set wbSource = Workbooks.Open(fname, False, True) ' no link update, read only
Set wsSource = wbSource.Sheets("Sheet1") ' change to suit
Dim wb As Workbook, ws As Worksheet
Set wb = ThisWorkbook
Set ws = wb.Sheets("Sheet2") '
' scan down source workbook calc average
Dim iRow As Long, lastRow As Long
Dim sMth As String, iMth As Long
Dim count(12) As Long, sum(12) As Long
lastRow = wsSource.Cells(Rows.count, 1).End(xlUp).Row
For iRow = 1 To lastRow
If IsDate(wsSource.Cells(iRow, 8)) _
And IsNumeric(wsSource.Cells(iRow, 30)) Then
iMth = Month(wsSource.Cells(iRow, 8)) ' col H
sum(iMth) = sum(iMth) + wsSource.Cells(iRow, 30) ' Col AD
count(iMth) = count(iMth) + 1
End If
Next
'calculate number of rows (CountIf)
Dim x As Long
Dim m As Variant
For x = 1 To 12
m = "01." & Format(x, "00")
ws.Cells(8, 1 + x) = _
Application.WorksheetFunction.CountIfs(wsSource.Columns(8), m & "*", _
wsSource.Columns(30), "<=" & 50)
ws.Cells(9, 1 + x) = _
Application.WorksheetFunction.CountIfs(wsSource.Columns(8), m & "*", _
wsSource.Columns(30), ">" & 50, wsSource.Columns(30), "<=" & 100)
ws.Cells(10, 1 + x) = _
Application.WorksheetFunction.CountIfs(wsSource.Columns(8), m & "*", _
wsSource.Columns(30), ">" & 100)
Next x
' close source worbook no save
wbSource.Close False
' update Sheet 2 with averages
With ws.Range("A3")
For iMth = 1 To 12
.Offset(0, iMth - 1) = MonthName(iMth) & " " & YEAR
If count(iMth) > 0 Then
.Offset(1, iMth - 1) = sum(iMth) / count(iMth)
.Offset(1, iMth - 1).NumberFormat = "0.0"
End If
Next
End With
Dim msg As String
msg = iRow - 1 & " rows scanned in " & TextBox1.Text
MsgBox msg, vbInformation, "Sheet 2 updated"
End Sub

Format Date VBA

i want to implement this recorded macro into a macro for my code, i succesfully transformed "E" row into general, and i want to change that date into Short Date format DD/MM/YYYY the macro i recorded is this one below:
Sub Macro2()
'
' Macro2 Macro
Range("L4").Select
ActiveCell.FormulaR1C1 = "=DATEVALUE(MID(RC[-7],1,10))"
Range("L4").Select
Selection.AutoFill Destination:=Range("L4:L4500"), Type:=xlFillDefault
Range("L4:L4500").Select
Selection.NumberFormat = "m/d/yyyy"
End Sub
I tried it by making the function into the L Column, if it is possible i would like to implement it in one column so all values change and then paste them into the E column
The whole E column is like this:
01-10-2019 52:59:76
02-10-2019 52:59:76
02-10-2019 52:59:76
05-10-2019 52:59:76
And i want them to change into
01/10/2019
02/10/2019
02/10/2019
05/10/2019
This the code i used to transform the whole E column data to the format of dd-mm-yyyy hh:mm:ss to correct the error of some data not changing into the correct format
With ActiveSheet.UsedRange.Columns("E").Cells
Columns("E").NumberFormat = "0"
Columns("E").NumberFormat = "General"
End With
If in '01-10-2019 52:59:76' first two digits means day, try please the next code:
Sub testDateFormat()
Dim lastRow As Long, sh As Worksheet, x As String, i As Long
Set sh = ActiveSheet 'use here your sheet, if not active one
lastRow = sh.Range("E" & sh.Rows.count).End(xlUp).Row
sh.Range("E1:E" & lastRow).NumberFormat = "dd/mm/yyyy"
For i = 2 To lastRow
If sh.Range("E" & i).Value <> Empty Then
If chkFind(CStr(sh.Range("E" & i).Value)) = True Then
x = CStr(sh.Range("E" & i).Value)
sh.Range("E" & i).Value = Format(DateSerial(Split(left(x, WorksheetFunction.Find(" ", x) - 1), "-")(2), _
Split(left(x, WorksheetFunction.Find(" ", x) - 1), "-")(1), _
Split(left(x, WorksheetFunction.Find(" ", x) - 1), "-")(0)), "dd/mm/yyyy")
Else
Debug.Print "Unusual string on the row " & i
End If
End If
Next i
End Sub
Private Function chkFind(strVal As String) As Boolean
On Error Resume Next
If WorksheetFunction.Find(" ", strVal) = 11 Then
chkFind = True
If Err.Number <> 0 Then
Err.Clear
On Error GoTo 0
chkFind = False
End If
Else
chkFind = False
End If
On Error GoTo 0
End Function
If first digits represents month, then the last two array (split) elements must be vice versa:
Split(left(x, WorksheetFunction.Find(" ", x) - 1), "-")(0), _
Split(left(x, WorksheetFunction.Find(" ", x) - 1), "-")(0))
instead of
Split(left(x, WorksheetFunction.Find(" ", x) - 1), "-")(1), _
Split(left(x, WorksheetFunction.Find(" ", x) - 1), "-")(1))

Excel VBA SUMIF Super slow code

I have SUMIF running really really slow. My data has 14,800 Rows and 39 Columns.
I do the following:
Application.ScreenUpdating = False
Application.DisplayStatusBar = False
Application.Calculation = xlCalculationManual
Application.EnableEvents = False
ActiveSheet.DisplayPageBreaks = False
EDITED TO ADD more potentially relevant code that may be interacting with the SUMIF command
It may be relevant to the speed issue so I'll mention it. I get the user to open a file from wherever they may have stored the report. The file then stays open. Maybe that is a problem. I don't know if it should be some other way.. like I close it but keep the address in mind or something??
FilterType = "Text Files (*.txt),*.txt," & "Comma Separated Files (*.csv),*.csv," & "ASCII Files (*.asc),*.asc," & "All Files (*.*),*.*"
FilterIndex = 4
Title = "File to be Selected"
File_path = Application.GetOpenFilename(FileFilter:=FilterType, FilterIndex:=FilterIndex, Title:=Title)
If File_path = "" Then
MsgBox "No file was selected."
Exit Sub
End If
Set wbSource = Workbooks.Open(File_path)
Original_Name = ActiveWorkbook.Name
If ActiveSheet.AutoFilterMode Then
ActiveSheet.AutoFilterMode = False
End If
Set wb1 = ActiveWorkbook
Set ws1 = wb1.Worksheets("Sheet1")
With ws1
FinalColumn = .Cells(1, .Columns.Count).End(xlToLeft).Column
FinalRow = .Range("B" & .Rows.Count).End(xlUp).Row
For j = 1 To FinalColumn
If .Cells(1, j).Value = "Effec.Date" Then
Effective_Date_Column = j
ElseIf .Cells(1, j).Value = "FolderId" Then
FolderId_column = j
ElseIf .Cells(1, j).Value = "FolderNotional" Then
FolderNotional_column = j
End If
Next j
'range_Total_Folder_Fixed = .Cells(2, Total_Folder_Column).Address & ":" & .Cells(FinalRow, Total_Folder_Column).Address
range_FolderId_Fixed = .Cells(2, FolderId_column).Address & ":" & .Cells(FinalRow, FolderId_column).Address
range_FolderId_Cell = .Cells(2, FolderId_column).Address(RowAbsolute:=False, ColumnAbsolute:=False)
range_FolderNotional_Fixed = .Cells(2, FolderNotional_column).Address & ":" & .Cells(FinalRow, FolderNotional_column).Address
Everything runs in 8-10 seconds until we come to the lie below. Now the total time jumps to a 150 seconds.
.Range(range_Total_Folder_Fixed).Formula = "=SUMIF(" & range_FolderId_Fixed & "," & range_FolderId_Cell & "," & range_FolderNotional_Fixed & ")"
Am I doing something wrong? Is there a better (more efficient) way to write a general formula?
EDIT: Code generated Raw Formula
Some of the excel worksheet functions in my code:
.Range(range_Isnumber).Formula = "=(RIGHT(" & range_TradeId_cell & ",2)<> ""IB"")*1"
.Range(range_Is_IB).Formula = "=(RIGHT(" & range_TradeId_cell & ",2)= ""IB"")*1"
.Range(range_Exceptions).Formula = "=(SUMIF(" & range_FolderId_Fixed & "," & range_FolderId_Cell & "," & range_Isnumber_fixed & ")= COUNTIF(" & range_FolderId_Fixed & "," & range_FolderId_Cell & "))*1+(SUMIF(" & range_FolderId_Fixed & "," & range_FolderId_Cell & "," & range_Is_IB_fixed & ")= COUNTIF(" & range_FolderId_Fixed & "," & range_FolderId_Cell & "))*1 "
.Range("C13").FormulaR1C1 = "=SUM(IF(FREQUENCY(MATCH([SCL_FL_2013_11_30.xlsx]Rest!R2C2:R2321C2,[SCL_FL_2013_11_30.xlsx]Rest!R2C2:R2321C2,0),MATCH([SCL_FL_2013_11_30.xlsx]Rest!R2C2:R2321C2,[SCL_FL_2013_11_30.xlsx]Rest!R2C2:R2321C2,0))>0,1))"
So Stuff like
Range("I2")=SUMIF($H$2:$H$5,H2,$G$2:$G$5)
Where the data could be like
RowG RowH RowI
Alice 1 4
Alice 3 4
Bob 9 17
Bob 8 17
Dan 2 2
EDIT2 : Implementing Sam's solution, I am getting errors:
Set range_FolderId_Fixed = .Range(.Cells(2, FolderId_column), .Cells(FinalRow, FolderId_column))
Set range_FolderId_Cell = .Range(.Cells(2, FolderId_column),.Cells(FinalRow, FolderId_column))
Set range_FolderNotional_Fixed = .Range(.Cells(2, FolderNotional_column), .Cells(FinalRow, FolderNotional_column))
Set range_Total_Folder_Fixed = .Range(.Cells(2, Total_Folder_Column), .Cells(FinalRow, Total_Folder_Column))
.Range(range_Total_Folder_Fixed).Value = SumIf_func(range_FolderId_Fixed, range_FolderId_Cell, range_FolderNotional_Fixed)
I am getting a type application defined or object defined error in the line below.
.Range(range_Total_Folder_Fixed).Value = SumIf_func(range_FolderId_Fixed, range_FolderId_Cell, range_FolderNotional_Fixed)
I have no idea what to do next.
Ok this is what I came up with
Public Function SumIf_func(rng As Range, _
criteria As Range, _
sumRange As Range) As Variant()
Dim rngArr() As Variant
Dim sumArr() As Variant
Dim criteriaArr() As Variant
Dim returnArr() As Variant
Dim temp As Double
rngArr = rng.Value2
sumArr = sumRange.Value2
criteriaArr = criteria.Value2
If UBound(sumArr) <> UBound(rngArr) Then _
Err.Raise 12345, "SumIf_func", "Sum range and check range should be the same size"
If UBound(sumArr, 2) <> 1 Or UBound(rngArr, 2) <> 1 Then _
Err.Raise 12346, "SumIf_func", "Sum range and check range should be a single column"
ReDim returnArr(1 To UBound(criteriaArr), 1 To 1)
For c = LBound(criteriaArr) To UBound(criteriaArr)
returnArr(c, 1) = Application.WorksheetFunction.SumIf(rng, criteriaArr(c, 1), sumRange)
Next c
SumIf_func = returnArr
End Function
This function takes in three ranges:
The range to check
The range where the criteria are
The range where the values to sum are
The range to check and the sum range should both be the same length and only be 1 column across.
The array that is returned will be the same size as the criteria array..
Here is an example of usage:
Public Sub test_SumIf()
Dim ws As Worksheet
Set ws = Sheet1
Dim rng As Range, sumRng As Range, criteria As Range
Set rng = ws.Range("A1:A100")
Set sumRng = ws.Range("B1:B100")
Set criteria = ws.Range("C1:C10")
ws.Range("D1:D10").Value = SumIf_func(rng, criteria, sumRng)
End Sub

Copying Rows in For Loop and Pasting to new Sheet

I am having problems copy/pasting rows based on criteria.
Dim lastrow1 As Long
Dim lastcolumn1 As Long
Dim Distance As Long
Distance = 14
Set sh = ThisWorkbook.Sheets("Sample Address Database")
Set sh2 = ThisWorkbook.Sheets("Workspace")
lastrow1 = sh.Cells(Rows.Count, "A").End(xlUp).row
lastcolumn1 = sh.Cells(1, Columns.Count).End(xlToLeft).Column
Dim L As Long
For L = 2 To lastrow1
If _
sh.Cells(L, Distance).Value <= CDbl(cboRadius.Value) Then
sh.Range("A" & L & ":" & lastcolumn1 & L).Copy _
Destination:=sh2.Range("A" & L)
End If
Next
cboRadius.Value is a number from a userform (there is no problem with that line.)
Whenever I try to run this code, I get a "Run-time error '1004': Method 'Range' of object '_Worksheet' failed, with the yellow arrow pointing to the destination line. What is the problem?
EDIT:
Ed Heywood-Lonsdale suggested I change
sh.Range("A" & L & ":" & lastcolumn1 & L).Copy _
To
sh.Range("A" & L & ":A" & lastcolumn1 & L).Copy _
Now only column A, or if I change it to B, C, D, etc. is being copied. I think the problem is that it may not be registering that lastcolumn1 and L are column/row numbers and is instead making them one value, thus causing a range malfunction.
Try adding "A" when defining the range to be copied:
sh.Range("A" & L & ":" & lastcolumn1 & L)
becomes
sh.Range("A" & L & ":A" & lastcolumn1 & L)
I would just filter your data in place using the built in Excel Filters, then copy the results over instead of trying to Loop over every row.
BUT If you want to loop the rows anyways:
In order to use the Range function you need to use column letters not column numbers.
You have 2 options here. Use
Chr(lastcolumn1 + 64)
instead of lastcolumn1. The flaw is This will only work for columns up to columns Z, and it won't work for double letter columns without an if statement and more code. Like the following should work for up to Column ZZZ
If lastcolumn1> 52 Then
strColumnLetter = Chr(Int((lastcolumn1- 1) / 52) + 64) & Chr(Int((lastcolumn1- 27) / 26) + 64) & Chr(Int((lastcolumn1- 27) Mod 26) + 65)
ElseIf lastcolumn1> 26 Then
strColumnLetter = Chr(Int((lastcolumn1- 1) / 26) + 64) & Chr(Int((lastcolumn1- 1) Mod 26) + 65)
Else
strColumnLetter = Chr(lastcolumn1+ 64)
End If
But you could also use
strColumnLetter = Split(Cells(1, lastcolumn1).EntireColumn.Address(False, False), ":")(0)
OR
strColumnLetter = Left(Replace(Cells(1, lastcolumn1).Address(1, 0), "$", ""), InStr(1, Replace(Cells(1, lastcolumn1).Address(1, 0), "$", ""), 1) - 1)
OR
strColumnLetter = Left(Cells(1, lastcolumn1).Address(1, 0), InStr(1, Cells(1, lastcolumn1).Address(1, 0), "$") - 1)
as that will work for as many columns as Excel will hold.
Your last option if you don't want to convert the number to the column Letter would be to get a range of Cells, as the Cells function CAN accept column numbers for arguments.
sh.Range(cells(L,1), cells(L,lastcolumn1))
Again I would suggest just using the standard built in filter function to filter out the data you don't want then just copy whats left though. This was just to add more options.
If you supply some sample info I could write you a sub that will do the filter copy paste for you but I don't know how your data is set up.
here is an example that should work based on your Original Question:
Sub FilterAndCopy()
Application.ScreenUpdating = False
Application.EnableEvents = False
Application.Calculation = xlCalculationManual
Dim sh As Worksheet, sh2 As Worksheet
Dim lastrow1 As Long
Dim lastcolumn1 As Long
Dim Distance As Long
Distance = 14
Set sh = ThisWorkbook.Sheets("Sample Address Database")
Set sh2 = ThisWorkbook.Sheets("Workspace")
lastrow1 = sh.Cells(Rows.Count, "A").End(xlUp).Row
lastcolumn1 = sh.Cells(1, Columns.Count).End(xlToLeft).Column
With sh
.Range(.Cells(1, 1), .Cells(lastrow1, lastcolumn1)).AutoFilter , _
field:=Distance, _
Criteria1:="<=" & CDbl(151), _
Operator:=xlAnd
.Range(.Cells(2, 1), .Cells(lastrow1, lastcolumn1)).Copy _
sh2.Range("A2")
End With
Application.ScreenUpdating = True
Application.EnableEvents = True
Application.Calculation = xlCalculationAutomatic
End Sub

Resources