Excel VBA - Date Filter AND blanks - vba

I am using VBA to make date filters. This filter will take the dates I specify in sheet 1 to and filter the column I have selected at the moment. Ideally, I would like to have all the values with the date in that range PLUS all blanks (where no date has been defined).
Set rep= ActiveWorkbook.Sheets("sheet2")
Set sh1 = ActiveWorkbook.Sheets("Sheet1")
Dim anf As String
Dim ende As String
Dim count As Integer
anf = ">=" + sh1.Range("J2")
ende = "<=" + sh1.Range("J3")
rep.Select
count = Range(Selection, Selection.End(xlToLeft)).Columns.count
rep.Range("$A$4:$GD$11668").AutoFilter Field:=count, Criteria1:=anf, Operator:=xlAnd, Criteria2:=ende, Operator _
:=xlFilterValues
This code functions beautifully. However, it only filters the date. My attempts to add blanks as well (in addition to it) have failed.
For example, adding a 3rd criterion for blanks:
rep.Range("$A$4:$GD$11668").AutoFilter Field:=count, Criteria1:=anf, Operator:=xlAnd, Criteria2:=ende, Operator:=xlAnd, Criteria3:="=", Operator _
:=xlFilterValues
I get an application defined or object defined error.
Any ideas? Thanks!

You can't do more than 3 criteria at the same time that way -if you notice in the user interface you can only get 2 "and" or whatever criteria at the same time.
However, you can do an array and set that as criteria. Refer to this example
Try the following example
Sub Sample()
Dim SampleRange As Range
Dim ArrayDates(2) As Date
ArrayDates(0) = "1-1-2017"
ArrayDates(1) = "1-2-2017"
ArrayDates(2) = Now()
'1st approach, add the array directly in the criteria
Set SampleRange = Range("A1:A31")
SampleRange.AutoFilter Field:=1, Operator:= _
xlFilterValues, Criteria1:=Array(2, "1/1/2017", 2, "1/2/2017", 2, "1/3/2017")
'2nd approach: define an array and just start to call it as needed
SampleRange.AutoFilter Field:=1, Operator:= _
xlFilterValues, Criteria1:=Array(ArrayDates(1))
End Sub

First off, your error was caused by trying to give Autofilter 3 criteria - it can only handle 2, and it had no idea what to do with the extra arguments you passed it.
Now, that's not the only problem with what you're trying. Notice that if you attempt to filter between two dates manually through the UI, you can do it, but you can't also add blanks. It's kind of a one-thing-or-the-other situation. I also tried Brian's solution at first, but Excel just doesn't work that way. At least, I couldn't coax it into working that way.
This leaves us with two very ugly possible solutions. Either you modify the table you're filtering (add an extra column with a formula or something along those lines) to give Autofilter something else to filter on, or manually set all the valid criteria. Here's how you do the latter:
Private Sub setFilter()
Set rep = ActiveWorkbook.Sheets("sheet2")
Set sh1 = ActiveWorkbook.Sheets("Sheet1")
Dim anf As String
Dim ende As String
Dim count As Integer
anf = sh1.Range("J2")
ende = sh1.Range("J3")
rep.Select
count = Range(Selection, Selection.End(xlToLeft)).Columns.count
rep.Range("$A$4:$GD$11668").AutoFilter Field:=count, Criteria1:=GetValidDates(CDate(anf), CDate(ende)), Operator:=xlFilterValues
End Sub
Private Function GetValidDates(ByVal startD As Date, ByVal endD As Date) As String()
Dim numDays As Integer: numDays = endD - startD + 1
Dim dateArray() As String
ReDim dateArray(numDays) ' (numDays + 1) entries
Dim i As Integer
For i = 0 To numDays - 1
dateArray(i) = CStr(startD + i)
Next
dateArray(i) = ""
GetValidDates = dateArray
End Function
This builds an array of all dates between and including the start and end (as well as an extra empty "" entry to allow blanks). It's ugly and I'm not sure how well it'll work for really big date ranges (might be slow), but at least Excel recognizes and accepts it.

I couldn't follow what you were trying to do, but maybe something like this:
Sub test1()
Dim rep As Worksheet
Dim sh1 As Worksheet
Set rep = ActiveWorkbook.Sheets("sheet4")
Set sh1 = ActiveWorkbook.Sheets("Sheet3")
Dim i As Long
Dim count As Integer
Dim arr As Variant
arr = Array("=" & """""", ">=" & sh1.Range("J2"), "<=" & sh1.Range("J3"))
rep.Select
count = Range(Selection, Selection.End(xlToLeft)).Columns.count
For i = 0 To UBound(arr)
rep.Range("$A$4:$GD$11668").AutoFilter Field:=count, Criteria1:=arr(i)
Next i
End Sub

