VBA - Issues with show detail for Pivot table - excel

Sorry if there is a basic answer to this but I've been stumped on this for a while. I have a pivot table which I want to show the detail of each cell in the second to last row between column D and the second to last column. The formula I wrote to do this isn't doing anything and I am not 100% sure why (I know I wrote it to do a different range just wanted to get something simpler working). It could be that there is a problem with me defining the last column or I could just be completely on the wrong page with this. Ideally each new page it would create would be named as the value of cell I2 on that page as well. Please find my code below and thanks for your help.
'Define LastColumn
LastColumn = sht.Cells(2, .Columns.Count).End(xlToLeft).Column
'Define last row
LastRow = sht.Cells(sht.Rows.Count, "A").End(xlUp).Row
For i = "D" To LastColumn
Range(LastRow & i).Select
With Selection
Selection.ShowDetail = True
End With
Next i

Related

Replicating values

Need a little help here.
In the "Data" Tab I want to copy values in column "c2:c1000" and paste in column "a1" of another Tab.
This is what i have so far,
Dim x As Long
Dim lastRow As Long
lastRow = Worksheet("Data").Cells(3, Columns.Count).End(xlUp).Column
For x = 1 To lastRow
If Worksheets("Sheet2").Cells(2, "A") = "" Then
Worksheets("Data").Range("c2:c1000").Copy Destination:=Worksheets("Sheet2").Range(1, "A")
Sheets("Sheet2").Range("A1").Value = Format(Now, "mm/dd/yyyy HH:mm:ss")
Else
Worksheets("Data").Range("c2:c1000").Copy Destination:=Worksheets("Sheet2").Cells(2,
Columns.Count).End(xlToLeft).Offset(, 1)
'Sheets("Sheet2").Range("A1").Value = Format(Now, "mm/dd/yyyy HH:mm:ss") --> can't figure how to increment this as this will need to be on the subsequent empty column
End If
Next
End Sub
Your help will be greatly appreciated!
Thank you.
Pasting values first into range A1 and down and then next time to cell B1 and so on, leaves no space for the timestamp to A1, B1 etc. So, I assume that you would like to paste the random values to row 2. So cells A1, B1, ... are left for the timestamp.
Inside the With statements we can refer to properties of the wsAudit so we can replace the "Worksheets("Audit")." reference with just "."
The column.count expression just checks the amount of columns in the worksheet.
The expression .Cells(2, Columns.Count) just points to last cell in the row 2.
The .End(xlToLeft).Column then looks from this column to left and is supposed to find the last not empty cell on this row. It's basically the same idea that in Excel's sheet you would go to cell XDF2 and hit CTRL+Arrow Left from keyboard.
But instead of activating the cell we just want to get the columns index number and then add 1 (the new column) and save it into variable. Now the new column is known.
The expression Range(.Cells(2, newColAudit), .Cells(1000, newColAudit)).Value is really the same as e.g. Range("B2:B1000"), but with this we can use the row and column index numbers instead. This is useful as the column number varies.
And as Samuel pointed out the copy paste operation can be avoided by setting the areas equal.
Dim wsAudit As Worksheet
Dim newColAudit As Long
Set wsAudit = Worksheets("Audit")
With wsAudit
newColAudit = .Cells(2, Columns.Count).End(xlToLeft).Column + 1
Range(.Cells(2, newColAudit), .Cells(1000, newColAudit)).Value = Worksheets("Data").Range("C2:C1000").Value
.Cells(1, newColAudit).Value = Format(Now, "mm/dd/yyyy HH:mm:ss")
End With
Much like your LastRow* variable for your source sheet, create a LastColumn variable for your destination sheet, which will find the last used column the same way you are finding your last used row.
Like so:
Dim LastColumn As Long
LastColumn = Sheets("Audit").Cells(1, Columns.Count).End(xlToLeft).Column
Then use the variable like so:
Destination:= Worksheets("Audit").Cells(1, LastColumn)
It seems that your code contradicts your question too, in your question you explained the data will be written to the Audit sheet in row 1, using the next column each time but your code looks for values in row 2 in your If statement:
If Worksheets("Audit").Cells(2, "A") = "" Then is the same as If Worksheets("Audit").Range("A2") = "" Then.
If you mean to check the first row, change the 2 to 1.
To help improve your codes efficiency:
(Also see the link to 'how to avoid select' in that question):
You can achieve 'copy/paste' without actually using the 'copy' and 'paste' methods by assigning the value of one range to the other, as example, like so:
Worksheets("Audit").Cells(1, LastColumn).Resize(999, 1) = Worksheets("Data").Range("c2:c1000").Value
Note: Change the Resize Property rows to suit the source range (in this case you are wanting to move values from C2:C1000).
*The LastRow variable is a bit confusing, as it is looking for the last used column in row 3.
If it's meant to find a column, consider renaming it to avoid confusion later on in debugging.
If it's meant to find the last row, try like this:
LastRow = Worksheet("Data").Cells(Rows.Count, 1).End(xlUp).Row

