VBA Match Against a Column With Duplicate Values - vba

I have an excel range I'm using as a database. The first column is sorted in ascending order by date, but there can be multiple records with the same date, e.g.
A B
1 15-Apr-2015 Carrot
2 15-Apr-2015 Yamagobo
3 16-Apr-2015 Turnip
4 17-Apr-2015 Parsnip
5 17-Apr-2015 Rutabaga
6 17-Apr-2015 Radish
7 18-Apr-2015 Daikon
(The stuff in column A are formatted dates, not text strings.)
What I'd like to get is the first record that has a date greater than or equal to the one I'm searching for, so I have:
Dim searchDate As Date
searchDate = CDate("4/17/15")
Dim searchRange As Range
Set dbRange = Worksheets("My DB").UsedRange
Dim v As Variant
v = Application.Match(CLng(searchDate), dbRange.columns(1), 1)
I expect v to now contain the index of the first row with 17-Apr-2015 in column A, 4. Instead, it seems to be returning the last row with 17-Apr-2015, 6.
Two questions:
1) Is there a way to use Match() (or any other function) to point at the first entry that matches, instead of the last?
2) Match() freaks out if I give it a Date type as its match parameter (hence the CLng() conversion). Is this to be expected, or am I doing something stupid?
Thanks.

Try to use Find instead of Match
To get the cell:
set MyRange = dbRange.Columns(1).Find(searchDate)
To get just the row:
MyRow= dbRange.Columns(1).Find(searchDate).row

Related

Get integer data from word tables

I have several word tables that I want to edit based on the value in the last row of the column. I want to delete column contents (except last row of specified column) if the value in the last row exceeds 20.
I have used . Range. Textbut I am having challenges with its implementation. I have this
For i = 3 To . Columns. Count
If ActiveDocument.Tables(i).Cell(ActiveDocument. Tables(i).Rows.Count, i).Range.Text >20 Then ...
How can I get VBA to return the contents of a cell not as a string but as an integer for calculation.
VBA's VAL() function will convert a string containing a numeric value into a number. The function starts from the left and recognises numbers until a non-numeric character is encountered and ignores everything that follows. In the case of text taken from a table cell that is very useful because the End-Of-Cell marker included in the text will be ignored.
Dim C As Long
For C = 1 To 3
With ActiveDocument.Tables(1)
If Val(.Rows.Last.Cells(C).Range.Text) > 20 Then
Debug.Print "Delete rows above"
End If
End With
Next C

VBA-Excel return the latest date

