Im pulling data from 2 tables, one works as i expected. but the other one, same code, wont work. I just dont get it. Everything seems to be identical.
I've isolated the "find" and they return the correct line numbers on both tables.
The problem is that the loCustomerTable doesnt seem to have anything in the .value
Debug.print does a line feed, thats it.
no errors generated.
Why? What am I missing?
Dim rProjectInfo As range
Dim rCustomerInfo As range
Dim loCustomerTable As ListObject
Dim loProjectTable As ListObject
Set loProjectTable = Worksheets("projektinformation").ListObjects("tblProjektinformation")
Set loCustomerTable = Worksheets("kundinfo").ListObjects("tblKundInformation")
sProjectNumber = "20-130"
' read project information
Set rProjectInfo = loProjectTable.range.rows((loProjectTable.range.Columns(1).Find(sProjectNumber).row))
' get customer info base off project info.
Set rCustomerInfo = loCustomerTable.range.rows((loCustomerTable.range.Columns(2).Find("gateau").row))
Debug.Print rProjectInfo.Cells(, 1).Value
Debug.Print rCustomerInfo.Cells(, 2).Value
I guess you are a victim of your monster statements (I don't get it why such statements are not split into smaller pieces so that you can easily debug it).
So let us split the statement
Set rCustomerInfo = _
loCustomerTable.range.rows((loCustomerTable.range.Columns(2).Find("gateau").row))
First thing you do is fetch the table column
dim tabCol as Range
set tabCol = loCustomerTable.range.Columns(2)
Debug.Print tabCol.Address
Check if the address is correct (should by). Now you search for your string ("gateau") in that column
dim rGateau as Range, row as long
set rGateau = tabCol.Find("gateau")
row = rGateau.Row
Debug.Print rGateau.Address, rGateau.Value2, row
That gives you the address of the cell with the search string (code assumes that this string can always be found), and its row number.
Now what you will see it that the row number is the row within the sheet, not within the table (listObject) - but you use it as row number within your table. If the table does not start at row 1, you are now reading data from the wrong row (which might even be below the table).
What you can do it either consider the start of your table or intersect the table with the row where you found the name.
' Variant 1: Calculate the row number within the table
row = rGateau.Row - loCustomerTable.Range.Row + 1
Set rCustomerInfo = loCustomerTable.Range.Rows(row)
' Variant 2: Intersect the (sheet) row with the table
row = rGateau.Row
Set rCustomerInfo = Intersect(loCustomerTable.Parent.Rows(row), loCustomerTable.Range)
Debug.Print rCustomerInfo.address
Related
I am new to vba and having a little trouble figuring this out. Don't really know where to start
I have two tables. I'm simply just trying to find code to say that if the last row in table 1 of worksheet 1 has any value in the 2nd column and the value in the last column is greater than 1 then take the value in the last column of the last row and add that many rows to table 2 of worksheet 2.
I tried searching other posts for this same type of topic but couldn't find any. Any help would be greatly appreciated.
Try adding this to a standard module
You could validate if the last column last row value is a number. I'm assuming that it is.
Code:
Public Sub AddRowsToTable()
Dim sourceTable As ListObject
Dim targetTable As ListObject
Set sourceTable = Range("Table1").ListObject
Set targetTable = Range("Table2").ListObject
Dim valueSecondCol As Variant
Dim valueLastRowLastCol As Long
' Get last row, second column value
valueSecondCol = sourceTable.ListColumns(2).DataBodyRange.Cells(sourceTable.ListRows.Count).Value
' Get last row, last column value
valueLastRowLastCol = sourceTable.ListColumns(sourceTable.ListColumns.Count).DataBodyRange.Cells(sourceTable.ListRows.Count).Value
' Exit if it's null
If valueSecondCol = vbNullString Then Exit Sub
' Add as many rows as the number in last column, last row of source table to target table
targetTable.Resize targetTable.HeaderRowRange.Resize(targetTable.ListRows.Count + valueLastRowLastCol + 1)
End Sub
Let me know if it works.
I am not sure what i am doing wrong. I am wanting to code to look at my named ranged for a specific value. Then go compare to the last cell in a specific column. Then take the first character and see what number that is. So my named range for an example would be 1 and the last cell in my specific column would be 1.02. I want it to pull the first character from the left and see if they are the same. However, ever time I run the code I keep returning the false if statement portion.
Dim TimeStamp As Range
Dim LastItem As Range
Dim LastItemNumber As Range
Set LastItemNumber = Range("C500").End(xlUp).End(xlUp)
Set TimeStamp = Range("C500").End(xlUp).Offset(1, -1)
Set LastItem = Range("C500").End(xlUp)
LastItemNumber.ListObject.ListRows.Add AlwaysInsert:=True
TimeStamp = Now
If Range("Meeting_Number") = Left(LastItemNumber, 1) Then
MsgBox ("True")
LastItemNumber.FormulaR1C1 = "1.02"
Else
MsgBox ("False")
End If
I am expecting the result to populate the message box and say true. Then input "1.02" into the last cell in that column.
Once I have identified a cell of interest via a for loop, how can I then make a range out of the entire table row that contains said cell of interest?
I need help with only this small part of my larger code.
'TransColumn is a table column in which I am looking for the phrase "NPD".
'TransCell is my cell of interest, containing the phrase "NPD".
'I want Trans_Queue_Row to be the table row in which TransCell is located.
For Each TransCell In TransColumn
If InStr(1, TransCell.Value, "NPD") > 0 Then
Dim Trans_Queue_Row As Range
Set Trans_Queue_Row = ThisWorkbook.Sheets("Project Queue").ListObjects("TableQueue").ListRows
'I know this looks like a weird way to achieve what I'm asking for, but I'm using InStr to support some other elements of my code not displayed here.
I want a variable (i.e. - Trans_Queue_Row) to identify the entire table row that contains TransCell.
Dim TableQueue as ListObject, Trans_Queue_Row As Range, i as Long
Set TableQueue = ThisWorkbook.Sheets("Project Queue").ListObjects("TableQueue")
With TransColumn.DataBodyRange
For i = 1 To .Count
If InStr(1, .Rows(i).Value, "NPD") > 0 Then
Set Trans_Queue_Row = TableQueue.DataBodyRange.Rows(i)
End If
Next i
End With
From my answer to your previous question:
Trans_new_NPD_row.Range.Value = _
Application.Intersect(TransCell.EntireRow, QueueTable.DataBodyRange).Value
You can use Intersect to find the range common to TransCell.EntireRow and the data section of the table/listobject.
I'm new to the world of VBA and can't quite wrap my head around this macro I want to create.
Essentially, I have a monthly data set that comes in, but the data is imperfect. I often need to clear excess data values of one cell, based on the value of another.
The complex part is that the data will be displaced week to week, and the only thing that is static are the column headers that are included.
Just as an example, columns A thru E have headers Company1, Company2, Company3, etc.
Columns Q thru U have headers Product1, Product2, Product3, etc.
The product columns will contain the company names as values (often more than one, delimited by commas), and if the name of a company doesn't appear for ANY of the product columns, the cell of the same row for that company's column should be cleared.
So if Q4:U4 doesn't contain "Product1" as as value, the value at A4 (product 1 column, row 4) should be cleared.
Any insight on how to go about this would be much appreciated!
edit
Screenshot of example data:
Example 2
Try this. Create a new module within the VBA editor and copy in the below code ...
Public Sub ProcessData()
Dim objCompanyRange As Range, objProductRange As Range, objCompanyCell As Range
Dim strCompany As String, objThisProductRange As Range, rngFrom As Range
Dim rngTo As Range, objFindResult As Range, lngLastRow As Long
On Error Resume Next
' Get the range for the company data.
Set objCompanyRange = Application.InputBox("Please select the COMPANY data range, including headers ...", "Company Data", , , , , , 8)
If Err.Description <> "" Then Exit Sub
' Get the range for the product data.
Set objProductRange = Application.InputBox("Please select the PRODUCT data range, including headers ...", "Product Data", , , , , , 8)
If Err.Description <> "" Then Exit Sub
On Error GoTo 0
For Each objCompanyCell In objCompanyRange
' We want the headers in the range but want to skip processing the first row.
If objCompanyCell.Row > objCompanyRange.Cells(1, 1).Row Then
' This is the only contentious line for me. If your headers are specified as you had in your
' example, i.e. "Group: Company1" then the below will work. If that was a mocked example that
' was not 100% accurate, the below line will need to change. It is currently splitting the header
' by a colon and only storing the right hand side as the company.
strCompany = Trim(Split(objCompanyRange.Cells(1, objCompanyCell.Column).Text, ":")(1))
' Only reset objThisProductRange if the row has changed, otherwise we use the same set of
' products we used last time.
If objCompanyCell.Row <> lngLastRow Then
' Determine the range for the product data given the current row being processed
With objProductRange.Worksheet
Set rngFrom = .Range(.Cells(objCompanyCell.Row, objProductRange.Cells(1, 1).Column).Address)
Set rngTo = rngFrom.Offset(0, objProductRange.Columns.Count - 1)
End With
Set objThisProductRange = Range(rngFrom.Address & ":" & rngTo.Address)
End If
' Find the company name within the current row of Product data.
Set objFindResult = objThisProductRange.Find(strCompany, MatchCase:=False)
' Clear the cell if nothing was found.
If objFindResult Is Nothing Then
objCompanyCell.ClearContents
End If
End If
lngLastRow = objCompanyCell.Row
Next
End Sub
... now watch the animated GIF below to see how you launch it and the resulting output.
If selecting the datasets each time gives you the sh!ts then feel free to hardcode it or use your own determination method. It's the easiest approach given I don't know how you may want to do that.
Here's hoping it's what you're after. Be sure to read the comments in the code in case you have any questions.
In a Worksheet_Change event routine, the target value is not of a specific cell but that of the range address of the pivot table that cell belongs to.
The code below is intended to detect if a certain named cell was created. If it wasn't then no action is needed. But if it was, then the value of that named cell will point to one less than the column of the cell that I want to detect changed, prompting a certain action.
I expect the change to occur in the value of a Report Filter field of a pivot table, which I fully expect to be in row 4, but at a column that is 1 more than the value of the dynamically-created named cell "cLeft" I checked the existence of.
I detect the change via a change event. However, instead of giving the address of the one cell that changed, I get the range address of the full pivot table. How can I get only the address of the cell that changed?
Private Sub Worksheet_Change(ByVal Target As Range)
Dim Pos As String
Dim rNW As Long
Dim cNW As Long
Dim rSE As Long
Dim cSE As Long
On Error Resume Next
Pos = Range("cLeft").Address
If Err <> 0 Then
On Error GoTo 0
Else
Call GetCorners(Target, rNW, cNW, rSE, cSE)
If rNW = 4 And cNW = Range("cLeft") + 1 Then
.
.
<some action>
.
.
End If
End If
End Sub
Sub GetCorners(Rng As Range, rNW As Long, cNW As Long, rSE As Long, cSE As Long)
' Decode the upper left and lower right of the range into appropriate row and column values.
Dim ArR() As String
Dim ANW() As String
Dim ASE() As String
Dim RngStr As String
RngStr = Rng.Address(ReferenceStyle:=xlR1C1)
' See if the range is a single cell. If it is, replicate it at the end.
If InStr(RngStr, ":") = 0 Then RngStr = RngStr & ":" & RngStr
ArR = Split(RngStr, ":")
ANW = Split(ArR(0), "R")
ANW = Split(ANW(1), "C")
ASE = Split(ArR(1), "R")
ASE = Split(ASE(1), "C")
rNW = Val(ANW(0))
cNW = Val(ANW(1))
rSE = Val(ASE(0))
cSE = Val(ASE(1))
End Sub
I have a pivot table whose "Northwest" corner is always in row 4. Its column number can vary. In my example it is column 101. At the moment of creation of the pivot table I also create a named cell called "cLeft". In it I then record the number of the leftmost column of the pivot table, 101.
The pivot table has only one report filter for a field called "Status". The position of the filter is always in row 4 and column number equal to 1 + the value of cLeft. In the example, it is row 4, column 102.
I want to perform some additional action when the value of the filter changes. For that, I use the above code, but it doesn't work all that well because the value of "target" is not the address of the one cell that changes, R4C102, but the address of the whole pivot table, R4C101:R29C117, which also changes, depending on the selection.
How can I coax the address R4C102 out of the routine? I don't want to use Worksheet_SelectionChange because, although that gives me the answer I want, it requires me to select the cell holding the filter first, and then change the filter, a 2-step sequence I can't expect the end users to remember to do.