Finding Last Row

I'm writing a macro to update key cells from a form, then write the data to a table in a second sheet. To do this, I'm using a For i = 1 to LR to match, then write the Row number to a variable if it finds a match.
However the code to find the last row, and therefore the length of the loop isn't working as expected.
Dim LR as Long
LR = Cells(Workbooks("Tracking Sheet.xlsx").Sheets("Sheet1").Rows.Count, "A").End(xlUp).Row
Debug.Print LR
I've tried pre-populating both less than 25 and more than 25 (e.g. 38) , but the LR variable is always showing 25 in the Immediate window. In the immediate window,
?Cells(Workbooks("Tracking Sheet.xlsx").Sheets("Sheet1").Rows.Count, "A").End(xlUp).Row
returns 38, as it should.
LR = Cells(Workbooks("Tracking Sheet.xlsx").Sheets("Sheet1").Rows.Count, "A").End(xlUp).Row
?LR
also returns 38
I just don't get it...
Try this:
Dim LR As Long
With Application.Workbooks("Tracking Sheet.xlsx").Worksheets("Sheet1")
LR = .Range("A" & .Rows.Count).End(xlUp).Row
End With
Maybe the code explained will help?
LRow = ThisWorkbook.Sheets(1).Cells(ThisWorkbook.Sheets(1).Rows.Count, "A").End(xlUp).Row
Let's break up this code. First, we place it in a With statement to save us some typing
With ThisWorkbook.Sheets(1)
LRow = .Cells(.Rows.Count, "A").End(xlUp).Row
End With
Let's break this up in pieces. First, there is .Cells(.Rows.Count, "A")
This is a reference to a single cell. Specifically, the very last row in column A of ThisWorkbook.Sheets(1). Rows.Count gives the last row number in the Excel sheets. Recent Excel versions support 1,048,576 rows, but e.g. Excel 97 only supported 65536 rows.
In short, .Cells(.Rows.Count, "A") is for recent excel versions equal to (A1048576)
Then End(xlUp). This does the same as when you hold CTRL and press the arrow up button on your keyboard. This brings Excel to the first filled row from the bottom.
Finally .Row, which returns the row number of this found cell.
You could also start looking from the the top. Try holding CTRL and then pressing the arrow down button. This would stop at the last non-empty cell. However, it would also stop when you have blank cells between filled cells. So, not always a preferred method. The code would be
With ThisWorkbook.Sheets(1)
LRow = .Cells(1, "A").End(xlDown).Row
End With

VBA Sumif vs Sumifs

