Filter Date with VBA - excel

[I need some help how to count date in another workbook with VBA.
Sub countMacro()
Dim oWBWithColumn As Workbook: Set oWBWithColumn = Application.Workbooks.Open("D:\U2000\Taishan01\Dump\Taishan01_0428.xlsx")
Dim oWS As Worksheet: Set oWS = oWBWithColumn.Worksheets("CurrentAlarms20210428102131871_")
Dim intLastRow As Long: intLastRow = oWS.Cells(Rows.Count, "J").End(xlUp).Row
ThisWorkbook.Worksheets("Sheet1").Range("D2").Value = Application.WorksheetFunction.CountA(oWS.Range("J7:J" & intLastRow), "<2021")
oWBWithColumn.Close False
Set oWS = Nothing
Set oWBWithColumn = Nothing
End Sub
This is my code I want filter date <2021 and between 2 date 01-01-2021 to 04-01-2021. Please kindly help. Because I want to try is can't run.

When you will have the range converted to Date, please use the next (adapted) code:
Sub countMacro()
Dim oWBWithColumn As Workbook: Set oWBWithColumn = Application.Workbooks.Open("D:\U2000\Taishan01\Dump\Taishan01_0428.xlsx")
Dim oWS As Worksheet: Set oWS = oWBWithColumn.Worksheets("CurrentAlarms20210428102131871_")
Dim intLastRow As Long: intLastRow = oWS.cells(oWS.rows.count, "J").End(xlUp).row
Dim firstD As Long, endD As Long, rng As Range
Set rng = oWS.Range("J7:J" & intLastRow)
firstD = CLng(DateSerial(2021, 1, 1))
endD = CLng(DateSerial(2021, 4, 1))
ThisWorkbook.Worksheets("Sheet1").Range("D2").value = WorksheetFunction.CountIfs(rng, ">=" & firstD, rng, "<=" & endD)
oWBWithColumn.Close False
End Sub
Edited:
In order to convert your text looking like Date, in Date, please use the next code:
Sub TransformTextInDate()
Dim sh As Worksheet, lastR As Long, rngT As Range, arrT, ArrD, arr, i As Long
Set sh = ActiveSheet 'use here the sheet you need, or activate the one to be processed
lastR = sh.Range("J" & sh.rows.count).End(xlUp).row
Set rngT = sh.Range("J7:J" & lastR) 'use here your range to be converted in Date
arrT = rngT.value 'load the range in an array
ReDim ArrD(1 To UBound(arrT), 1 To 1) 'redim the array to keep Date
For i = 1 To UBound(arrT)
arr = Split(arrT(i, 1), "-") 'split the text by "-"
ArrD(i, 1) = CDate(left(arr(2), 2) & "/" & arr(1) & "/" & arr(0)) 'build the Date
Next i
rngT.Offset(0, 1).EntireColumn.Insert xlLeft 'insert a column to the right of the processed one
With sh.cells(rngT.row, rngT.Offset(0, 1).Column).Resize(UBound(ArrD), 1)
.Value2 = ArrD 'drop the processed array values at once
.EntireColumn.AutoFit 'fit the new column
.NumberFormat = "dd/mm/yyyy" 'format the range in the standard way
End With
MsgBox "Converted to Date..."
End Sub
The above code will insert a column and drop the processed result in it.
If the conversion is correct, you may delete the previous column, or if you like keeping it (for hours or for something else), you should adapt the first code to make it working on the new column (please, change J to K).
Please, send some feedback after testing the above codes.

Related

Get the respective values of the latest closing date

As you see on the above picture:
I need to match the values on Wb1.coumns(1) with the other workbook Wb2.coumns(1) with some conditions.
Wb2 will be filtered of the value Close at column M.
Then I seek the latest closing date and get it’s respective value at column B and input that value in Wb1.column(K).
the below code may work on the provided example correctly, But it is not reliable on my actual dataset,
because it depends on the sort of many columns from oldest to newest.
This is a link for the provided sample
Sub Get_the_respective_value_of_Last_Closing_Date()
Dim wb1 As Workbook, wb2 As Workbook
Dim ws1 As Worksheet, ws2 As Worksheet
Dim rng1 As Range, rng2 As Range
Dim arr1() As Variant, arr2() As Variant
Application.ScreenUpdating = False
Set wb1 = ThisWorkbook
Set wb2 = Workbooks.Open("Path of wb2", UpdateLinks:=False, ReadOnly:=True)
Set ws1 = wb1.Sheets(1)
Set ws2 = wb2.Sheets(1)
Set rng1 = ws1.Range("A3:K" & ws1.Cells(ws1.Rows.Count, "A").End(xlUp).Row) 'Main Range
Set rng2 = ws2.Range("A3:M" & ws2.Cells(ws2.Rows.Count, "A").End(xlUp).Row)
arr1 = rng1.Value2
arr2 = rng2.Value2
Dim i As Long, k As Long
For i = LBound(arr1) To UBound(arr1)
For k = LBound(arr2) To UBound(arr2)
If arr1(i, 1) = arr2(k, 1) And arr2(k, 13) = "Close" Then
rng1.Cells(i, 11) = arr2(k, 2)
End If
Next k
Next i
wb2.Close SaveChanges:=False
Application.ScreenUpdating = True
End Sub
Please, try the next adapted code. It uses a dictionary, to keep the unique kay (and last value from "K:K" as item) of the opened Workbook, then placing the appropriate data in the working workbook:
Sub Get_Last_Closing_Date()
Dim wb1 As Workbook, wb2 As Workbook
Dim ws1 As Worksheet, ws2 As Worksheet
Dim rng1 As Range, rng2 As Range
Dim arr1() As Variant, arr2() As Variant
Dim dict As Object
Application.ScreenUpdating = False
Set wb1 = ThisWorkbook
'Please, update the real path of "Book2.xlsx":
Set wb2 = Workbooks.Open(ThisWorkbook.Path & "\Book2.xlsx", UpdateLinks:=False, ReadOnly:=True)
Set ws1 = wb1.Sheets(1)
Set ws2 = wb2.Sheets(1)
Set rng1 = ws1.Range("A3:K" & ws1.Cells(ws1.Rows.Count, "A").End(xlUp).Row) 'Main Range
Set rng2 = ws2.Range("A3:M" & ws2.Cells(ws2.Rows.Count, "A").End(xlUp).Row)
arr1 = rng1.Value2
arr2 = rng2.Value2
'place the unique last key in a dictionary:
Dim i As Long
Set dict = CreateObject("Scripting.dictionary")
For i = 1 To UBound(arr2)
If arr2(i, 13) = "Close" Then
dict(arr2(i, 1)) = arr2(i, 2)
End If
Next i
Debug.Print Join(dict.items, "|") 'just to visualy see the result
'Place the necessary data in its place:
For i = 1 To UBound(arr1)
If dict.Exists(arr1(i, 1)) Then
arr1(i, 11) = dict(arr1(i, 1))
Else
arr1(i, 11) = "NA"
End If
Next i
rng1.Value2 = arr1 'drop back the updated array content
wb2.Close SaveChanges:=False
Application.ScreenUpdating = True
MsgBox "Ready..."
End Sub
Column "K:K" of the workbook to be opened must be sorted ascending...
Edited:
The next version works without needing to have column "K:K" sorted:
Sub Get_Last_Closing_Date()
Dim wb1 As Workbook, wb2 As Workbook
Dim ws1 As Worksheet, ws2 As Worksheet
Dim rng1 As Range, rng2 As Range
Dim arr1() As Variant, arr2() As Variant
Dim dict As Object
Application.ScreenUpdating = False
Set wb1 = ThisWorkbook
Set wb2 = Workbooks.Open(ThisWorkbook.Path & "\Book2.xlsx", UpdateLinks:=False, ReadOnly:=True)
Set ws1 = wb1.Sheets(1)
Set ws2 = wb2.Sheets(1)
Set rng1 = ws1.Range("A3:K" & ws1.Cells(ws1.Rows.Count, "A").End(xlUp).Row) 'Main Range
Set rng2 = ws2.Range("A3:M" & ws2.Cells(ws2.Rows.Count, "A").End(xlUp).Row)
arr1 = rng1.Value2
arr2 = rng2.Value2
'place the unique last key in a dictionary:
Dim i As Long
Set dict = CreateObject("Scripting.dictionary")
For i = 1 To UBound(arr2)
If arr2(i, 13) = "Close" Then
If Not dict.Exists(arr2(i, 1)) Then
dict(arr2(i, 1)) = Array(arr2(i, 2), arr2(i, 11)) 'place the date from K:K, too
Else
If CDate(arr2(i, 11)) > CDate(dict(arr2(i, 1))(1)) Then 'change the item only in case of a more recent date:
dict(arr2(i, 1)) = Array(arr2(i, 2), arr2(i, 11))
End If
End If
End If
Next i
'Place the necessary data in its place:
For i = 1 To UBound(arr1)
If dict.Exists(arr1(i, 1)) Then
arr1(i, 11) = dict(arr1(i, 1))(0) 'extract first item array element
Else
arr1(i, 11) = "NA"
End If
Next i
rng1.Value2 = arr1 'drop back the updated array content
wb2.Close SaveChanges:=False
Application.ScreenUpdating = True
MsgBox "Ready..."
End Sub
You may benefit from functions in Excel and combine them with Evaluate trough VBA. Just as example I made up this:
I made up this in same worksheet just as explanation. The formula to get this in column K is:
=IFERROR(INDEX($N$2:$N$16,SUMPRODUCT(--($W$2:$W$16=MAX(--($Y$2:$Y$16="Close")*--($M$2:$M$16=A2)*$W$2:$W$16))*ROW($M$2:$M$16))-1),"NA")
This formula will return desired output. Applied to VBA would be:
Sub Get_Last_Closing_Date()
Dim wb1 As Workbook, wb2 As Workbook
Dim ws1 As Worksheet, ws2 As Worksheet
Dim rng2 As Range
Dim i As Long
Dim MyFormula As String
Application.ScreenUpdating = False
Set wb1 = ThisWorkbook
Set wb2 = Workbooks.Open("D:\Users\gaballahw\Desktop\Book2.xlsx", UpdateLinks:=False, ReadOnly:=True)
Set ws1 = wb1.Sheets(1)
Set ws2 = wb2.Sheets(1)
Set rng2 = ws2.Range("A3:M" & ws2.Cells(ws2.Rows.Count, "A").End(xlUp).Row)
With ws1
For i = 3 To ws1.Cells(ws1.Rows.Count, "A").End(xlUp).Row Step 1
MyFormula = "IFERROR(INDEX(" & rng2.Columns(2).Address & ",SUMPRODUCT(--(" & rng2.Columns(11).Address & _
"=MAX(--(" & rng2.Columns(13).Address & "=""Close"")*--(" & rng2.Columns(1).Address & _
"=" & .Range("A" & i).Value & ")*" & rng2.Columns(11).Address & "))*ROW(" & rng2.Columns(1).Address & "))-2),""NA"")" '-2 because data starts at row 3
.Range("K" & i).Value = Evaluate(MyFormula)
Next i
End With
wb2.Close SaveChanges:=False
Set rng2 = Nothing
Set ws1 = Nothing
Set ws2 = Nothing
Set wb1 = Nothing
Set wb2 = Nothing
Application.ScreenUpdating = True
End Sub
Also, if you have Excel365 you may benefit from function MAXIFS:
MAXIFS function
I'm pretty sure that in the formula provided the part --($W$2:$W$16=MAX(--($Y$2:$Y$16="Close")*--($M$2:$M$16=A2)*$W$2:$W$16)) could be replaced with a MAXIFS but i got an older version of Excel so I can't test.
Also, check Evaluate:
Application.Evaluate method
(Excel)
SORT And XLOOKUP to Get Maximums
In Microsoft 365, you could use the following spilling formula in cell K3 of Sheet1:
=LET(sArray,Sheet2!A3:M22,sFilterCol,13,sCriteria,"Closed",sSortCols,{11;1},sSortOrders,{-1;1},sLookupCol,1,sReturnCol,2,
dLookup,A3:A14,dNotFound,"NA",
sSorted,SORT(FILTER(sArray,CHOOSECOLS(sArray,sFilterCol)=sCriteria),sSortCols,sSortOrders),
sLookup,CHOOSECOLS(sSorted,sLookupCol),sReturn,CHOOSECOLS(sSorted,sReturnCol),
XLOOKUP(dLookup,sLookup,sReturn,dNotFound))
The 1st row holds the source constants (7) while the 2nd row the destination constants (2).
The 3rd row returns the source array filtered and sorted.
In the 4th row this modified array is used to get the source lookup and return columns.
These columns, along with the destination constants, are then fed to the XLOOKUP function in the 5th row.
Edit
For this to work with your test files, with Book2.xlsx open, you need to replace Sheet2!A3:M22 with '[Book2.xlsx]Wb2-sh1'!A3:M18, A3:A14 with A3:A8 and Closed with Close (my bad).

Create New Row in Sheet1 if Value in ((Sheet2, Column A) or (Sheet3, Column A)) Doesn't Exist in (Sheet 1, Column A)

I am trying to write a macro that will look in column A on sheet1 and see if it is missing any values from column A on sheet2 or column A on sheet3. If it is missing have the value added to the bottom of the column A on sheet1. The same value may exist on sheet2 and sheet3 but it only needs to be represented once on sheet1.
I'm working with the code below.
Sub newRow()
Dim rngSh1 As Range, rngSh2 As Range, rngSh3 As Range, mySelSh2 As Range, mySelSh3 As Range
Dim lastRowSh1 As Long, lastRowSh2 As Long, lastRowSh3 As Long
Dim wb As Worksheet
Dim cell As Range
Set wb = ThisWorkbook
With wb
lastRowSh1 = Worksheets("Sheet1").Range("A" & .Rows.Count).End(xlUp).Row
lastRowSh2 = Worksheets("Sheet2").Range("A" & .Rows.Count).End(xlUp).Row
lastRowSh3 = Worksheets("Sheet3").Range("A" & .Rows.Count).End(xlUp).Row
Set rngSh1 = Worksheets("Sheet1").Range("A1:A" & lastRowSh1)
Set rngSh2 = Worksheets("Sheet2").Range("A1:A" & lastRowSh2)
Set rngSh3 = Worksheets("Sheet3").Range("A1:A" & lastRowSh3)
End With
For Each cell In rngSh2.Cells
If IsError(Application.Match(cell.Value, rngSh1, 0)) Then
If mySelSh2 Is Nothing Then
Set mySelSh2 = cell
Else
Set mySelSh2 = Union(mySelSh2, cell)
End If
End If
Next cell
If Not mySelSh2 Is Nothing Then mySelSh2.Copy Destination:=Worksheets("Sheet1").Range("A" & lastRowSh1 + 1)
For Each cell In rngSh3.Cells
If IsError(Application.Match(cell.Value, rngSh1, 0)) Then
If mySelSh3 Is Nothing Then
Set mySelSh3 = cell
Else
Set mySelSh3 = Union(mySelSh3, cell)
End If
End If
Next cell
If Not mySelSh3 Is Nothing Then mySelSh3.Copy Destination:=Worksheets("Sheet1").Range("A" & lastRowSh1 + 1)
End Sub
I've made every adjustment I can think of but with every change I make I get a different error.
Any help would be greatly appreciated. Thanks!
Save yourself a little bit of time using a Scripting.Dictionary:
Option Explicit
Sub test()
Dim dict As New Scripting.dictionary, sheetNum As Long
For sheetNum = 2 To Sheets.Count
With Sheets(sheetNum)
Dim lastRow As Long: lastRow = .Cells(.Rows.Count, 1).End(xlUp).Row
Dim rowNum As Long
For rowNum = 1 To lastRow
Dim dictVal As Long: dictVal = .Cells(rowNum, 1).Value
If Not dict.Exists(dictVal) Then dict.Add dictVal, 0
Next rowNum
End With
Next sheetNum
With Sheets(1)
Dim checkableRangeLastRow As Long: checkableRangeLastRow = .Cells(.Rows.Count, 1).End(xlUp).Row
Dim checkableRange As Range: Set checkableRange = .Range(.Cells(1, 1), .Cells(checkableRangeLastRow, 1))
Dim dictKey As Variant
For Each dictKey In dict.Keys
If IsError(Application.Match(dictKey, checkableRange, 0)) = True Then
lastRow = .Cells(.Rows.Count, 1).End(xlUp).Row
.Cells(lastRow + 1, 1).Value = dictKey
End If
Next dictKey
End With
End Sub
You add all values in your not-master-sheet into dict then loop through that list; if it's not found in your master-sheet, then you add then to the end of the list.
A significant note is that the Type of value used as the dictVal may cause the IsError() statement to always be True if it is not the same Type as the data being assessed in the checkableRange.

SUMIF Using VBA has Object Error But Unable to Resolve

I am unable to find the Object Error is appearing when i run the function. I do not why this is happening. It should work fine but no it does not. I hope to get some help and any help will be appreciated.
Sub SumIF()
Dim LastRow As Long
Dim sh As Worksheet
Set sh = Sheets("SumIF")
LastRow = sh.Cells(Rows.Count, 1).End(xlUp).Row
Table1 = sh.Range("A2:A" & LastRow) 'Need to Match this with Table3
Table2 = sh.Range("B2:B" & LastRow) 'Need to Sum this in K2:K
Table3 = sh.Range("J2:J" & LastRow)
sh.Range("K2:K" & LastRow) = Application.WorksheetFunction.SumIF(Table1, Table3, Table2)
End Sub
VBA SumIf Using .Formula
Option Explicit
Sub VBASumIfFormula()
' Workbook, Worksheet
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
Dim ws As Worksheet: Set ws = wb.Worksheets("SumIf")
' Source Column Ranges
Dim slRow As Long: slRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
If slRow < 2 Then Exit Sub ' no data in column range
Dim slrg As Range: Set slrg = ws.Range("A2:A" & slRow) ' lookup column
Dim svrg As Range: Set svrg = slrg.Offset(0, 1) ' values column
' Destination Column Ranges
Dim dlRow As Long: dlRow = ws.Cells(ws.Rows.Count, "J").End(xlUp).Row
If dlRow < 2 Then Exit Sub ' no data in column range
Dim dlrg As Range: Set dlrg = ws.Range("J2:J" & dlRow) ' lookup column
Dim dvrg As Range: Set dvrg = dlrg.Offset(0, 1) ' values column (empty)
' Construct formula string.
Dim FormulaString As String
FormulaString = "=IFERROR(SUMIF(" & slrg.Address & "," _
& dlrg.Cells(1).Address(0, 0) & "," & svrg.Address & "),"""")"
'Debug.Print FormulaString
' Write formulas.
dvrg.Formula = FormulaString
' Convert formulas to values.
dvrg.Value = dvrg.Value
End Sub
If you insist on doing it your way, which is less efficient since looping is necessary, you could do...
' Either...
Dim cCell As Range
For Each cCell In Table3.Cells
cCell.Offset(0, 1).Value = Application.WorksheetFunction _
.SumIf(Table1, cCell.Value, Table2)
Next cCell
' ... or:
Dim cCell As Range
For Each cCell In sh.Range("K2:K" & LastRow).Cells ' or e.g. 'Table4'
cCell.Value = Application.WorksheetFunction _
.SumIf(Table1, cCell.Offset(0, -1).Value, Table2)
Next cCell
because the second argument, criteria, is not supposed to be a range:
... criteria in the form of a number, expression, a cell reference, text, or a function...

VBA to Vlookup inside a Loop that compares data between 2 sheets and pastes de repeated values in a 3rd sheet

My objective is to compare data in a range in Ws1 with data in a range in ws2, and copy those values that repeat in ws3.
Ideally I would like to copy the value found and the rest of the information to the right of that cell (from ws2), but for now I am happy just copying the value found.
I decided to use a loop for this but I was getting an infinite looping, and now that I re-summarize; I am getting:
range of object _ global failed" error and it points to: "With
Range(ws3.cells(i, 1))
Sub quicktests()
Dim ws1, ws2, ws3 As Worksheet
Dim ColNum, ColNum2 As Long
Dim rng, Range2 As Range
Dim lRow1, lRow2, lCol2 As Integer
ColNum = 9
ColNum2 = 14
lRow1 = 347
Set ws2 = Sheets("Filled today")
With ws2
lCol2 = .cells(1, .Columns.Count).End(xlToLeft).Column
'MsgBox lCol2
lRow2 = .cells(.Rows.Count, 1).End(xlUp).Row
'MsgBox lRow2
Set Range2 = .Range(.cells(1, ColNum2), .cells(lRow2, lCol2))
End With
Set ws3 = Sheets("Duplicates filled and hiring")
Set ws1 = Sheets("Reconciliated Recruiment Plan")
For i = 1 To lRow1
With ws1
Set rng = .cells(i, ColNum)
End With
With Range(ws3.cells(i, 1))
.Formula = "=VLookup(rng, Range2, ColNum, False)"
.Value = .Value
End With
Next i
End Sub
Looking at just the part with the VLOOKUP:
You can't used range with one cells() it needs a begining and an end, remove the Range wrapper.
The Vlookup; You need to remove the vba part from the string and concatenate it.
With ws3.cells(i, 1)
.Formula = "=VLookup(" & rng.Address(0,0) & "," & Range2.Address(0,0) & "," & ColNum & ", False)"
.Value = .Value
End With

Concatenate unique cell values in every row

How can I concatenate unique cell values in every row to adapt in the code below. Removing duplicate values in a cell. Result after macro is the second image.
Sub Concatenar()
Dim myRange As Range
Dim c As Long
Dim r As Long
Dim txt As String
Set myRange = Application.InputBox("Selecione a primeira e a última célula:", Type:=8)
For r = 1 To myRange.Rows.Count
For c = 1 To myRange.Columns.Count
If myRange(r, c).Text <> "" Then
txt = txt & myRange(r, c).Text & vbLf
End If
Next
If Right(txt, 1) = vbLf Then
txt = Left(txt, Len(txt) - 1)
End If
myRange(r, 1) = txt
txt = ""
Next
Range(myRange(1, 2), myRange(1, myRange.Columns.Count)).EntireColumn.Delete
End Sub
This does what you want, I believe. It pastes/tranposes the values to a temporary workbook, uses RemoveDuplicates to trim them down, and Join to munge them all together. It then pastes the munged values back into column A of the original workbook and deletes the other columns.
Because of the destructive nature of this code, you must test it on a copy of your data:
Sub CrazyPaste()
Dim wsSource As Excel.Worksheet
Dim rngToConcat As Excel.Range
Dim wbTemp As Excel.Workbook
Dim wsPasted As Excel.Worksheet
Dim rngPasted As Excel.Range
Dim i As Long
Dim LastRow As Long
Dim Results() As String
Set wsSource = ActiveSheet
Set rngToConcat = wsSource.UsedRange
Set wbTemp = Workbooks.Add
Set wsPasted = wbTemp.Worksheets(1)
wsSource.UsedRange.Copy
wsPasted.Range("A1").PasteSpecial Transpose:=True
Set rngPasted = wsPasted.UsedRange
ReDim Results(1 To rngPasted.Columns.Count)
For i = 1 To rngPasted.Columns.Count
If WorksheetFunction.CountA(rngPasted.Columns(i)) = 0 Then
Results(i) = ""
Else
rngPasted.Columns(i).RemoveDuplicates Columns:=1, Header:=xlNo
LastRow = Cells(wsPasted.Rows.Count, i).End(xlUp).Row
Results(i) = Replace(Join(Application.Transpose(rngPasted.Columns(i).Resize(LastRow, 1)), vbCrLf), _
vbCrLf & vbCrLf, vbCrLf)
End If
Next i
With wsSource
.Range("A1").Resize(i - 1, 1) = Application.Transpose(Results)
.Range(.Cells(1, 2), .Cells(1, .Columns.Count)).EntireColumn.Delete
wbTemp.Close False
End With
End Sub
In my limited testing, the only situation where this might yield unwanted results is when a cell in the first column is blank, but there's other data in that row. The resulting cell would then start with a blank.

Resources