Search a Dynamic Number of rows in Column A for a specific string in VBA - excel

I have a worksheet that contains a varying amount of Rows of data in Column A , within this worksheet I need to search for a specific string then copy the data contained in the Cell adjacent to it and paste into Column C, i.e if data was found in A2 then i need to copy the data from B2 and paste into C1. I can easily find and copy when the string appears once but the string will appear more than once 100% of time. here is when i run into issues.
The temporary code I have written for ease of understanding, searches the spreadsheet for the last Mention of A, get the row number, copy the B cell for that row number then pastes the value into C1.
I guess you need to use range variables for this but not 100% sure how to do it.
i have found no way to copy all mentions of A into a column, or ideally sum up the contents of the B cells. (I can do this, just long winded)
Ive placed my code below.
Sub ValueFinder()
Dim LastALocation As String
Dim ValueContent As String
LastALocation = Range("A:A").Find(What:="A", after:=Range("A1"), searchdirection:=xlPrevious).Row
ValueContent = Cells(LastALocation, 2)
Cells(1, 3) = ValueContent
End Sub
The spreadsheet that its using for more information, contains A,B,C on a loop in Column A and the odd numbers in Column B.
Thanks for any help your able to provide.
Mark

This will look for a string in Column A, and add to Column C the same row's B Column Value.
Sub find_move()
Dim foundCel As Range
Dim findStr As String, firstAddress As String
Dim i As Long
i = 1
findStr = "A"
Set foundCel = Range("A:A").Find(what:=findStr)
If Not foundCel Is Nothing Then
firstAddress = foundCel.Address
Do
Range("C" & i).Value = foundCel.Offset(0, 1).Value
Set foundCel = Range("A:A").FindNext(foundCel)
i = i + 1
Loop While Not foundCel Is Nothing And foundCel.Address <> firstAddress
End If
End Sub
Note: You should add the worksheet in front of all the range values, i.e. Sheets("Sheet1").Range("A:A").Find(...

Consider:
Sub LookingForA()
Dim s As String, rng As Range, WhichRows() As Long
Dim rFound As Range
ReDim WhichRows(1)
s = "A"
Set rng = Range("A:A")
Set rFound = rng.Find(What:=s, After:=rng(1))
WhichRows(1) = rFound.Row
Cells(1, 3) = Cells(rFound.Row, 2)
Do
Set rFound = rng.FindNext(After:=rFound)
If rFound.Row = WhichRows(1) Then Exit Do
ReDim Preserve WhichRows(UBound(WhichRows) + 1)
WhichRows(UBound(WhichRows)) = rFound.Row
Cells(Cells(Rows.Count, "C").End(xlUp).Row + 1, 3) = Cells(rFound.Row, 2)
Loop
End Sub
This code builds column C. It also builds an internal array of the row numbers in the event they are needed later.
EDIT#1:
To read about dynamic arrays:
Dynamic Arrays
or Google:
Excel VBA dynamic array

Related

I would like to select a range in a column only until a certain word/character is found

I need help in setting a range to a variable. Basically, I have a column with an "if" formula in it. The results in the formula returns either a "" or a number. Here is what I have currently. This selects all the rows since it "finds" the "" in the formula of the entire column.
Set rngEnd = wkLS.Columns(cLS).Find("").Offset(-1, 0)
How do I select only the range that contains numbers considering that the "" will only show up at the row after the last cell with a number?
Your way or questioning is not the clearer I could see...
Supposing that I understood what you want to ask, try this, please. The code may look more complicated than it should, because I do not know how cLS variable has been declared. If it would be a Long, the code would be simpler. But it can be a string "A:A"...
In case that you need the range from the first row of the column up to the first empty row (using Find):
Set rngEnd = wkLS.Range(wkLS.Columns(cLS).Cells(1, 1).Address, wkLS.Columns(cLS).Find("").Offset(-1, 0).Address)
Debug.Print rngEnd.Address
If you need the range from the first row of the column up to the last empty row:
Set rngEnd = wkLS.Range(wkLS.Columns(cLS).Cells(1, 1).Address, wkLS.Cells(wkLS.Cells(wkLS.Rows.count, wkLS.Columns(cLS).Column).End(xlUp).Row, 1).Address)
If you need a discontinuous range containing only the cells not being empty, up to the last empty row, but supposing that such empty cells exist, please use the next code:
Dim wkLS As Worksheet, rngEnd As Range, cLS As Long
Set wkLS = ActiveSheet: cLS = 1 'for A:A column, change for yours
Dim c As Range, finalRange As Range
Set rngEnd = wkLS.Range(wkLS.Columns(cLS).Cells(1, 1).Address, _
wkLS.Cells(wkLS.Cells(wkLS.Rows.count, wkLS.Columns(cLS).Column).End(xlUp).Row, 1).Address)
For Each c In rngEnd.Cells
If c.Value <> "" Then
If finalRange Is Nothing Then
Set finalRange = c
Else
Set finalRange = Union(finalRange, c)
End If
End If
Next
Debug.Print finalRange.Cells.count, finalRange.Address

Using variable as column reference to autosum said column VBA

I'm really new to VBA and have been working section by section on a number of pieces of code to format a worksheet (I've been doing it piece by piece so that I understand how each works, and using a final macro to Call all the macros into one long process).
Issue is sometimes the worksheets I work with are not exported with columns in the same order from month to month (out of my control), thus to autosum a particular column I have to Find the column header, then autosum that column, but this makes the column letter(or number) completely variable. I know how to work with rows as variables, but I'm stuck on column. I've been scouring forums to try and find a concise explanation, but to no avail, yet.
This code DOES work for column Y specifically, but I'm trying to figure out how to get it to use a variable for the column.
For example, I'm using a separate Macro called "FindInvoiceColumn" to select the 1st cell in the column that contains the string "invoice_amount", then I'd like to use something like I wrote below to set "ColumnAddress" as the column value of that cell. As far as I know .Column returns the column number, which is fine, but I'm assuming I'd have to use with Cells() instead of Range(), I just don't know how to get here.
(Part of the code also shows Adding the word "Total" to the left of the cell containing the autosum value, and making both bold).
Here's what I have so far:
Dim Rng As Range
Dim c As Range
Set Rng = Range("Y" & rows.Count).End(xlUp).Offset(1, 0)
Set c = Range("Y1").End(xlDown).Offset(1, 0)
c.Formula = "=SUM(" & Rng.Address(False, False) & ")"
'Selects next empty row of column X to add "Total" label for sum of column Y'
Range("X" & Cells.rows.Count).End(xlUp).Offset(1, 0).Select
ActiveCell.FormulaR1C1 = "Total"
'Bolds Total and the Sum of invoices'
Range("X" & Cells.rows.Count).End(xlUp).Select
Selection.Font.Bold = True
Range("Y" & Cells.rows.Count).End(xlUp).Select
Selection.Font.Bold = True```
'The below is what I'd like to use to find the dynamic value of the column.'
'Finds cell in row 1 that contains column header "invoice_amount" and selects it'
Call FindInvoiceColumn
'Dim ColumnAddress As Integer
ColumnAddress = ActiveCell.Column
You can use .Address to get a column reference, such that:
Sub test()
Dim varCol As String
varCol = Columns(ActiveCell.Column).Address
Debug.Print varCol 'OUTPUTS $A:$A when I had cells(1,1) selected
End Sub
In the above example, I chose a single cell to A) find it's column reference, via .Column, and B) found the .address of said column.
You could also perform the sum on a defined range using .cells() notation, rather than .range() notation.
Sub test2()
Dim rng As Range
Set rng = Range(Cells(1, 1), Cells(2, 1))
Cells(3, 1).Formula = "=sum(" & rng.Address & ")"
End Sub
The above code ouputs:
Specific to using the .cells() notation, you can make your column reference a variable, e.g.:
dim r as long, c as long
r = 1
c = 4
debug.print cells(r,c).address `should output $D$1 (untested)
You can choose r or c to fit your needs.
And as always... avoid select/activate where possible!!!
Edit
Adding use of last row via code since comments are terrible:
dim col as long
col = 25 'Y
With sheets("name")
dim lastRow as long
lastRow = .cells(.rows.count,col).end(xlup).row
Dim rng As Range
Set rng = .Range(.Cells(1, 1), .Cells(lastRow, col))
end with
This is exactly why I mentioned the specifics abotu the notation after that section (use of r and c as variables).
I've used this code to set a column number if your header is in a variable position
Dim F As Object
ColumnAddress = 0
With ActiveSheet.Rows(1)
Set F = .Find(What:="invoice_amount", LookAt:=xlWhole)
If F Is Nothing Then
MsgBox "This is not a proper file"
' other code
Else
ColumnAddress = F.Column
End If
End With
You would then use Cells() in place of range to do further work with the result of ColumnAddress. Also, ColumnAddress should dim as Long, to be accurate.