Related

VBA - search for a date in a range of cells, returning cell address

I am trying to write a function to search for a specific date, entered as a parameter, in a range of cells in an adjacent worksheet. On finding the date, the function should return a string, "found: " and the cell reference.
All seems to be working well enough, but the function returns 'nothing' even when there is a (deliberately entered) date, in date format, both in the cell range and the cell referred to when the function is called.
Have I missed something critical when calling find when using a Date?
A note, the function looks in the same row that it is called from, in the other sheet. This may help explain how i'm setting rng
Public Function d_scan(targ As Date) As String
Dim ws As Worksheet
Dim targetSheet As Worksheet
Dim ret As String
Dim rng As String
Dim scanner As Date
Dim found As Range
Set targetSheet = ThisWorkbook.Worksheets("2018")
Set ws = Application.Caller.Worksheet
Let intRow = Application.Caller.Row
Let intCol = Application.Caller.Column
Let rng = "F" & intRow & ":" & "X" & intRow
Set found = targetSheet.Range(rng).Find(What:=targ, LookAt:=xlWhole)
If found Is Nothing Then
Let ret = "nothing"
Else
Let ret = "found: " & found
End If
d_scan = ret
End Function
date issues are quite subtle and their solution may depend on the actual scenario (what variable type is used, what data format is used in the sheet,...)
for a start, you may want:
specify all relevant Find() method parameters, since undefined ones will be implicitly assumed as per its last usage (even from Excel UI!)
convert Date to String via the CStr() function
so, you may want to try this code:
Option Explicit
Public Function d_scan(targ As Date) As String
Dim rng As String
Dim found As Range
Dim intRow As Long
intRow = Application.Caller.Row
rng = "F" & intRow & ":" & "X" & intRow
Set found = ThisWorkbook.Worksheets("2018").Range(rng).Find(What:=CStr(targ), LookAt:=xlWhole, LookIn:=xlValues) ' specify 'LookIn' parameter, too
If found Is Nothing Then
d_scan = "nothing"
Else
d_scan = "found: " & found
End If
End Function
I think you are comparing day/hour/minute/second with day/hour/minute/second and getting no matches (everything's too specific). I used this to massage targ into "today" at 12:00 AM, but you would need to do something to massage the data on the sheet like this as well for the range.find to work.
targ = Application.WorksheetFunction.Floor(targ, 1)
I suggest using a method other than range.find... Looping perhaps, looking for a difference between targ and the cell that's less than 1?

Vba: Automate the selection of a cell using match function on the just upper row composed of dates

I'm a beginner with Vba
I want to automate the selection of a cell depending on the current date. For this purpose, I have the values I want to select on row 8 and the dates in chronological order on row 7.
I tried the following code:
Sub selectvalues()
Dim rtc As Double
Dim ystdy As Date
Dim tdy As Date
szToday = Format(Date, "YYYYMMDD")
tdy = szToday
ystdy = WorksheetFunction.WorkDay(tdy, -1)
rtc = WorksheetFunction.Match(ystdy, Range("A7", "ZZ7"), 0)
Cells(8, rtc).Select
End Sub
But I get
13 error on execution time, types doesn't match
Could anyone help me?
Thank you very much.
In your case, since you are looking to find a certain value in a row, the Find function could work better.
Try the code below, explanations inside the code's comments:
Option Explicit
Sub selectvalues()
Dim rtc As Variant
Dim ystdy As Date
Dim tdy As Date
Dim FindRng As Range
tdy = Date
ystdy = WorksheetFunction.WorkDay(tdy, -1)
' use Find function
Set FindRng = Range("A7", "ZZ7").Find(What:=ystdy, LookIn:=xlValues, lookat:=xlWhole)
If Not FindRng Is Nothing Then ' see that find was successful
FindRng.Select
Else
MsgBox "Error, unable to match " & ystdy & " in the specified range", vbCritical
End If
End Sub

How To Have VBA Insert Formula Result as a Value

I got help last week getting my syntax and ranges correct, and thought I could just do a vlookup to finish it but apparently I was mistaken. It just seems like when I try to research how to accomplish this, I find various examples but I don't have the background to translate it to my code.
The macro runs and does almost everything its supposed to do. But in addition to inserting the arrays, there are 3 other cells that need values when there are blank cells in my ‘sourcerng’.
This is the logic for the cells that need values (the values are already in my worksheet, I just need to get them to these blank cells). I tried to do an IIF statement for these but I still have no idea what I'm doing. Would that be the best way? Should it just be another IF THEN statement?
rngBE - IF Column Z = 0 Then copy value from corresponding row in column O. Otherwise copy value from column Z
rngBG - IF Column AA = "Unknown" Then copy value from corresponding row in column I. Otherwise copy value from column AA.
rngBK - IF Column AB = "Unknown" Then copy value from corresponding row in column N. Otherwise copy value from column AB.
Sub AutomateAllTheThings6()
Dim arr3() As String
Dim arr11() As String
'Dim resBE As String
Dim rng3 As Range
Dim rng11 As Range
Dim rngBE As Range
Dim rngBG As Range
Dim rngBK As Range
Dim sourcerng As Range
'Dim firstRow As Long
Dim lastRow As Long
'Dim i As Long
Call OptimizeCode_Begin
'firstRow = 2
lastRow = ActiveSheet.Range("D1").End(xlDown).Row
Set rng3 = ActiveSheet.Range("BH2:BJ" & lastRow)
Set rng11 = ActiveSheet.Range("BL2:BV" & lastRow)
Set rngBE = ActiveSheet.Range("BE2:BE" & lastRow)
Set rngBG = ActiveSheet.Range("BG2:BG" & lastRow)
Set rngBK = ActiveSheet.Range("BK2:BK" & lastRow)
Set sourcerng = ActiveSheet.Range("BE2:BE" & lastRow)
arr3() = Split("UNKNOWN,UNKNOWN,UNKNOWN", ",")
arr11() = Split("UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,00/00/0000, _
00/00/0000,00/00/0000,00/00/0000,NEEDS REVIEW", ",")
For Each cell In sourcerng
If IsEmpty(cell) Then
Intersect(rng3, ActiveSheet.Rows(cell.Row)).Value = arr3
Intersect(rng11, ActiveSheet.Rows(cell.Row)).Value = arr11
'***PLS HELP***
Intersect(rngBE, ActiveSheet.Rows(cell.Row)).Value = "WEEEEE"
Intersect(rngBG, ActiveSheet.Rows(cell.Row)).Value = "WOOOOO"
Intersect(rngBK, ActiveSheet.Rows(cell.Row)).Value = "WAAAAA"
End If
Next
Range("BR2:BU2").Select
Range(Selection, Selection.End(xlDown)).Select
Selection.NumberFormat = "mm/dd/yyyy"
Columns("BF:BF").Select
Selection.Delete Shift:=xlToLeft
Call OptimizeCode_End
End Sub
'*********TESTING***********
'resBE = IIf(Cells(13,Z).Value = 0, Cells(13,BE).Value = Cells(13,Z), Cells(13,BE).Value = Cells(13,O))
'***************************************
'For i = firstRow To lastRow
' valZ = Range("Z" & i)
' valOh = Range("O" & i)
'
' If valZ = 0 Then
' rngBE.Value = valOh
' Else rngBE.Value = valZ
' End If
There are several ways to do your task. If you're more of an "Excel" person than VBA you might consider this approach: You can inject the syntax of any "regular" formula in R1C1 Format.
So the formula mentioned above =if($Z2=0,$O2,$Z2) is .FORMULA format for any value in row 2.
But in .FORMULAR1C1 it can be inserted in ANY cell as: =IF(RC26=0,RC15,RC26) (basically no rows up or down, but always columns O (15) and Z(26).
So, your modified code would have something like this:
Intersect(rngBE, ActiveSheet.Rows(cell.Row)).FormulaR1C1 = "=IF(RC26=0,RC15,RC26)"
Intersect(rngBE, ActiveSheet.Rows(cell.Row)).Value = _
Intersect(rngBE, ActiveSheet.Rows(cell.Row)).Value
Again, this is NOT the most efficient way to accomplish your task, but if you're dealing with thousandsof rows, versus tens to hundreds of thousands, I wouldn't worry about it and it gives you a new tool to use.

VBA countif statement only returns 0

I'm working on a macro that is supposed to count the number of times the term "GM" appears in a column. I decided to use a countif statement, as I have before and it worked well. However, for some reason when I run my code it outputs 0 every time, which definitely is not correct. I've run this same code with other columns and strings and it has worked fine, but for some reason if I search this certain column for the term "GM" it fails. The only thing I can think of is maybe countif only works if the string you're searching for is the only string in a cell, because in all cases where this is true the code works fine. In this particular case the string I'm looking for is not the only string in the cell and the code is failing. I've tried to find more info on whether or not this is true but I can't find anything online. Here's the code if anyone would like to take a look:
Function OemRequest() As Long
Sheets("CS-CRM Raw Data").Select
Sheets("CS-CRM Raw Data").Unprotect
Dim oem As Long
Dim LastRow As Long
Dim LastColumn As Long
'Determines size of table in document
LastRow = Range("A" & Rows.Count).End(xlUp).row
LastColumn = Cells(1, Columns.Count).End(xlToLeft).Column
oem = Application.WorksheetFunction.CountIf(Range(2 & "2:" & 2 & LastRow), "gm")
OemRequest = oem
End Function
You are correct that the COUNTIF as written will only match cells where the whole content is "gm". The criteria in the COUNTIF function will also accept wildcards, so to match on cells that contain "gm" do:
.CountIf(Range(2 & "2:" & 2 & LastRow), "*gm*")
Update
As you noted there is also an issue with your Range call. As it is, the expression inside the parens will evaluate to "22:2<LastRow>" (where <LastRow> is the value of the LastRow variable).
The 2's in there should be a variable containing the column name you're interested in. Something like:
Dim col as String
col = "B"
... Range(col & "2:" & col & LastRow) ...
This will evaluate to "B2:B<LastRow>", which is what you want.
Another possibility:
oem = WorksheetFunction.CountIf(Columns(LastColumn).Cells(2).Resize(rowsize:=LastRow - 1), "gm")
This will count cells containing "gm" (use wilcards if needed) in the LAST column of the table, except the one in the first row. (It assumes the table upper left corner is in cell "A1")
Of course you can create a variable if you would like to count any other column:
Dim lngCol as Long
lngCol = ...
oem = WorksheetFunction.CountIf(Columns(lngCol).Cells(2).Resize(rowsize:=LastRow - 1), "gm")
I think in this way
Sub Main()
Application.ScreenUpdating = 0
Dim Count As Double
Range("C1").Activate 'Firs row in the column
Do While ActiveCell.Value <> ""
If InStr(ActiveCell.Value, "MyText") Then
Count = Count + 1
End If
ActiveCell.Offset(1, 0).Activate
Loop
Application.ScreenUpdating = 1
End Sub
This will work, only if the data cell is not empty, if there is an empty space in middle of the worksheet, do this:
Sub Main()
Application.ScreenUpdating = 0
Dim Count As Double
Range("C1").Activate
Do While ActiveCell.Row <> Rows.Count ' This wil evaluate all the rows in the 'C' Column
If InStr(ActiveCell.Value, "MyText") Then
Count = Count + 1
End If
ActiveCell.Offset(1, 0).Activate
Loop
Application.ScreenUpdating = 1
End Sub
Hope it's work for you.

Combine and Pass value from cells to another in Excel VBA

Basically, I 'd like to combine values from two cell and then display it in another new cell after I click a button. The tricky part is every time when I enter a new value, it should display in the next row. For example, I shall combine value from A1 and B1 and pass it to C1. the next time I re-enter A1 and B1, the new value should pass to C2.
Here is the code I wrote:
Private Sub CommandButton2_Click()
Dim count As Integer
Dim rowNo As String
Dim val As String
Dim val2 As String
Dim sum As String
count = 1
rowNo = "C" + CStr(count)
If (Range("A1") <> "" And Range("B1") <> "") Then
val = Range("A1")
val2 = Range("B1")
sum = val + "/" + val2
Worksheets("Sheet1").Range(rowNo).Value = sum
count = count + 1
End If
End Sub
I am new to excel VBA, the above code only write value in A1 and it didnt go to next row when I re-enter the values, can anyone help me to solve this?
Besides answering your specific question, I will add a few (hopefully useful) comments on your code.
It is convenient to fully qualify ranges, e.g., use Worksheets("Sheet1").Range instead of Range, see this explanation.
To choose a Range you can use direct addressing (as in the comment by chris nielsen) or other options, as Offset, possibly convenient here.
It appears that your code will always go to the same target cell rowNo C1, since count is reset to 1. You will have to let your Sub know where to place the result, and this is key. I guess the safest option is to have one cell in your worksheet set to contain that information, and have your Sub reading it. If you know that column C will only contain the results you want, and that data will be contiguous there, then you could use the code below, which accounts for all items here.
It is often convenient to define a variable for using as reference, e.g., Dim rng as Range and Set rng = Worksheets("Sheet1").Range...
Code below should work.
Private Sub CommandButton2_Click()
Dim val As String
Dim val2 As String
Dim sum As String
Dim rng As Range
Dim ws As Worksheet
Set ws = Worksheets("Sheet1")
With Worksheets("Sheet2")
If (IsEmpty(.Range("C1"))) Then
Set rng = .Range("C1")
Else
' There are other options to find the last cell
Set rng = .Cells(.Rows.count, "C").End(xlUp).Offset(1, 0)
End If
If (ws.Range("A1") <> "" And ws.Range("B1") <> "") Then
val = ws.Range("A1")
val2 = ws.Range("B1")
' Added ' to prevent sum being converted into a date
'sum = "'" + val + "/" + val2
sum = "'" & val & "/" & val2
rng.Value = sum
End If
End With
End Sub
PS: there are some variations in the way to select the target range, depending on the contents of your worksheet.
Try
Worksheets("Sheet1").Range(rowNo).Value = sum