I want to create a button that, when the user presses it, will do this:
before press the button user mark name in pivot table on sheet Eff
after press button the same name will be filter in another pivot table on sheet Pivot_All
I tried this, but in .PivotItems(a(g)).Visible = True it gives me object required error
Public Function GetLength(a As Variant) As Integer
If IsEmpty(a) Then
GetLength = 0
Else
GetLength = UBound(a) - LBound(a) + 1
End If
GL = GetLength
End Function
Public Function a(ByVal rng As Range) As Variant
Dim f As Long, r As Range
ReDim arr(1 To rng.Count)
f = 1
For Each r In rng.Cells
arr(f) = r.Value
f = f + 1
Next r
a = arr
End Function
Sub Macro6()
Dim rngCopy As Range
Dim rngPaste As Range
Dim rng As Range
Set rng = Selection
Sheets("Pivot_All").Activate
ActiveSheet.PivotTables("PivotTable1").PivotFields("Empl").ClearAllFilters
With ActiveSheet.PivotTables("PivotTable1").PivotFields("Empl")
For i = 1 To .PivotItems.Count - 1
.PivotItems(.PivotItems(i).Name).Visible = False
Next i
End With
With ActiveSheet.PivotTables("PivotTable1").PivotFields("Empl")
For g = 0 To GL
.PivotItems(a(g)).Visible = True
Next g
End With
End Sub
a is waiting for a range a(ByVal rng As Range) but you submit a number g as parameter. You must submit a Range object instead.
Also GL is not defined in your procedure, I assume you wanted to use GetLength instead.
So it should be something like:
Dim ResultOfA As Variant
ResultOfA = a(Range("your range here"))
For g = 0 To GetLength(ResultOfA)
.PivotItems(ResultOfA(g)).Visible = True
Next g
I recommend always to activate Option Explicit: In the VBA editor go to Tools › Options › Require Variable Declaration.
Note that you can read a continous range into an array in one line:
Dim ArrValues() As Variant
ArrValues = Range("A1:A10").Value
Debug.Print ArrValues(1, 5) '= A5
If you use this you might not need your function a anymore.
You would end up with something like:
Option Explicit
Public Sub Macro6()
Dim rngCopy As Range
Dim rngPaste As Range
Dim rng As Range
Set rng = Selection
Dim ws As Worksheet
Set ws = Worksheets("Pivot_All")
With ws.PivotTables("PivotTable1").PivotFields("Empl")
.ClearAllFilters
Dim i As Long
For i = 1 To .PivotItems.Count - 1
.PivotItems(.PivotItems(i).Name).Visible = False
Next i
Dim ResultOfA As Variant
ResultOfA = a(rng)
If Not IsEmpty(ResultOfA) Then
Dim g As Long
For g = LBound(ResultOfA) To UBound(ResultOfA)
.PivotItems(ResultOfA(g)).Visible = True
Next g
End If
End With
End Sub
Public Function a(ByVal rng As Range) As Variant
Dim f As Long, r As Range
ReDim arr(1 To rng.Count)
f = 1
For Each r In rng.Cells
arr(f) = r.Value
f = f + 1
Next r
a = arr
End Function
Related
I want to offset a range if the numerical part of a range's address can be divided by 11.
(A11, A22, A33, and so forth).
Take a range in a given sheet, for example Range("A2").
Could I do ...
Dim isRng as Range
Dim rngAddress as String
Dim tstAddress as Integer, nsnAddress as Integer
isRng = Range("A2")
isRng.Select
rngAddress = isRng.Address
Currently, rngAddress = $A$2 (I think). So then, could I ...
tstAddress = Right(rngAddress, 2)
nsnAddress = Right(tstAddress, 1)
If tstAddress / nsnAddress = 11 Then
'whatever code
Set isRng = ActiveCell.Offset(4,0).Select
Else
End If
I want it to skip down 4 rows after hitting any range like A11 or A22.
Would this work? Is there a better way of doing this? I really appreciate the help.
This should do the trick...
Sub sully_was_here()
Dim r As Range
Set r = [a22]
With r
.Select
If .Row Mod 11 = 0 Then
'whatever code here
.Offset(4).Select
End If
End With
End Sub
Divisible: Using Mod
If cCell.Row Mod 11 = 0 Then
Option Explicit
Sub Divisible()
Dim ws As Worksheet: Set ws = ActiveSheet ' improve!
Dim rg As Range: Set rg = ws.Range("A2:A33")
Dim cCell As Range
Dim r As Long
For Each cCell In rg.Cells
r = r + 1
If cCell.Row Mod 11 = 0 Then
Debug.Print r, cCell.Offset(4).Address, "***"
Else
Debug.Print r, cCell.Address
End If
Next cCell
End Sub
I have this sample table.
What I am trying to do is to get all the cell values in all colored cells and transpose them to another worksheet.
I have trouble with the code below to add and set those ranges together so that I can transpose all of them in a ROW in the other worksheet. I have started with the code below
Sub AddRanges()
Dim inRange As Range, inRangeValues() As Variant, outRangeValues() As Variant
Dim finalRow As Long
Dim inRange As Range
Set inRange = Sheet1.Range("A1:A6", "C1:C6", C10:C14) 'I think i got this wrong; Error Type Mismatch
inRangeValues() = inRange.Value 'generate 2d array
outRangeValues = Application.Transpose(inRangeValues)
With Sheet2
finalRow = .Cells(Rows.Count, 1).End(xlUp).Row 'find last row
If inRange.Columns.Count > 1 Then '2d array for output
.Cells(finalRow + 1, 1).Resize(UBound(outRangeValues, 1), UBound(outRangeValues, 2)) = outRangeValues 'Resize according to output array dimensions
Else '1D array for output
.Cells(finalRow + 1, 1).Resize(1, UBound(outRangeValues, 1)) = outRangeValues
End If
End With
End sub
In this example, what is the best approach to combine these ranges so I can transpose them as a ROW? Thanks.
Your code has major problems due to:
Double declaration of inRange
Wrong syntax for Set inRange the entire address needs to be enclosed in a single pair of quotes
Try Set inRange = Range("a1:a6, c1:c6, c10:c14")
Wrong method of reading into an array
When you have a range that consists of multiple areas, you have to convert each area separately.
Then you can create a 1-D array from this depending on the order you wish to have these elements, and write it wherever you want.
For example:
Option Explicit
Sub test()
Dim inRange As Range, inRangeValues As Variant, outRangeValues As Variant
Dim finalRow As Long
Dim I As Long, J As Long, V As Variant, L As Long
Dim lCols As Long
Set inRange = Range("a1:a6, c1:c6, c10:c14")
ReDim inRangeValues(1 To inRange.Areas.Count)
For I = 1 To inRange.Areas.Count
inRangeValues(I) = inRange.Areas(I)
Next I
'how many columns?
lCols = 0
For I = 1 To UBound(inRangeValues, 1)
lCols = lCols + UBound(inRangeValues(I), 1)
Next I
ReDim outRangeValues(1 To lCols)
L = 0
For I = 1 To UBound(inRangeValues, 1)
For J = 1 To UBound(inRangeValues(I), 1)
L = L + 1
outRangeValues(L) = inRangeValues(I)(J, 1)
Next J
Next I
Stop
' enter some code to write the results where you want
' below is just throwaway for proof of concept
Range("f20").Resize(columnsize:=UBound(outRangeValues)).Value = outRangeValues
End Sub
Given your input, the above code would create output like:
You are correct that your code is wrong where you highlight. Try a union. From there, it should be pretty basic to just loop through your range and put them wherever you want in the Sheet2 spreadsheet. See if the below does what you need.
Sub AddRanges()
Dim inRange As Range, acell As Range, aCounter As Long
Const startAddress As String = "A1"
Set inRange = Union(Sheet1.Range("A1:A6"), Sheet1.Range("C1:C6"), Sheet1.Range("C10:C14"))
For Each acell In inRange.Cells
If Not IsEmpty(acell) Then
finalRow = sheet2.Cells(Rows.Count, 1).End(xlUp).Row + 1 'find last row
sheet2.Cells(finalRow, 1).Value = acell.Value
End If
Next acell
End Sub
Check it out.
Sub RngAreaTransps()
Dim RangeArea As Range, LstRw As Long
Dim sh As Worksheet, ws As Worksheet
Dim col As Long, InRange As Range
Set sh = Sheets(1)
Set ws = Sheets(2)
LstRw = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row + 1
With sh
Set InRange = .Range("A1:A6, C1:C6, C10:C14")
For Each RangeArea In InRange.Areas
With ws
col = .Cells(LstRw, .Columns.Count).End(xlToLeft).Column
If col <> 1 Then col = col + 1
RangeArea.SpecialCells(xlCellTypeConstants).Copy
.Cells(LstRw, col).PasteSpecial Transpose:=True
End With
Next RangeArea
End With
Application.CutCopyMode = False
End Sub
I am trying to write a function that automatically deletes the minimum value for a given selection of cells. I know how to find the minimum value but I just don't know how to delete that value.
Here's what I've got.
Function MinDel(Stuff)
MinDel = Application.Worksheetfunction.min(stuff)
End Function
How do I delete the MinDel value?
You could modify the function from here like that
Option Explicit
Function AddressOfMax(rng As Range) As Range
Set AddressOfMax = rng.Cells(WorksheetFunction.Match(WorksheetFunction.Max(rng), rng, 0))
End Function
Function AddressOfMin(rng As Range) As Range
Set AddressOfMin = rng.Cells(WorksheetFunction.Match(WorksheetFunction.Min(rng), rng, 0))
End Function
Sub TestIt()
Dim rg As Range
Dim rgMin As Range
Set rg = ActiveSheet.Range("A1:A6")
Set rgMin = AddressOfMin(rg)
rgMin.Clear
End Sub
As mentioned in the comments, a UDF (User Defined Function) cannot change a value or range in Excel by default and a Sub should be used. This is my way to delete the minimal value in the Selection:
Public Sub DeleteMinimum()
Dim myRange As Range
Dim minValue As Double
Dim myMin As Range
If Not TypeOf Selection Is Excel.Range Then Exit Sub
Dim valueAssigned As Boolean: valueAssigned = False
minValue = 0
For Each myRange In Selection
If IsNumeric(myRange) Then
If Not valueAssigned Then
valueAssigned = True
minValue = myRange
Set myMin = myRange
Else
If myRange < minValue Then
minValue = myRange
Set myMin = myRange
End If
End If
End If
Next myRange
If Not myMin Is Nothing Then
myMin = "DELETED!"
End If
End Sub
The procedure below will delete the lowest value in a selection, provided that the selection comprises more than one cell. It ignores all but the first column of the selection.
Sub DelMin()
' 05 Jan 2019
Dim Arr As Variant, i As Integer
Dim Mm As Variant, m As Integer
With Selection
If .Cells.Count > 1 Then
Arr = .Value
For i = 1 To UBound(Arr)
If Not IsEmpty(Arr(i, 1)) Then
If IsEmpty(Mm) Or (Arr(i, 1) < Mm) Then
Mm = Arr(i, 1)
m = i
End If
End If
Next i
.Cells(m, 1).ClearContents
End If
End With End Sub
32 Bit Excel 365 on 64 Bit Win7
Worksheet 300600 Rows x 105 Columns
Goal: Calculate the Number of Unique Entries in each Column
Attempted Solution 1: Formula
{=SUM(1/COUNTIF(A8:A300600,A8:A300600))}
Issue: Long Runtime, Freezes Excel, Must Stop Calculation
Attempted Solution 2: VBA UDF
Function UniqueCount(Selection As Range) As Integer
Dim UniqueArray()
ReDim UniqueArray(0 To Selection.Count)
Dim Rng As Range
Dim CUniqueCount As Integer
CUniqueCount = 0
For Each Rng In Selection
For i = 0 To Selection.Count
If UniqueArray(i) = Rng.Value Then Exit For
If UniqueArray(i) = "" Then
UniqueArray(i) = Rng.Value
CUniqueCount = CUniqueCount + 1
Exit For
End If
Next i
Next
UniqueCount = CUniqueCount
End Function
Note: This is Much faster, but I'm still looking for an even faster approach
I'd use an array as well as the Dictionary:
Public Function CountUnique(rngInput As Range) As Double
Dim rngCell As Range
Dim dData As Object
Dim vData
Dim x As Long
Dim y As Long
Set dData = CreateObject("Scripting.Dictionary")
vData = rngInput.Value2
For x = LBound(vData, 1) To UBound(vData, 1)
For y = LBound(vData, 2) To UBound(vData, 2)
If LenB(vData(x, y)) <> 0 Then dData(CStr(vData(x, y))) = Empty
Next y
Next x
CountUnique = dData.Count
End Function
Try this
'Set a reference to MS Scripting runtime ('Microsoft Scripting Runtime')
Function UniqueCount(SelRange As Range)
Dim Rng As Range
Dim dict As New Scripting.Dictionary
Set dict = CreateObject("Scripting.Dictionary")
For Each Rng In SelRange
If Not dict.Exists(Rng.Value) Then
dict.Add Rng.Value, 0
End If
Next Rng
UniqueCount = dict.Count
Set dict = Nothing
End Function
I tried to get the unique value of each column in the range "RD" and display them in single column. I need to create an object ("scripting.Dictionary") where there are just as many as the number of columns in Range "RD". I tried this code but it resulted in "Run time error 13".
Private Sub CommandButton1_Click()
Range(Me.RefEdit1).Name = "RD"
Range(Me.RefEdit2).Name = "OT"
Dim d As Object, c As Variant, i As Long, s As Long
Dim JK As Long
Dim o As Collection
JK = Range("RD").Columns.Count
Set d = CreateObject("Scripting.Dictionary")
For k = 0 To JK + 1
d.Item(k) = CreateObject("Scripting.Dictionary").Item(k)
c = Range("RD").Columns(k + 1)
If d.Exists(k) Then
d.Item(k) = d.Item(k) + 1 'increment
Else
d.Item(k) = 1 'set as 1st occurence
End If
For i = 1 To UBound(c, 1)
d.Item(k)(c(i, 1)) = 1
Next i
Range("OT").Cells((k * 5) + 2, 2).Resize(d.Item(k).Count) = Application.Transpose(d.Item(k).Keys)
Range("OT").Cells((k * 5) + 2, 2).Resize(d.Item(k).Count).Sort Key1:=Range("OT").Cells((k * 5) + 2, 2).Resize(d.Item(k).Count)
Next k
End Sub
I'm adding some code below to help loop through a list, looking for unique values, and adding them to a new column. In my example, I enclose the entire functionality into a single loop for efficiency. I'm also adding the unique values to a new column in Sheet2 starting with cell A1.
Let me know if you need any additional help.
EDITED CODE BASED ON A MISUNDERSTANDING:
Private Sub CommandButton1_Click()
Dim oDict As Object
Dim rngToScrub As Range
Dim rngNewColumnToStoreUnique As Range
Dim oCol As Range
Dim cel As Range
Set rngToScrub = Range(Me.RefEdit1.Value)
Set rngNewColumnToStoreUnique = Sheet2.Range("A1")
For Each oCol In rngToScrub.Columns
Set oDict = CreateObject("Scripting.Dictionary")
For Each cel In oCol.Cells
If oDict.exists(cel.Value) Then
'Do Nothing for Now
Else
oDict.Add cel.Value, 0
rngNewColumnToStoreUnique.Value = cel.Value
Set rngNewColumnToStoreUnique = rngNewColumnToStoreUnique.Offset(1)
End If
Next cel
Set oDict = Nothing
Next oCol
End Sub
Old code: Misunderstood requirements
Private Sub CommandButton1_Click()
Dim oDict As Object
Dim rngToScrub As Range
Dim rngNewColumnToStoreUnique As Range
Dim cel As Range
Set oDict = CreateObject("Scripting.Dictionary")
Set rngToScrub = Range(Me.RefEdit1.Value)
Set rngNewColumnToStoreUnique = Sheet2.Range("A1")
For Each cel In rngToScrub
If oDict.exists(cel.Value) Then
'Do Nothing for Now
Else
oDict.Add cel.Value, 0
rngNewColumnToStoreUnique.Value = cel.Value
Set rngNewColumnToStoreUnique = rngNewColumnToStoreUnique.Offset(1)
End If
Next cel
End Sub