Grab first cell of data in row by column

Excel Data
The image is for the excel data I am playing around with. I will attach my code later. But I am trying to fill Column H with the first found cell of each row from Column A-E. Ex. for row 1 it should find "B" and place that to H, row 2 should have "c" place that to "H", and so on row 3 "is" to H, row 4 "a" to H.
I cannot for the life of me figure this out. VBA has never been my strongest suit and I have been playing around with this for 2 days now. Here is my code.
Function findValue() As String
Dim rng As Range
Dim row As Range
Dim cell As Range
Dim val As String
' Sets range of 5 columns to search in by column
Set rng = Range("A:E")
' searches through count of rows
For i = 2 To Range("A" & Rows.Count).End(xlUp).row
For Each cell In rng.Cells(i)
If IsEmpty(cell) = True Then
MsgBox cell
MsgBox i
Else
'MsgBox Range.(cell & i).Value
findValue = cell
Set rng = Range("A:E")
Exit For
End If
Next cell
Next i
End Function
Any Help is greatly appreciated.
The formula is:
=INDEX(A1:E1,AGGREGATE(15,6,COLUMN(A1:E1)/(A1:E1<>""),1))
If this is intended as a UDF, I believe that the following code is what you are after:
Function findValue() As String
Application.Volatile = True
Dim r As Long
Dim c As Long
r = Application.Caller.Row
For c = 1 To 5
If Not IsEmpty(Cells(r, c)) Then
findValue = Cells(r, c).Value
Exit Function
End If
Next
findValue = ""
End Function
An alternative method, where you pass the range to be checked rather than just checking the current row, would be:
Function findValue(rng As Range) As String
Dim c As Range
For Each c In rng
If Not IsEmpty(c) Then
findValue = c.Value
Exit Function
End If
Next
findValue = ""
End Function
This could then be used in cell H2 as =findvalue(A2:E2), and has the advantage that it does not need to be marked Volatile. ("Volatile" functions have to be recalculated every time anything at all changes on the worksheet.)
P.S. I strongly suggest that you use an Excel formula instead (such as the one in Scott's answer) - why reinvent the wheel when Excel already provides the functionality?
I'm not by my PC so can't test it, but you could try this
Sub FindValue()
Dim myRow As Range
' Sets Range of 5 columns to search in by column
Set rng = Intersect(Range("A:E"),ActiveSheet.UsedRange)
' searches through count of rows
For each myRow in rng.Rows
Cells(myRow.Row, "H").Value = myRow.Cells.SpecialCells(xlCellTypeConstants).Cells(1)
Next
End Sub

Excel VBA: Insert values from sheet 1 to sheet 2 if value in column matches

I'm a total newbie in VBA, just started this morning when confronted with a spreadsheet with ~30K rows.
I have two worksheets:
named "tohere", contains ordinal numbers in column C.
named "fromhere", contains numbers in column C and values in column B. It's basically the same ordinal numbers, but some are missing - that's why I started to write a macro in he first place.
I want Excel to check if the number in "tohere", Cell C1 exists in any cell in "fromhere", column C, and if it does, copy the value from the corresponding row in "fromhere", column B into "tohere", Cell B1; then do it again for C2 etc. If there's no such number in sheet "fromhere", just do nothing about this row.
I tried this code:
Dim i As Long
Dim tohere As Worksheet
Dim fromhere As Worksheet
Set tohere = ThisWorkbook.Worksheets("tohere")
Set fromhere = ThisWorkbook.Worksheets("fromhere")
For i = 1 To 100
If fromhere.Range("C" & i).Value <> tohere.Range("C" & i).Value Then
Else: fromhere.Cells(i, "B").Copy tohere.Cells(i, "B")
End If
Next i
It does what I want for the first cells that are equal (4 in my case) and then just stops without looking further.
I tried using Cells(i, "C") instead, same thing. Using i = i + 1 after Then doesn't help.
I feel that the problem is in my cells addressing, but I don't understand how to fix it.
This is how my sample "fromhere" list looks like (you can notice some numbers are missing from the C column):
This is the sample of what I get with the "tohere" list:
It gets to the point where there's no "5" in "fromhere" and stops at this point.
P.S.: i = 1 To 100 is just to test it.
This should do your job. Run this and let me know.
Sub test()
Dim tohere As Worksheet
Dim fromhere As Worksheet
Dim rngTohere As Range
Dim rngfromHere As Range
Dim rngCelTohere As Range
Dim rngCelfromhere As Range
'Set Workbook
Set tohere = ThisWorkbook.Worksheets("tohere")
Set fromhere = ThisWorkbook.Worksheets("fromhere")
'Set Column
Set rngTohere = tohere.Columns("C")
Set rngfromHere = fromhere.Columns("C")
'Loop through each cell in Column C
For Each rngCelTohere In rngTohere.Cells
If Trim(rngCelTohere) <> "" Then
For Each rngCelfromhere In rngfromHere.Cells
If UCase(Trim(rngCelTohere)) = UCase(Trim(rngCelfromhere)) Then
rngCelTohere.Offset(, -1) = rngCelfromhere.Offset(, -1)
Exit For
End If
Next rngCelfromhere
End If
Next rngCelTohere
Set tohere = Nothing
Set fromhere = Nothing
Set rngTohere = Nothing
Set rngfromHere = Nothing
Set rngCelTohere = Nothing
Set rngCelfromhere = Nothing
End Sub

I can't get my VBA Excel Macro to stop at the end of the row

I have a row of data that changes once a month but it only changes roughly 30 cells out of 90 and every month they are different so I am trying to make a Macro to automate it.
The Macro looks at Cells A2 - B98 and searches for information that matches the Values of H2-I98 and if the values in A match H then it copies what the value is in I and replaces it in B but it doest stop at the end of the row i.e. at row 98 it loops infinatly. So I was hoping someone could find my error so that it wont loop for ever. Thanks
Sub Update_Holiday()
Dim Search As String
Dim Replacement As String
Dim rngTmp As Range
Dim rngSearch As Range
LastInputRow = Range("A65536").End(xlUp).Row
Set rngSearch = Worksheets("Holiday").Range(Cells(2, 1), Cells(98, 2))
For k = 2 to 98
Search = Worksheets("Holiday").Cells(k, 8)
Replacement = Worksheets("Holiday").Cells(k, 9)
With rngSearch
Set rngTmp = .Find(Search, LookIn:=xlValues)
If rngTmp Is Nothing Then
GoTo Go_to_next_input_row:
Else
Worksheets("Holiday").Cells(rngTmp.Row, rngTmp.Column + 1).Value = Replacement
End If
End With
Go_to_next_input_row:
Next K
End Sub
If I understand your question correctly: for each Cell in H2:H98, you're looking for a match in A2:A98. It won't necessarily be on the same row. If you find a match in Column A, you want to take the value from Column B and put it in Column I on the same row as the search value we just looked for. In this case, this code will work:
Option Explicit
Sub Test()
Dim ws As Worksheet
Dim srcRng As Range '' Source range
Dim schRng As Range '' Search range
Dim c As Range
Dim search As Range
Set ws = ThisWorkbook.Sheets(1)
Set srcRng = ws.Range("H2:H98")
Set schRng = ws.Range("A2:A98")
For Each c In srcRng '' Iterate through your source range
Set search = schRng.Find(c.Value, LookIn:=xlValues, SearchDirection:=xlNext) '' Find the value from column H in column A
If Not search Is Nothing Then
c.Offset(, 1).Copy search.Offset(, 1) '' Get the value from column B, from the same row as the value it found in column A
'' Then paste that value in column I, on the same row as the value we searched for from column H
End If
Next c
GoTo statements are generally (generally, not always) very, very bad practice. Especially in this kind of situation. You don't need them, it just makes your code convoluted.

Resources