I am a newbie to using VBA to program functions in Excel (just started this morning actually). Here is my problem including my almost but non-working solution.
Problem outline:
I have 2 columns of data, a userID and a Date column. I want to create a function that returns the maximum date (latest) for a userID. A userID may appear once, or many times (number varies from user to user, but doesn't exceed 20).
Now to make this problem a easier for my little brain, I pre-sort the userIDs to appear one after another, and then created a third column to mark the first instance of a userID (not necessarily the one with the earliest/latest date) using a simple excel If statement: If its the first instance it is the userID, else it is empty.
Then I created a function that takes in a range of cells (userIDs), and a single other value (the third column cell containing a single value of userID)
A typical excel worksheet will look like this:
userID Date First Instance
1 3/3/12 1
1 3/2/11
1 2/3/14
2 2
2 3/4/15
2
2 5/6/15
3 5/5/16 3
4 4/4/14 4
4
4 6/7/08
...
I wrote the function below, which takes in userID as the iRange (I select more than 20 so that for other users which may have 20 instances of userID are also included), and idCell as the single identifier of the first instance of userId.
The logic behind the code is:
Loop through iRange and compare it to idCell
If it's equal, check that the next cell (next to the element from iRange is a date) and if it is
Check whether it is larger than the maxDate, store it if true
At the end I return maxDate
The result:
I get a number back, but it isn't a date and I don't think it has any bearing on a date.
Function returnDate2(iRange As Range, idCell As Range) As Date
Dim elem As Variant
Dim i As Long
Dim maxDate As Date
For Each elem In iRange
If elem = idCell Then
If IsDate(elem.Offset(0, 1)) Then
If elem.Offset(0, 1) > maxDate Then
maxDate = DateValue(elem.Offset(0, 1))
End If
returnDate2 = maxDate
End If
End If
Next elem
End Function
Thanks in advance!

How to Find the Length of a Column in VBA with a Variable Last Row

I'm a beginner to vba and hope to use it in order to automate a process. I have a computer program that provides me an excel spreadsheet, where the amount of data in the columns can change. For example, I could have a column of length 9 one time:
1
3
2
4
24
23
432
55
2
Or a column of length 5 (note: actual column sizes are usually in the thousands):
1
2
3
4
8
I want to write code that will grab this column, not knowing how long the column will be. Something like:
Dim list1 As Array
'I don't know how to find the upper bound
list1 = Range("A1:A" & Upper Bound).Value
Any help is appreciated. Thanks!
If you are working with constant values in these columns and are working with larger amounts of cells in these columns, it could be useful to use the count function:
dim x As Long
x = Worksheets(*sheet name*).Range(*column range*).Cells.SpecialCells(xlCellTypeConstants).Count
x is being passed the number of non-empty cells in your column. So you may want to be careful in your column range not to include any headers, if applicable.
I believe this will get you the last row. You might need to be careful if you're looking to count all values (e.g. the first couple rows are blank).
Dim lastRow As Long
lastRow = Cells(Rows.Count, "A").End(xlUp).Row
To get the data from column A into a 2d variant array, use the following:
Dim vA As Variant
vA = [a1:index(a:a,counta(a:a))]
This assumes that there are no blank cells within the data, and that the data starts in row 1.

Visual Basic in Excel - WorksheetFunction.max

What is the behavior of this function to be when the values being compared are equal?
If
Cell1 = 10
Cell2 = 11
Debug.Print WorksheetFunction.max(Cell1,Cell2)
Will return Cell2. But if
Cell1 = 10
Cell2 = 10
It returns Cell1.
What am I missing here?
UPDATE:
I have a label at Offset(0,-3)
If one value is greater then I get the appropriate label.
When the values are equal I get on label in particular.
Since I have not defined a consequence when the vales are equal, I wonder what I am missing
about the behavior of this function such that it will choose one value over the other when they are equal.
How much more coding should I assume is necessary?
Function testWinner(rng As Range) As String
testWinner = WorksheetFunction.Index(rng, WorksheetFunction.Match(WorksheetFunction.max(rng), rng, 0)).Offset(0, -3)
End Function
It's nothing to do with Max (now that you've edited your question). Max simply returns the value (since they are all the same) and your Match function is looking for an exact match so it scans through the range until it finds the first match then that position is passed to Index.
WorksheetFunction.Max does not return a cell/range. It returns the largest value in a set of value.
By the way: in case of when both values are equal (10,10), the result of Min, Max and Average function will be the same: 10.
If by saying the behaviour of function you mean how it works, it sorts data and gets the highest value. So, in case of set of value: {10, 10, 8}
Max(10,10,8)** = 10
Max.K({10,10,8},2) = 10 'where K = 2, which means the second highest value
Max.K({10,10,8},3) = 8 'where K = 3, which means the third highest value
On the other hand...
row/column A B
1 10 =RANK(A$1:A$3,A1) 'returns 1
2 10 =RANK(A$1:A$3,A2) 'returns 1
3 8 =RANK(A$1:A$3,A3) 'returns 3
Is it more understandable, now?
In your updated example the following happens in your testWinner function:
The Max value is determined: in example one this is 15 (in example 2 it's 14). Note that only the value but not a reference to any cell is returned
The MATCH function searches through through D2:D3 for the value 15 (14). It does so top down and return the first exact match. I.e. in the first example, this results in 1 (in the second example the result is 2)
INDEX will return the 2nd element of D2:D3 (the 1st in the second example), i.e. cell D2 (D3 in ex. 2)
OFFSET returns the cell three to the left
Thus, the answer to your question is not the return of the MAX function - but the MATCH function - which goes top to bottom until it finds the first element matching your criteria, in your case the max.
Independent of this: why do you use a User-Defined-Function for this? If you'd build this with the normal Excel functions, performance would be higher - and non-VBA users could still understand it. Using =INDEX($A$2:$A$3,MATCH(MAX($D$2:$D$3),$D$2:$D$3,0)) you would save the OFFSET - and make it less error prone, as you can now insert columns between A and D without breaking your model!

Excel VBA: Finding the last row of a column full of duplicates, but NOT the end of the entire column

I have a large datasheet, where each row has a column that gives it a unique identifier, like so:
Row Identifier Value
1 A 3
2 A 4
3 A 7
4 B 1
5 B 3
6 B 0
7 C 9
I want to be able to find the row number of the last duplicated identifier in a range. So, for example, if the identifier "B" was some unknown number of rows long, and I wanted to find the number of the last row that had the column identifier "B," how would I do so?
So far I've come up with code to select the row of the FIRST identifier:
' Select first row where first instance of <B> exists:
With ActiveWorkbook.ActiveSheet
Set First_B_Row = .Range("A:A").Find(What:="B", LookIn:=xlValues)
End With
Dim First_B_RowNumber As Long
First_B_RowNumber = First_B_Row.Row
ActiveWorkbook.ActiveSheet.Rows(First_B_RowNumber).Select
But, I want to be able to find the row number of the LAST identifier in a column full of them. Any ideas?
You are absolutely close. Instead of searching top to bottom, then search in reverse like this:
With ActiveWorkbook.ActiveSheet
Dim Last_B_RowNumber As Long
Last_B_RowNumber = .Range("A:A").Find(What:="B", After:=[A1], _
SearchDirection:=xlPrevious).Row
End With
Above code will find the last occurrence of B in Column A and assigns it's row number to a variable.
Your just missing the SearchDirection argument.