Getting value of specific column for each row - excel

I need to write script which allows me import data in xml format. It generates block of code I need for every order except variables. I need to go through all rows in range and get values from column "G". Problem is it gets only value of first row in range.
Dim rng As Range, row As Range
Set rng = Range(Range("G11"), Range("G11").End(xlDown))
For Each row In rng.Rows
Dim partner_id As String
partner_id = Cells(rng.row, 7).Value
line9 = "<typ:id>" & partner_id & "</typ:id>" & vbNewLine
...
I'm not a programmer as you can see, but I really need to get that partner_id. I would appreciate any suggestions.

You really should avoid using variable names which are the same as the common Methods used by Excel objects so, for instance, change row to be myRow perhaps. That would then highlight what I think was causing your issue - the use of rng.row which, with a renamed variable, you would probably have written as rng.myRow - which would then give a compile error.
rng.row is simply returning the row number of the first cell in rng, and has nothing to do with your variable called row.
Dim rng As Range, myRow As Range
Set rng = Range(Range("G11"), Range("G11").End(xlDown))
'There's no need to use "rng.Rows" when each row is a single cell anyway
For Each myRow In rng
Dim partner_id As String
'because myRow is a single cell, we can just use its Value
partner_id = myRow.Value
line9 = "<typ:id>" & partner_id & "</typ:id>" & vbNewLine
...
FWIW - leaving your variable names and coding style unchanged, you could have fixed the code by using row.Row instead of rng.Row, i.e.
Dim rng As Range, row As Range
Set rng = Range(Range("G11"), Range("G11").End(xlDown))
For Each row In rng.Rows
Dim partner_id As String
partner_id = Cells(row.Row, 7).Value
line9 = "<typ:id>" & partner_id & "</typ:id>" & vbNewLine
...

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.

Finding if a cell values (delimited by comma) are all existing in a defined table

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.

Sum based on rows.count

I am trying to add up the numbers that are in a column, but the column of numbers varies so I'm using the Rows.Count instead of a set number of rows such as "a1:a5". Each time I run this I get the row count (represented as rngcount) but instead of the sum of the cells within the row count (rngcount) I get the row count again.
I was getting an object error (1004) until I added "a" to .Range("a" & rngcount) but I admit I'm not sure why the "a" is needed as I thought the rngcountwould be all that is needed to Sum.
So Unfortunately two issues in one post.
Sub simpleSUM()
Dim rng1 As Range
Dim rng2 As Range
Dim rngcount As Integer
Set rng1 = Range("b1") 'This indicates how many cells are in use
Set rng2 = Range("b2") 'This indicates the sum of the cells that are in use
rngcount = cells(Rows.Count, "A").End(xlUp).row
rng1.Value = rngcount
rng2.Value = Application.WorksheetFunction.Sum(ThisWorkbook.Sheets("sheet2").Range("a" & rngcount))
End Sub
Your code now only counts the last row. The variable rngcount returns the last rownumber in the range, instead of the whole range.
It should work if you use
rng2.Value = Application.WorksheetFunction.Sum(Thisworkbook.Sheets("sheet2").Range("A1:A" & rngcount))

.Find method not searching in order

Can anyone explain why the string in D2 is being built out of order in the first loop?
This is only happening for the first search value, Dom. The rest of the strings are being built in the order in which they appear (see Column B). I treid adding SearchDirection:= xlNext but the output remained the same with or without that bit of code.
In the photo, Column A:B are the raw data and Column C:D are the output from macro.
The cell in question is D2. It should show USD/EUR/GBP instead of EUR/GBP/USD
Dim ws As Worksheet: Set ws = ThisWorkbook.Sheets("Sheet1")
Dim FoundName As Range, SearchRange As Range, Names As Range, Name As Range
Dim MyString As String, i As Long
ws.Range("A1:A" & ws.Range("A" & ws.Rows.Count).End(xlUp).Row).AdvancedFilter Action:=xlFilterCopy, CopyToRange:=ws.Range("C1"), Unique:=True
Set SearchRange = ws.Range("A2:A" & ws.Range("A" & ws.Rows.Count).End(xlUp).Row)
Set Names = ws.Range("C2:C" & ws.Range("C" & ws.Rows.Count).End(xlUp).Row)
For Each Name In Names
Set FoundName = SearchRange.Find(Name, SearchDirection:=xlNext)
For i = 1 To Application.WorksheetFunction.CountIf(SearchRange, Name)
MyString = MyString & FoundName.Offset(, 1) & "/"
Set FoundName = SearchRange.FindNext(FoundName)
Next i
Name.Offset(, 1) = Left(MyString, Len(MyString) - 1)
MyString = ""
Next Name
According to Microsoft documentation about the Range.Find method, the After parameter is:
The cell after which you want the search to begin. This corresponds to the position of the active cell when a search is done from the user interface. Notice that After must be a single cell in the range. Remember that the search begins after this cell; the specified cell isn't searched until the method wraps back around to this cell. If you do no specify this argument, the search starts after the cell in the upper-left corner of the range.
(Emphasis mine)
In your code, you set the range you're searching like:
Set SearchRange = ws.Range("A2:A" & ws.Range("A" & ws.Rows.Count).End(xlUp).Row)
which means that the first cell actually searched will be Range("A3"). There are two ways of fixing this:
Expand the search range to include "A1", so the default start is "A2"
Specify the After parameter as the last cell in the range. Since the search wraps back around to the first cell after reaching the last cell.
In your scenario, I believe the simplest solution would be (1). This can be done by simply adjusting your code line to read:
Set SearchRange = ws.Range("A1:A" & ws.Range("A" & ws.Rows.Count).End(xlUp).Row)

Resources