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.
Related
Attempting to write some vba but not having much luck. I have column A with a whole list of values that I am counting and looping through. For Each value in column A, there can be a match in range C:D. If a value in column A matches a value in column C. I want to insert the corresponding value in column D below the Column A value. I am not too certain on what my IF then statement should look like. I have my counter and loop... I am just unsure where to go with the middle portion of the code.
Sub SetListOrder()
Dim wp As Worksheet
Dim ef As Long
Set wp = Workbooks("Packing Slip FIXED").Worksheets("Locate Order")
ef = wp.Range("A" & Rows.Count).End(xlUp).Row
For i = 1 To ef
IF (UNSURE WHAT TO PLACE HERE!) THEN
Next i:
End Sub
Edit: adding sample data
Sample Data screenshot
In this example, I would like to insert a new row under the value in "A" where A=C. ie. Range in column "A" = Range in Column "C". I would like to then insert the value from "D". The new order in rows 4-6 would be:
Range
Order Group 1
2604291
I already have written the code to manually move my sheets around to follow the specific order once I am able to get the names in said order.
I agree with #BigBen that the simpler approach would be to insert a formula in column D that only replicates the column A value when a match is detected. Such a formula would probably look like the following -
=IF($A1=$C1,$A1,"")
This would be copied into cell D2 of your column and copied down as far as needed.
However, if you did want to achieve this with VBA and I have noted you used the word insert a value (as opposed to simple enter a value or copy & paste a value) then this could be your approach -
Sub SetListOrder()
Dim wp As Worksheet
Dim ef As Long
Dim i As Long
Set wp = Workbooks("Packing Slip FIXED").Worksheets("Locate Order")
ef = wp.Range("A" & Rows.Count).End(xlUp).Row
For i = ef To 1 Step -1
If wp.Range("A" & i).Value = wp.Range("C" & i).Value Then
wp.Range("D" & (i + 1)).Insert xlShiftDown
wp.Range("D" & (i + 1)).Value = wp.Range("A" & i).Value
Else
End If
Next i
End Sub
This approaches the problem in reverse by going up your column instead of going down. Note that by inserting your data, will cause each previous value to move down as well. If you don't want this, then simply erase the .Insert line and it will enter the value instead of inserting a cell.
Modify the below code and use:
Formula:
=IFNA(VLOOKUP(A1,$C$1:$D$5,2,0),"Missing")
VBA Code:
Option Explicit
Sub test()
Dim rngSearch As Range, rngFound As Range
Dim LastRowA As Long, LastRowC As Long, i As Long
With ThisWorkbook.Worksheets("Sheet1")
LastRowA = .Cells(.Rows.Count, "A").End(xlUp).Row
LastRowC = .Cells(.Rows.Count, "C").End(xlUp).Row
Set rngSearch = .Range("C1:D" & LastRowC)
For i = 1 To LastRowA
Set rngFound = rngSearch.Find(.Range("A" & i).Value, LookIn:=xlValues, Lookat:=xlWhole)
If Not rngFound Is Nothing Then
.Range("B" & i).Value = .Range("D" & rngFound.Row).Value
Else
.Range("B" & i).Value = "Missing"
End If
Next i
End With
End Sub
Result:
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
Here is a sample of the report I have:
Basically the report consists in a huge list of suppliers where among other things, I need to identify which of them have all entities (content groups) for the same country, while ignoring the "integrate" tag. Entities for each country are defined in a table separately (right).
So far I tried a combination of =SUMPRODUCT(--(ISNUMBER(SEARCH())) but always getting partially what I want.
In column C, in need:
to display YES if the supplier on that row has all entities for the mentioned country code;
to display NO otherwise;
My logic on this:
The formula/s needs to pick the country code from 1st table, then look into the 2nd table where entities are defined and check if all the entities in the content group are matching, ignoring "integrate" which is a default tag applied everywhere.
Expected result:
Try:
Option Explicit
Sub test()
Dim ws1 As Worksheet, ws2 As Worksheet
Dim LastRowA As Long, i As Long, y As Long
Dim arr As Variant
Dim CountryCode As String
Dim rng As Range, SearchRange As Range, FindPosition As Range
Dim Appears As Boolean
'Set worksheets on variables
With ThisWorkbook
Set ws1 = .Worksheets("Sheet1")
Set ws2 = .Worksheets("Sheet2")
End With
'Set the range to search in for country codes
Set SearchRange = ws2.Range("H1:R1")
With ws1
'Find the last row of Column A sheet1
LastRowA = .Cells(.Rows.Count, "A").End(xlUp).Row
'Start loop from row 2 to last row sheet1
For i = 2 To LastRowA
'Criteria needed ( Column A - Not empty cell, Column D - Includes "Europe" & Column E - Includes "No" Columns D and E are CASE SENSITIVE)
If .Range("A" & i).Value <> "" And .Range("D" & i).Value = "Europe" And .Range("E" & i).Value = "No" Then
CountryCode = .Range("B" & i).Value
'In which column the country code found
Set FindPosition = SearchRange.Find(What:=CountryCode, LookIn:=xlValues, LookAt:=xlWhole)
'If code excist
If Not FindPosition Is Nothing Then
'Set the range to search for the groups in the column where the code is header
Set rng = ws2.Range(ws2.Cells(2, FindPosition.Column), ws2.Cells(ws2.Cells(ws2.Rows.Count, FindPosition.Column).End(xlUp).Row, FindPosition.Column))
'Split the string with comma and assing it on arr
arr = Split(.Range("A" & i).Value)
Appears = False
'Loop the arr
For y = LBound(arr) To UBound(arr)
'Check if the arr(y) start from C as all code start from C
If Left(arr(y), 1) = "C" Then
'Count how many times the arr(y) with out the comma appears in the rng
If Application.WorksheetFunction.CountIf(rng, Replace(arr(y), ",", "")) > 0 Then
'If appears the variable Appears is true
Appears = True
Else
'If does not appear the variable Appears is False & Exit the loop
Appears = False
Exit For
End If
End If
Next y
'Check Appears variable status and import value in Column C
If Appears = True Then
.Range("C" & i).Value = "Yes"
Else
.Range("C" & i).Value = "No"
End If
'If code does not excist
Else: MsgBox "Country Code not does not excist."
End If
End If
Next i
End With
End Sub
If you have a version of Excel 2013+ which has the FILTERXML function, you can use this array formula:
=IF(OR(ISNA(MATCH(FILTERXML("<t><s>"&SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(A2,"Integrate",""),", ",","),",","</s><s>")&"</s></t>","//s"),INDIRECT("Table2["&B2&"]"),0))),"No","Yes")
We remove the Integrate
Create an XMLfrom the strings in Table1
Extract each element of the XML
Try to find them in the appropriate column of Table2
If we don't find one, then it has multiple countries.
Since this is an array formula, you need to "confirm" it by holding down ctrl + shift while hitting enter. If you do this correctly, Excel will place braces {...} around the formula as observed in the formula bar
If you have a version of Excel that does not have this function, and you are still interested in using excel formulas as opposed to VBA, there is another formula we can use.
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
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