UPDATE:
Thank you for your help thus far! That definitely worked.
I am wondering though if there is a way to add a loop function as well. What i would like it to do is to reference Column A of Worksheet 888 to identify the same value in Column A of 999, and sum the values in the last column of the sheet (keeping in mind that the last column may differ, thus allowing the macro to be more dynamic).
Dim LastRow, LastColumn As Long
LastRow = Cells(Rows.Count, "A").End(xlUp).Row
LastColumn = Worksheets("888").Cells(1,
Columns.Count).End(xlToLeft).Column
For x= 1 To LastRow
Cells(x, 2).Value =
WorksheetFunction.SumIf(Worksheets("888").Range("A:LastRow"),
Worksheets("999").Range("A:LastRow"),
Worksheets("888").Range("LastColumn:LastColumn"))
Next c
I keep getting the error "Unable to get the SumIf property of the worksheet function class. Any thoughts on how to fix?
i don't really understand why do you want to use macro when formula can do the job
put below in C2 and drag down the formula should do the job
=SUMIF(Sheet2!F:F,A2,Sheet2!H:H)+SUMIF(Sheet3!J:J,A2,Sheet3!L:L)
but if you insist macro,
Worksheets("Sheet1").Range("C2").Value = "=SUMIF(Sheet2!F:F,A2,Sheet2!H:H)+SUMIF(Sheet3!J:J,A2,Sheet3!L:L)"
Worksheets("Sheet1").Range("C2:C7").FillDown

Inserting a formula all the way to the last row in the last empty column

I am fairly new to writing macros in VBA, but I am doing my best to learn as much as I can as quickly as possible. Anyway, the task I am trying to perform is pretty simple, but I'm having trouble coming up with a way to do it.
What I want to do is paste a formula into all of the rows in the last empty column of a spreadsheet.
So in this case, into the highlighted cells shown in the screenshot:
Example:
However, I don't want to rely on typing ("K1:K" & lastrow), what I want to do is create reference to the last column and the last row.
So far I've only been able to paste the formula into the entire column by using this:
lastcol = .Cells(1, .Columns.Count).End(xlToLeft).Offset(0, 1).Column
fvlookup = "=vlookup(#1,#2,#3,False)"
fvlookup = Replace (fvlookup, "#1", rng.Address)
fvlookup = Replace (fvlookup, "#2", [LookupFile.csv]LookupFile!$B:$1")
fvlookup = Replace (fvlookup, "#3", "5")
.Columns(lastcol).Formula = fvlookup
But later on in the process I'm working on, I want to remove all of "#N/A" and place them into a different tab named "JEs" because some of the items in the table actually don't have a value in the table I'm looking up to, and need JEs created for them. So, I would have a lot of unnecessary rows in the new tab if I went down this route.
Anyway, I've tried this:
lastcol = .Cells(1, .Columns.Count).End(xlToLeft).Offset(0,1).Column
fvlookup = "=VLOOKUP(#1,#2,#3,False)"
fvlookup = Replace(fvlookup, "#1", rng.Address)
fvlookup = Replace(fvlookup, "#2", "[LookupFile.csv]LookupFile!$B:$I")
fvlookup = Replace(fvlookup, "#3", "5")
With .Columns(lastcol)
lrow = .range("A" & .Rows.Count).End(xlUp).Row
.Rows(lrow).Formula = fvlookup
End With
But it only places the formula into "K1" (referencing the attached image)
I've also tried selecting this value after the formula is pasted and auto filling (I know it is advised to avoid selecting ranges with vba, but I still wanted to try as a last resort), but it says it isn't allowed.
I've also tried using:
.range(lastcol & .range("A" & .Rows.Count).End(xlUp).Rows).Formula = fvlookup
But that gives me run-time error '1004': Application-defined or object-defined error. I tried creating a variable named 'lrange' and setting it similar to how I set lastcol, but no luck (it returns the same error message as above). I've also tried editing lastcol to lastcol.Column or .Columns(lastcol) in the above string, but still nothing.
I've tried researching similar questions and all of the recommendations advise defining two variables (a lastrow one and a lastcolumn one) and inserting the formula into that range, but that doesn't seem to work with what I'm using.
I feel like the process VBA is trying to execute is restricted to only being able to insert data into the entire column, or part of it, with the way I set the macro up, like it's not able to find the last column and insert all the way to the last row with the way I set it up. So I apologize if this is the case, or if I should have written it differently.
Any advise or direction anyone can provide on this topic would be much appreciated.
Thank you for your time!
Instead of looping at the end I would just use .FillDown
Cells(2, lastcol).FormulaR1C1 = fvlookup
Range(Cells(2, lastcol), Cells(lrow, lastcol)).FillDown
How about replacing your code with something like this:
Sub foo()
lastcol = .Cells(1, .Columns.Count).End(xlToLeft).Offset(0, 1).Column
fvlookup = "=VLOOKUP(#1,#2,#3,False)"
fvlookup = Replace(fvlookup, "#1", Rng.Address)
fvlookup = Replace(fvlookup, "#2", "[LookupFile.csv]LookupFile!$B:$I")
fvlookup = Replace(fvlookup, "#3", "5")
lrow = .Range("A" & .Rows.Count).End(xlUp).Row
For i = 2 To lrow
.Cells(i, lastcol).Formula = fvlookup
Next i
End Sub
This will loop from row 2 to lastrow and add the formula to lastcol.
As long as the table doesn't have an entirely blank row or column (as it shouldn't) Range("A1").CurrentRegion identifies the extent of the table. Then .Rows.Count and .Columns.Count gives you the information you need to be able to populate the adjacent column with a formula.
You can also fill the column's cells with formulas in one go, using FormulaR1C1 - provided you are careful with cell referencing. Here is an example:
Dim tableRange As Range
Dim x As Integer, y As Integer
Set tableRange = Range("A1").CurrentRegion
x = tableRange.Rows.Count
y = tableRange.Columns.Count
'fill the adjacent column with a SUM function summing across each row
Range(Cells(2, y + 1), Cells(x, y + 1)).FormulaR1C1 = "=SUM(RC[-" & y & "]:RC[-1])"
(You can also use y and 1 to give this new column a heading.)
If you need to replicate VLOOKUP with a specific (absolute) address such as $B:$I then I would first name that range and insert the name in the formula.
An example of such is:
.FormulaR1C1 = "=VLOOKUP(RC[-1],mylookup,2,FALSE)"
where mylookup is the range name and RC[-1] is referencing the cell immediately to the left of each formula.

