I'm trying to auto-filter my pivot table based on the most recent date in my data and I came across the following code. However, when I try to execute it, I get a
run-time error (1004 - Unable to set the Visible property of the PivotItem class)
on the line: pfiPivFldItem.Visible = False Can someone help me with this?
Dim pfiPivFldItem As PivotItem
Dim dtmDate As Date
With Worksheets("Sheet2").PivotTables("Campaigns")
.PivotCache.Refresh
.ClearAllFilters
With .RowRange
dtmDate = Evaluate("MAX(IF(ISNUMBER(" & .Address(0, 0) & ")," & .Address(0, 0) & ",))")
End With
For Each pfiPivFldItem In .PivotFields("Date").PivotItems
If pfiPivFldItem.Value = "(blank)" Then
pfiPivFldItem.Visible = False
Else
pfiPivFldItem.Visible = (CDate(pfiPivFldItem.Value) = CLng(dtmDate))
End If
Next pfiPivFldItem
This is a common error I see when folk are trying to iterate over PivotItems and change visibility. It happens because you must leave one item visible at all times. The workaround is to set the first item to visible, do the loop, and then check whether the first item should be visible or not. Of course, this won't help you if the thing you're looking for isn't in the PivotTable, which might be the case here. It may well be that (CDate(pfiPivFldItem.Value) will never equal CLng(dtmDate)) due to the issue/bug I outlined at http://dailydoseofexcel.com/archives/2013/11/09/a-date-with-pivotitems/
There's some other things you want to do when filtering PivotTables, such as switch the PivotTable .ManualUpdate property to TRUE while you loop the PivotTable, or otherwise the PT will try to update after each and every change.
Suggest you check out my answer at Filter pivot table 1004 error as the code comments show how and why to do some of this stuff. Also suggest you check out my blogpost at http://dailydoseofexcel.com/archives/2013/11/14/filtering-pivots-based-on-external-ranges/ that takes an indepth look at bottlenecks and workarounds when iterating over PivotItems.
Note that the quickest way to filter a field on one item is to make it a PageField, and set the .PageField value to the PivotItem concerned. That's near instantaneous, and makes a massive difference if you have thousands of items in the PivotField that you're iterating over.
I have a very large embedded IF formula that appears to occasionally break for no reason. Opening and closing the page a few times eventually gets it working again. I am wondering if there is a VBA alternative for it. Here is the IF formula I am running.
=IF(ISNUMBER(SEARCH("76210",E125)),"_012_00762_10",IF(ISNUMBER(SEARCH("76220",E125)),"_012_00762_20",IF(ISNUMBER(SEARCH("76900",E125)),"_012_00769_00",IF(ISNUMBER(SEARCH("76901",E125)),"_012_00769_01",IF(ISNUMBER(SEARCH("85702",E125)),"_012_00857_02",IF(ISNUMBER(SEARCH("85710",E125)),"_012_00857_10",IF(ISNUMBER(SEARCH("100800",E125)),"_012_01008_00",IF(ISNUMBER(SEARCH("100900",E125)),"_012_01009_00",IF(ISNUMBER(SEARCH("123100",E125)),"_012_01231_00",IF(ISNUMBER(SEARCH("124600",E125)),"_012_01246_00",IF(ISNUMBER(SEARCH("124601",E125)),"_012_01246_01",IF(ISNUMBER(SEARCH("124640",E125)),"_012_01246_40",IF(ISNUMBER(SEARCH("124641",E125)),"_012_01246_41",IF(ISNUMBER(SEARCH("142301",E125)),"_012_01423_01",IF(ISNUMBER(SEARCH("158801",E125)),"_012_01588_01",IF(ISNUMBER(SEARCH("158900",E125)),"_012_01589_00",IF(ISNUMBER(SEARCH("159203",E125)),"_012_01592_03",IF(ISNUMBER(SEARCH("159303",E125)),"_012_01593_03",IF(ISNUMBER(SEARCH("159401",E125)),"_012_01594_01",IF(ISNUMBER(SEARCH("159410",E125)),"_012_01594_10",IF(ISNUMBER(SEARCH("159420",E125)),"_012_01594_20",IF(ISNUMBER(SEARCH("159501",E125)),"_012_01595_01",IF(ISNUMBER(SEARCH("169000",E125)),"_012_01690_00",IF(ISNUMBER(SEARCH("186900",E125)),"_012_01869_00",IF(ISNUMBER(SEARCH("213200",E125)),"_012_02132_00",IF(ISNUMBER(SEARCH("213300",E125)),"_012_02133_00",IF(ISNUMBER(SEARCH("215400",E125)),"_012_02154_00",IF(ISNUMBER(SEARCH("220100",E125)),"_012_02201_00",IF(ISNUMBER(SEARCH("223800",E125)),"_012_02238_00",IF(ISNUMBER(SEARCH("225600",E125)),"_012_02256_00",IF(ISNUMBER(SEARCH("230700",E125)),"_012_02307_00",IF(ISNUMBER(SEARCH("230701",E125)),"_012_02307_01",IF(ISNUMBER(SEARCH("231800",E125)),"_012_02318_00",IF(ISNUMBER(SEARCH("235000",E125)),"_012_02350_00",IF(ISNUMBER(SEARCH("235020",E125)),"_012_02350_20",IF(ISNUMBER(SEARCH("242000",E125)),"_012_02420_00",IF(ISNUMBER(SEARCH("246400",E125)),"_012_02464_00",IF(ISNUMBER(SEARCH("292900",E125)),"_012_02929_00",""))))))))))))))))))))))))))))))))))))))
Basically it is built so a serial number is scanned and it populates a cell for the users who use this sheet with its results from the search. I am already running one macro in this sheet as well. Here is that...
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
Dim rng As Range
Set rng = Intersect(Range("A2:A500, J2:J500"), Target) ' define range of interest
If Not rng Is Nothing Then ' check it's not "nothing"
If WorksheetFunction.CountA(rng) = rng.Count Then 'check for all of its cells being not empty
On Error GoTo safe_exit 'add error control
Application.EnableEvents = False 'don't do anything until you know something has to be done
rng.Offset(, 1).Value = Date 'write Date next to all relevant changed cells
End If
End If
safe_exit:
Application.EnableEvents = True
End Sub
Maybe there is a better way to build this search using a formula that isn't using embedded IF statements, but i couldn't think of another way to do it. Thanks in advance.
This may be what you're looking for:
=IF(ISNA(MATCH(1,IF(ISERR(SEARCH($A$5:$A$42,$E$125)),0,1),0)),"",INDEX($B$5:$B$42,MATCH(1,IF(ISERR(SEARCH($A$5:$A$42,$E$125)),0,1),0)))
entered as an array formula (CTRL-SHIFT-ENTER).
Here $A$5:$A$42 contains 76210, 76220, ... , 292900 (entered as text, not numbers); and $B$5:$B$42 contains _012_00762_10, _012_00762_20, ... , _012_02929_00.
Hope that helps.
Any time you have to go more than 2 deep on an IF you may want to rethink the usage.
What you can do is build a table from your values. Then reference that table as part of your lookup. Assuming your list of value is in range D8:E45 you could use the formula =VLOOKUP(E125,$D$8:$E$45,2).
The beginning of your table would look like what's seen below. The input result cell is referencing your input value and pulling the match of the second column.
To get your table you can take your source formula and replace (Find and Replace - Ctrl+H) some characters with unique delimiting characters. Then use Text To Columns Alt+D+E and delimit and Copy>Paste special>Transpose to quickly have it close to the format you need.
Currently I'm creating a macro that reads data from a table column, Asset No. The data is obtained through drop-down menu via data validation from another table in another sheet, DieMaster.
It will then perform Index and Match to find the matching data from DieMaster and insert it into Description column.
In addition, Upon obtaining data, the table column will then copy and paste as value to get the data only. That way the formula won't slow down filter searches.
This is what I have come up with.
Sub convert()
Dim osh As Worksheet
Set osh = ActiveSheet
osh.Range("ProjectEntry[Description]").Formula = "=IF(ISNA(INDEX(DieMaster,MATCH(B4,DieMaster[Asset No],FALSE),2)),"""",INDEX(DieMaster,MATCH(B4,DieMaster[Asset No],FALSE),2))"
osh.Range("ProjectEntry[Description]").Copy
osh.Range("ProjectEntry[Description]").PasteSpecial xlPasteValues
End Sub
I have tested the macro and it works perfectly. However the issue I have now is trying to assign the macro to Asset No column. My plan is to have it so that when a value is selected in a cell within the Asset No column via aforementioned drop-down menu, it will automatically show me the data for description.
Since I cannot assign macro to column using the traditional method, is there an alternative?
You can use the Worksheet_Change Event to accomplish this:
Private Sub Worksheet_Change(Target as Range)
If Not Intersect(Target, Me.ListObjects("ProjectEntry").ListColumns("AssetNo").DataBodyRange) Is Nothing Then
With Me.Range("ProjectEntry[Description]")
.Formula = "=IF(ISNA(INDEX(DieMaster,MATCH(B4,DieMaster[Asset No],FALSE),2)),"""",INDEX(DieMaster,MATCH(B4,DieMaster[Asset No],FALSE),2))"
.Value = .Value
End With
End If
End Sub
My syntax may be slightly off (since I am not on my normal computer), but it will get you close enough to tweak on your own.
I'm trying to organise my columns in some VBA code. I need them in a particular order depending on the value in Row A. I know i can do it using the following method but it's very long winded and looks horrible. Is there any more efficient way to do it?
Current Code
Set Name = Cells.Find(What:="Name", LookAt:=xlWhole)
Then activate the cell, find column and then paste to where i want it. So if the columns went like this:
It would change the order to this
I can't just delete the columns that I don't want because they change position. So if i was to delete columns C:C it might delete what i need on occasion.
I need it so that no matter what columns are there, or how many, it will also go in the same order as shown above.
Thanks
Try this.
Dim Rng As Range
Set Rng = ActiveSheet.UsedRange
If Range("A2").Value = "x" Then
Range("A1,C1,D1").EntireColumn.Hidden = True
Rng.SpecialCells(xlCellTypeVisible).Copy Destination:=Sheets("Sheet2").Range("A1")
End If
I've come across Stackoverflow many times when I've been looking for excel VBA scripting answers, but I've come up to a wall with my searching for an answer to my problem.
I have data which I need to sort, but it needs to be row grouped first, otherwise the sorting takes away the conformity of the info.
I want to group using info on just 1 column, and have found VBA scripts which do what I want except that they group 1 row to many. (if that makes sense)
i have info a little like
a b c d
Revise blank cell info info
blank cell blank cell info info
blank cell blank cell blank cell blank cell
Revise blank cell info info
blank cell blank cell info blank cell
Revise blank cell info info
etc etc
I want to group the top 3 rows, then the next 2.
but the only VBA script I found looks down the column for the word 'revise' but then groups the cells above the word revise, not below
I hope all that makes sense
thanks for any help
BTW, I dont really have any knowledge of programming, other than what I've gained through running some macros on other projects. Hopefully I wont need telling like a 5 YO, but may need some explanations of any code specific terms
I got this VBA script which works as I described
Option Explicit
Sub Macro2()
Dim rData, rCel As Range
Set rData = Range("a1", Range("a" & Rows.Count).End(xlUp))
Application.ScreenUpdating = False
With rData
On Error Resume Next
.Rows.Ungroup
.Rows.EntireRow.Hidden = False
On Error Goto 0
End With
For Each rCel In rData
If rCel = "END" Then Exit For
If rCel <> "Revise" Then
Rows(rCel.Row).Group
rCel.EntireRow.Hidden = True
End If
Next
Application.ScreenUpdating = True
End Sub
Here's a quick and dirty non-VBA solution, as you say you are not overly familiar with VBA.
Add a column to the right of your data, which will hold a new index that tracks each time 'Revise' is listed in column A. Starting in A2 and copied down, this will look as follows [you may need to hardcode the first entry as 1]:
=if(A2="Revise",A1+1,A1)
This will create a column which increases by 1 each time there is a new "Revise". Then just select your entire data block, right-click, and Sort by your new index column.