I have data that is similar to:
A1: ID
B1: Start date
C1: End Date
I have another worksheet (call it New) that has
A1: ID and
B1: Date
I need to find out if the date for the ID in New worksheet was already in the previous Worksheet. If the date is start date, end date or anything in between, I want it to show that there is a record that already exist.
Solution here assuming something more practical:
A Master sheet with ID, Start Date, End Date (multiple rows)
Other sheets with ID and Date (multiple rows)
Uses a User Defined Function (UDF) and the ID cell as input
One drawback is that you will need "Calculate Sheet" if other sheets has been updated
Sample screenshots:
Formula for Sheet1 D2: =FindDuplicates(A2)
Code in a Module:
Option Explicit
Function FindDuplicates(oRngID As Range) As String
Dim sID As String, dStart As Date, dEnd As Date, lCount As Long, sWhere As String
Dim oWS As Worksheet, oRngFound As Range, dFound As Date, sFirstFound As String
sID = oRngID.Text
dStart = oRngID.Offset(0, 1).Value
dEnd = oRngID.Offset(0, 2).Value
lCount = 0
sWhere = ""
For Each oWS In ThisWorkbook.Worksheets
' Find all IDs in other worksheeets
If oWS.Name <> oRngID.Worksheet.Name Then
sFirstFound = ""
Set oRngFound = oWS.Cells.Find(What:=sID)
If Not oRngFound Is Nothing Then
sFirstFound = oRngFound.Address
' Keep searching until the first found address is met
Do
' Check the dates, only add if within the dates
dFound = oRngFound.Offset(0, 1).Value
If dStart <= dFound And dFound <= dEnd Then
lCount = lCount + 1
If lCount = 1 Then
sWhere = sWhere & lCount & ") '" & oWS.Name & "'!" & oRngFound.Address
Else
sWhere = sWhere & vbCrLf & lCount & ") '" & oWS.Name & "'!" & oRngFound.Address
End If
End If
Set oRngFound = oWS.Cells.Find(What:=sID, After:=oRngFound)
Loop Until oRngFound.Address = sFirstFound
End If
End If
Next
If lCount = 0 Then sWhere = "Not Found"
FindDuplicates = Replace(sWhere, "$", "") ' Removes the $ sign in Addresses
End Function
So, you have 3 problems:
1) Take value from another worksheet:
Dim startDate As Date
startDate = ActiveWorkbook.worksheets("OtherSheetName").cells(row,col).Value
2) Compare data:
If startDate <= actualDate And actualDate <= endDate Then
...
Else
...
End If
3) Set cell value:
ActiveWorkbook.worksheets("SheetName").cells(row,col).Value = something
Combine those steps and you'll obtain a solution for your problem.
I kinda liked this question...
The way I would do it would be using the SUMPRODUCT() function to check for multiple criteria (there are many references both on this site and Google explaining how that works)
In your New worksheet, assuming the first row is for headers, in cell C2 put in the following formula:
=SUMPRODUCT(--(Sheet1!$A$2:$A$180=A2),--((B2-Sheet1!$B$2:$B$180)>=0),--((Sheet1!$C$2:$C$180-B2)>=0)) > 0
And drag it down for your entire range (Obviously, adjusting the 180 row reference to suit your data-set)
Basically, what you're saying is:
Give me a TRUE only if there is at least one row in my other sheet where:
- The IDs match
- [My Date] minus row's [Start date] >= 0
- Row's [End Date] - [My Date] >= 0
Hope that makes sense!
Related
I have the following problem: I try to filter a date column (A) in a worksheet (HISTORICALS) to return the highest and second highest date value. Currently, there is dates ranging from the 25th to the 31st of December in this column. Unfortunately, below formula (using the Large function) returns the 31st two times (and not the 30th and 31st as intended).
Sub Select_Last_Two_Days()
With Worksheets("HISTORICALS")
Highest_Max = Format(WorksheetFunction.Large(Worksheets("HISTORICALS").Range("A:A"), 1), "Short Date")
Second_Highest_Max = Format(WorksheetFunction.Large(Worksheets("HISTORICALS").Range("A:A"), 2), "Short Date")
Debug.Print Highest_Max, Second_Highest_Max
End With
End Sub
The column has approx. 2000 rows, with dates occuring multiple times. So ideally I would want to filter for distinct values and then return the two highest dates. Any idea how I can do that?
Simply translate Barry Houdinis answer from How to find the first and second maximum number? to VBA:
Sub Select_Last_Two_Days()
With Worksheets("HISTORICALS")
Highest_Max = Format(WorksheetFunction.Max(.Range("A:A")), "Short Date")
Second_Highest_Max = Format(WorksheetFunction.Large(.Range("A:A"), WorksheetFunction.CountIf(.Range("A:A"), WorksheetFunction.Max(.Range("A:A"))) + 1), "Short Date")
Debug.Print Highest_Max, Second_Highest_Max
End With
End Sub
The recommendations given in the comments are probably the simplest and least amount of code way to do things but here is another sugggestion:
Sub test()
Dim wb As Workbook
Dim ws As Worksheet
Set wb = ThisWorkbook
Set ws = wb.Worksheets("HISTORICALS")
Dim lastRow As Long
lastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
Dim loopArr()
loopArr = ws.Range("A1:A" & lastRow).Value
Dim maxVal As Date
maxVal = Application.WorksheetFunction.Large(ws.Range("A1:A" & lastRow), 1)
Dim i As Long
Dim secondVal As Date
For i = UBound(loopArr, 1) To LBound(loopArr, 1) Step -1
If loopArr(i, 1) < maxVal Then
secondVal = loopArr(i, 1)
Exit For
End If
Next i
End Sub
I have the following formula:
=IF(IFERROR(MATCH($C3,'\\share\done\[dones 05.10.2016.xls]done'!$A$2:$A$49,0),0),VLOOKUP($C3,'\\share\done\[dones 05.10.2016.xls]done'!$A$2:$B$49,2,FALSE),0)
In A1 I have another date: 10.10.2016
How can I replace only the date that is in the file name from the formula?
Until now, I've been using this:
Sub modify()
Dim a As Range
Set a = Range("a1")
[e3:e4].Replace "dones 05.10.2016.xls", ("dones " & a & ".xls"), xlPart
End Sub
The problem that in A2 I have another date and F3:F4 must have the date from A2, and so on until A300. How can I replace only the date of the file name in the formula?
The names of the files are standard: dones dd.mm.yyyy.xls
Sub modify()
Dim c As Range, r As Range
Set c = [a1]
Set r = [e3:e4]
Application.DisplayAlerts = False ' optional to hide dialogs
While c > ""
Debug.Print c.Address(0, 0), r.Address(0, 0) ' optional to check the address
r.Replace "[dones ??.??.????.xls]", "[dones " & c & ".xls]", xlPart
Set c = c.Offset(1, 0) ' A1 to A2
Set r = r.Offset(0, 1) ' E3:E4 to F3:F4
Wend
Application.DisplayAlerts = True
End Sub
Replace with wildcards:
[e3:e4].Replace "[dones ??.??.????.xls]", "[dones " & [a1] & ".xls]", xlPart
? matches any single character and * can be used to match 0 or more characters:
[e3:e4].Replace "[*.xls*]", "[dones " & [a1] & ".xls]", xlPart
https://www.ablebits.com/office-addins-blog/2015/09/29/using-excel-find-replace/#find-replace-wildcards
Instead of hard-coding "dones 05.10.2016.xls", you'll have to build that string from the cell values. Also, you'll need some looping logic to track which row you're reading from and which column you're writing to.
Assuming a date read in row 1 goes in column 5, a date read in row 2 goes in column 6, and so on, something like this should be good enough:
Dim targetColumn As Long
Dim sourceRow As Long
With ActiveSheet
For sourceRow = 1 To WhateverTheLastRowIs
targetColumn = 4 + sourceRow 'column 5 / "E" for sourceRow 1
Dim sourceDateValue As Variant
sourceDateValue = .Cells(sourceRow, 1).Value
Debug.Assert VarType(sourceDateValue) = vbDate
Dim formattedSourceDate As String
formattedSourceDate = Format(sourceDateValue.Value, "MM.DD.YYYY")
'replace string in rows 3 & 4 of targetColumn:
.Range(.Cells(3, targetColumn), .Cells(4, targetColumn) _
.Replace "[*.xls]", "[dones " & formattedSourceDate & ".xls]", xlPart
Next
End With
My understanding of the requirements is this:
There is a List of Dates in Column A starting at Row 1
A formula needs to be entered in rows 3:4 starting in Column E and moving one column to the right for each value in the List of Dates, i.e. Formula in column E has date from row 1, column F has date from row 2, …
This is the formula, in which the date 05.10.2016 in the filename '\\share\done\[dones 05.10.2016.xls]done should be update with corresponding value from the List of Dates as per point 2.
=IF(
IFERROR(MATCH($C3,'\\share\done\[dones 05.10.2016.xls]done'!$A$2:$A$49,0),0),
VLOOKUP($C3,'\\share\done\[dones 05.10.2016.xls]done'!$A$2:$B$49,2,FALSE),0)
This solution assumes the dates in column A are already formated as required by the filename link.
This solution uses a variable to hold the Link Formula and another variable to update the Link Formula with each Value in the List of Dates.
Also to simplify the update\replacement of the date let’s change the original date in the formula for 05.10.2016 for an unique key such as #DATE
Dim sFmlLink As String, sFml As String
sFmlLink = "=IF(" & Chr(10) & _
"IFERROR(MATCH($C3,'\\share\done\[dones #DATE.xls]done'!$A$2:$A$49,0),0)," & Chr(10) & _
"VLOOKUP($C3,'\\share\done\[dones #DATE.xls]done'!$A$2:$A$49,2,FALSE),0)"
Then we set a Range with the List of Dates and loop trough it to update and enter the formula as per point 2.
Sub FormulaLink()
Dim sFmlLink As String, sFml As String
sFmlLink = "=IF(" & Chr(10) & _
"IFERROR(MATCH($C3,'\\share\done\[dones #DATE.xls]done'!$A$2:$A$49,0),0)," & Chr(10) & _
"VLOOKUP($C3,'\\share\done\[dones #DATE.xls]done'!$A$2:$A$49,2,FALSE),0)"
Dim rDates As Range, lRow As Long, iCol As Integer
Rem Set Start Column
iCol = 5
With ThisWorkbook.Sheets("DATA")
Rem Set Dates List Range
Set rDates = Range(.Cells(1), .Cells(Rows.Count, 1).End(xlUp))
Rem Enter Link Formula in Rows 3:4, starting at Column 5
Rem and moving one column to the right for each Date in Column A
For lRow = 1 To rDates.Rows.Count
Rem Refresh Link Formula with Date from Column A
sFml = Replace(sFmlLink, "#DATE", rDates.Cells(lRow).Value)
Rem Enter Formula in Column iCol Rows 3:4
.Cells(3, iCol).Resize(2).Formula = sFml
Rem Move One Column to the right
iCol = 1 + iCol
Next: End With
End Sub
You will need to work with the string functions InStr and Mid here. Maybe this can help you:
Dim str As String
Dim intPos1 As Integer
Dim intPos2 As Integer
Dim intLastPos As Integer
'Formula as string
str = "\\share\done\[dones 05-10-2016.xls]done'!$A$2:$A$49,0),0),VLOOKUP($C3,'\\share\done\[dones 05-10-2016.xls]done"
'Get the start and the End Position of the First Excel File
intPos1 = InStr(1, str, "[dones") - 1
intPos2 = InStr(1, str, ".xls") + 5
'Save the Last Postion for the second Replacement
intLastPos = intPos2
'Replace old File with [dones 01-10-1911.xls]
str = Mid(str, 1, intPos1) & "[dones 01-10-1911.xls]" & Mid(str, intPos2, Len(str))
'Get the start and the End Position of the second Excel File
intPos1 = InStr(intLastPos, str, "[dones")
intPos2 = InStr(intLastPos, str, ".xls")
'Replace the second File with [dones 01-10-1911.xls]
str = Mid(str, 1, intPos1) & "[dones 01-10-1911.xls]" & Mid(str, intPos2, Len(str))
After that you can read back the formula.
I am trying to set up a function which will be able to check dates from one sheet (sheet1) through 2 columns: startDate and endDate in Sheet2. And if there are matching then I want to copy values from one cell which is located on sheet2 into specific cell (the same row where is the cheking date) into sheet1.
I wrote some code, but later on I've realized that my logic is not good.
I also found this link on stackoverflow website...
my xls file - function is in the module 3 "checkDate"
and here is the code..I need find function somehow..maybe I need to insert two iterators (2 for loops?)
Sub CheckDate()
Dim d1 As Date
Dim d2 As Date
Dim datumPok As Date
Dim s As String
Dim i As Long
Dim LR As Long
LR = Range("K" & Rows.Count).End(xlUp).Row
For i = 2 To LR
d1 = ActiveWorkbook.Worksheets("Glasnik").Cells(i, 2).Value
d2 = ActiveWorkbook.Worksheets("Glasnik").Cells(i, 3).Value
With .Range("K" & i)
datumPok = ActiveWorkbook.Worksheets("Spisak").Cells(i, 11)
If d1 < datumPok < d2 Then
MsgBox "opaaa"
s = ActiveWorkbook.Worksheets("Glasnik").Cells(i, 4).Value
ActiveWorkbook.Worksheets("Spisak").Cells(i, 6).Value = s
Else
MsgBox "test"
End If
End With
Next i
End Sub
I am really into this..thanks guys!
I found the solution!
Ordinary lookup with some mathematic formula was enough.
=LOOKUP(2,1/((G1>=$A$1:$A$24)*(G1<=$B$1:$B$24)),$C$1:$C$24)
assuming that date is in the column G1..
Thanks to all! :)
So, right now I have this Excel sheet where there is a last revision date. I have named this column "LastRevisionDate". And then I have a column named "RevisionFrequency" . The "RevisionFrequency" contains a drop-down menu (data validation) consisting of terms, Annually, Semi-Annually, and Quarterly. And then I have a column where it states the "NextRevisionDate".
So I want to write some VBA code that would calculate the NextRevisionDate from the LastRevisionDate and the RevisionFrequency.
For example. Say in column "A" I have the RevisionFrequency to be Semi-Annually, And the last revision date was Mar-14 in column "B", then I would want the NextRevisionDate in column "C" to state September. That's basically saying that the item gets revised twice a year.
So I would want to create a macro where Column "C" is based off the RevisionFrequency and LastRevisionDate. I realize I could do this with a formula, but I have new items being added constantly so I do not want to keep copying formulas into each cell. Also for some items, they do not need revision, I would also like to have a blank cell if there is no LastRevisionDate.
So far, I have this code:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim ws As Worksheet
Set ws = Sheets(1)
'For this reference of the Column Named LastCalDate I am getting an error
If Not Intersect(Target, ws.Range("LastCalDate").Value) Is Nothing Then
Dim Lastdate As Date
Dim DueDate As Variant
Dim Frequency As String
Dim R As Variant
Dim C As Variant
Dim R1 As Variant
Dim C1 As Variant
Dim R2 As Variant
Dim C2 As Variant
R = Range("LastCalDate").Row
C = Range("LastCalDate").Column
R1 = Range("CalDueDate").Row
C1 = Range("CalDueDate").Column
R2 = Range("CalFrequency").Row
C2 = Range("CalFrequency").Column
Lastdate = Cells(R, C).Value 'Last Cal Date
DueDate = Cells(R1, C1).Value 'Cal Due Date
Frequency = Cells(R2, C2)
If Frequency = "Annually" Then
DueDate = DateAdd("mmm", 12, Lastdate)
End If
If Frequency = "Semi-Annually" Then
DueDate = DateAdd("mmm", 6, Lastdate)
End If
If Frequency = "Quarterly" Then
DueDate = DateAdd("mmm", 3, Lastdate)
End If
End Sub
This is what I have so far. I'm not sure If I am doing this correctly?
Using the Worksheet_Change method is a great way to create the new cell value without having to copy and paste a formula. I included checks in my code as well to make sure if the date or frequency is not set, then the value is cleared out.
Private Sub Worksheet_Change(ByVal Target As Range)
' declare and set worksheet
Dim ws As Worksheet
Set ws = Sheets(1)
' declare and set default date
Dim DefaultDueDate As Date
' declare needed variables
Dim StartDate As Date
Dim Frequency As String
Dim DueDate As Date
' make sure the change only occured on the "A" or "B" column
If Target.Column = 1 Or Target.Column = 2 Then
StartDate = ws.Range("A" & Target.Row)
Frequency = ws.Range("B" & Target.Row)
' if start date does not equal the default due date and the frequency is not blank, set due date variable
If StartDate <> DefaultDueDate And Frequency <> "" Then
' add months to the provided start date
If Frequency = "Annually" Then
DueDate = DateAdd("m", 12, StartDate)
ElseIf Frequency = "Semi-Annually" Then
DueDate = DateAdd("m", 6, StartDate)
ElseIf Frequency = "Quarterly" Then
DueDate = DateAdd("m", 3, StartDate)
End If
' Make sure frequency selection is correct and due date was set
If DueDate <> DefaultDueDate Then
ws.Range("C" & Target.Row) = DueDate
End If
Else
' clear Next Revision Date when Frequency or Start Date is blank
ws.Range("C" & Target.Row) = ""
End If
End If
End Sub
I have been looking around to find good examples, but can't find what I need.
Here is the context: The code is for a sales tracker worksheet with around 50 vendors (each of them can add value and most of them didn't know anything about Excel).
I want to select the first empty cell (where the first they can enter a value is B5, not higher, because the top of the sheet includes some instructions). In fact, from this cell (Date value is in Column B, and begin in Row 5) the second date value is in B6
Add the Date (date or now) as activecell.value
Then 2 cells to the right activecell.offset(0,2)
And insert the value of the textbox (their ID).
For now, I can add the date and the Textbox ID.
Here what I have so far:
Sub CommandButton1_click()
Dim Input_ID As String, Date_in As String
Date_in = Format(Now, "DD-MMM")
ActiveCell.Value = Date_in
Input_ID = InputBox("SVP entré votre ID ", "Data Entry Form")
ActiveCell.Offset(0, 2) = Input_ID
End Sub
But is it possible to make that command/button only available for column "B?" Because I don't what them add a date and their ID to another Column.
PS: I More or less begin in VBA, I learn from a bit of everywhere, So if you could add some explanation in your code, i appreciate it. Thanks
Edit1: Post from comment
Sub Date_insert_click()
Dim Input_ID As String, Date_in As String
Dim ws As Worksheet
Set ws = ActiveSheet 'change to your actual worksheet
'Dim Date_in As Date
Date_in = Format(Now, "DD-MMM")
With ws.Range("B" & ws.Rows.Count).End(xlUp)
If .Row >= 4 Then .Offset(1, 0).Value = Date_in Else Exit Sub
Input_ID = InputBox("SVP entré votre ID ", "Data Entry Form")
If Input_ID <> "" Then .Offset(1, 1).Value = Input_ID Else .Offset(1, 0).Value = ""
End With
End Sub
But I found a weakness. If I select a cell anywhere down like K378,
I Still can add the value (date_In or value of the inputbox) but can't see it because the cell isn't active.
Try this as commented:
Sub CommandButton1_click()
Dim Input_ID As String, Date_in As String
Dim ws As Worksheet
Set ws = Thisworkbook.Sheets("Sheet1") 'change to your actual worksheet
Date_in = Format(Now, "DD-MMM")
With ws.Range("B" & ws.Rows.Count).End(xlUp)
If .Row >= 4 Then .Offset(1, 0).Value = Date_in Else Exit Sub
Input_ID = InputBox("SVP entré votre ID ", "Data Entry Form")
If Input_ID <> "" Then .Offset(1, 2).Value = Input_ID Else .Offset(1, 0).Value = ""
End With
End Sub
Edit1: Explanation as requested
Q: Why pass Worksheet object to a variable?
A: HERE is some explantion for this question. Also it makes your code a lot more readable and easy to debug.
Explaining the code:
'This line simply finds the last cell in Column B
With ws.Range("B" & ws.Rows.Count).End(xlUp)
'other code here
End With
Why use With? I used with because all the coding focuses on Column B and other data input is also reference to it. You can also see explanation on the advantages of using it in the link I provided above.
With ws.Range("B" & ws.Rows.Count).End(xlUp)
'Since we used With, you can directly access the Range properties
'The following line uses the Row and Offset property
'Row returns the row number of the range you're workning on
'Offset literally offets the range you are currently working on
If .Row >= 4 Then .Offset(1, 0).Value = Date_in Else Exit Sub
'This next line is already known to you, no need to explain
Input_ID = InputBox("SVP entré votre ID ", "Data Entry Form")
'Next line checks if Input_ID is supplied
'If yes, we use offset to get to the 2nd column from the current range
'If no, we delete the Date_In value
If Input_ID <> "" Then .Offset(1, 2).Value = Input_ID Else .Offset(1, 0).Value = ""
End With
I hope I explained it enough.
But if ever you still need more explanation, just comment it out.
If you encounter dificulties somewhere just post another question.