Selecting dynamic, filtered range in VBA

I am attempting to select a dynamic range of filtered data that spans from col. A: col. J without selecting the header (in row 1). From there I need to copy and paste it into a new sheet where I will manipulate it further, but I cannot come up with an efficient or functional way to do this. Based on some code I found on another forum I was able to select all of the "visable cells" in a single column, but I am running into issues trying to select the whole range. I am still very new to vba so forgive my syntax, but my code posted below was an attempt to itterate through Rows.Count and i which was an integer 1-10. If you have any advice on how to do this better and more efficiently I would really appreciate it.
Sub SelectVisibleInColD()
Dim lRow As Long, i As Integer
Set i = 1
Do While i <= 10
With ActiveSheet
lRow = .Cells(.Rows.Count, i).End(xlUp).Row
If lRow < 3 Then Exit Sub
.Cells(1, 1).Offset(1, 0).Resize(lRow - 1).SpecialCells(xlCellTypeVisible).Select
End With
i = i + 1
Loop
End Sub
You can select a range by using Range property of ActiveSheet. You already have the last row and you know that the header is in the first row, so your range starts from position A2 and goes to the last row of column J
ActiveSheet.Range("A2:J"&lRow).SpecialCells(xlCellTypeVisible)
If you want to copy this range, use Copy function like
yourRangeAsAbove.Copy
This function only moves the selection to memory, to paste it, build your destination range and call PasteSpecial function.
I came across this answer googling my issue for: deleting of filtered selection in vba.
However trying your answer &lRow gives me an runtime error 1004, application-defineed or object-defined error
I got around it with this
ActiveSheet.Range("A2:G" & Range("A" & Rows.Count).End(xlUp).Row).SpecialCells(xlCellTypeVisible).Delete
For those that may also get the same issue.

Resources