I'm trying to set the slicer for a pivot table based on values I collected earlier in a string.
The code below works:
ActiveWorkbook.SlicerCaches("Slicer_Merk1").VisibleSlicerItemsList = Array( _
"[dXref].[Merk].&[J17]", "[dXref].[Merk].&[J18]")
However, instead of having two values like J17 and J18, I could have an unknown amount of them. So the code could also be:
ActiveWorkbook.SlicerCaches("Slicer_Merk1").VisibleSlicerItemsList = Array( _
"[dXref].[Merk].&[J17]", "[dXref].[Merk].&[J18]", "[dXref].[Merk].&[J50]", "[dXref].[Merk].&[J500]")
To solve this, in earlier code I collect whatever values I want to filter and I put them together in a string called "txt".
So the string "txt" could contain this:
"[dXref].[Merk].&[J17]", "[dXref].[Merk].&[J18]"
or
"[dXref].[Merk].&[J17]", "[dXref].[Merk].&[J18]", "[dXref].[Merk].&[J50]", "[dXref].[Merk].&[J500]"
Then I assumed this would work:
ActiveWorkbook.SlicerCaches("Slicer_Merk1").VisibleSlicerItemsList = Array( _
txt)
I thought that replacing the code that normally works to set the slicer with a variable containing the same code would work. However I keep getting error messages that give a clue that there is something wrong with the comma in the txt string when setting the slicer.
I have been searching online for hours and trying endless things. I might be missing something simple.... can someone figure this out? Thanks a lot.
The full sub below (which is now working):
Sub FiltersMatchen()
Dim Selectie As Range
Dim FilterArray() As String
Dim FilterString As String
Dim i As Long
Dim Merk As Range
Dim FiltercodeBegin As String
Dim FiltercodeEinde As String
Set Selectie = Selection
i = 0
ReDim FilterArray(0)
For Each Merk In Selectie
FilterArray(i) = Merk
i = i + 1
ReDim Preserve FilterArray(i)
Next
FiltercodeBegin = "[dXref].[Merk].&["
FiltercodeEinde = "]"
For i = LBound(FilterArray) To UBound(FilterArray) - 1
FilterString = FilterString & FiltercodeBegin & FilterArray(i) & FiltercodeEinde & Chr(44)
Next i
FilterString = Left(FilterString, Len(FilterString) - 1)
FilterArray = Split(FilterString, ",")
ActiveWorkbook.SlicerCaches("Slicer_Merk1").VisibleSlicerItemsList = FilterArray
End Sub
The answer is that I had to split the string into an array, and the individual items in the array don't need to have the " " characters around them while preparing those items. I assumed they were required because if you manually fill in the array (what you see when you record the a macro) they are there.
So, manually an array item looks like this:
"[dXref].[Merk].&[J17]"
Using an array, an item looks like this:
[dXref].[Merk].&[J17
I edited the start post with the solution. The sub is used to apply a selection of the pivot table to the slicer connected to that pivot table.
Related
the ones who read this topic, this is my first question at StackOverflow if any mistakes were made by me please forgive me, I would appreciate it if someone helps.
I have a table which is declared 'genTable' as ListObject. In order to insert and get data from each column of the table. I just set ranges like as:
Dim genTable As ListObject
Set genTable = test.ListObjects("hourly_data")
Set u1_NetLoad = genTable.ListColumns("U1 Net Load").DataBodyRange
I used the following code and helped me setting ranges however it did not satisfied me. I want to learn more dynamic method for my knowledge of programming.
For Each word In genTable.HeaderRowRange
i = 1 + i
test.Cells(22 + i, 2).value = "Set " & Replace(CStr(word), " ", "") & _
" = genTable.ListColumns(" & Chr(34) & word & Chr(34) & ").DataBodyRange"
Next
the output of this code is copied to VBA module to set ranges.
Actually, my scripts work pretty well, but I just want to know If can I set ranges more easily and depending on variables. For my case, I typed every single ranges. I tried the 'for each' loop like this but it did not work.
For Each word In genTable.HeaderRowRange
range_name = Replace(CStr(word), " ", "") & "_"
Set range_name = genTable.ListColumns(word).DataBodyRange
Next
The code above does not work, is there anyway to make it works?
Thanks for reading.
I had the same question last week, I did some digging and came up with this.
Fist I define array then reDim with the the lower and upper limits of the array.
Then using the loop, populated each array entry with the sheet and range needed, noting I needed to use the sheet number, not the name, then I could populate the whole array, then proceed with the code:
...
Dim Nbr_Students As Integer
Dim Counter As Integer
Dim i As Integer
Dim Student_Sheets As Variant
Nbr_Students = Sheets("Master Lists").Range("M3").Value - 1
Counter = 15
i = 0
ReDim RngArray(0 To Nbr_Students)
Do While i <= Nbr_Students
Set RngArray(i) = Worksheets(Counter).Range("C141:C157")
Counter = Counter + 1
i = i + 1
Loop
...
Original Post: Link
I have an excel file with four columns: name, surname, address, area.
There are a lot of rows.
Is there a way to concatenate all the values of every single row in a variable, using vba?
I need a variable that should contain something like this:
(name1, surname1, address1, area1); (name2, surname2, address2, area2); (name3, surname3, address3, area3)...
If you have the following data in your worksheet
Then the following code will read the data into an array …
Option Explicit
Public Sub Example()
Dim RangeData() As Variant ' declare an array
RangeData = Range("A1:D5").Value2 ' read data into array
End Sub
… with the following structure:
Alternatively you can do something like
Public Sub Example()
Dim DataRange As Range
Set DataRange = Range("A2:D5")
Dim RetVal As String
Dim Row As Range
For Each Row In DataRange.Rows
RetVal = RetVal & "(" & Join(Application.Transpose(Application.Transpose(Row.Value2)), ",") & "); "
Next Row
Debug.Print RetVal
End Sub
To get this output:
(name1, surname1, address1, area1); (name2, surname2, address2, area2); (name3, surname3, address3, area3); (name4, surname4, address4, area4);
.. is there a way to write the result like a sort of list that shows all the values of the cells of the range?
Yes, there is. In addition to PEH's valid answers and disposing of Excel version MS365 you might also use
Dim s as String
s = Evaluate("ArrayToText(A2:D5, 1)") ' arg. value 1 representing strict format
resulting in the following output string:
{"name1","surname1","address1","area1";"name2","surname2","address2","area2";"name3","surname3","address3","area3";"name4","surname4","address4","area4"}
Syntax
ARRAYTOTEXT(array, [format])
The ARRAYTOTEXT function returns an array of text values from any specified range. It passes text values unchanged, and converts non-text values to text.
The format argument has two values, 0 (concise default format) and 1 (strict format to be used here to distinguish different rows, too):
Strict format, i.e. value 1 includes escape characters and row delimiters. Generates a string that can be parsed when entered into the formula bar. Encapsulates returned strings in quotes except for Booleans, Numbers and Errors.
Thank you for your answers, suggestions, ideas and hints. I am sorry if my question was not so clear, all the solutions you added were perfect and extremely elegant.
In the end I found a way - a dumber way in comparison to all the things you wrote - and I solved with a for statement.
I did like this:
totRow = ActiveSheet.UsedRange.Rows.Count
For i = 1 To totRow
name = Cells(i, 1)
surname = Cells(i, 2)
address = Cells(i, 3)
area = Cells(i, 4)
Example = Example & "(" & name & ", " & surname & ", " & address & ", " & area & "); "
Next i
Range("E1").Value = Example
It works (it does what I wanted to do), but I noticed a little limit: if the rows are a lot I can't keep the whole text in the variable.
The thing is not always the amount of values (IDs) will be the same within each cell (at least 1, max=several) that's why the fixed version of using concatenated vlookup+left/mid/right will not work for me due to that will solution will only work up to 3 values. The only fixed size is the size of the values to lookup (IDs - in green), 8 characters (letters+numbers).
I'm not sure but, is it possible to setup a loop within excel formulas/functions ?
Below is a table containing an example of the issue I'm trying to resolve and the expected values (tables are in different tab). Hope you can help.
Thanks.
example-tables
If you have windows Excel O365 with the TEXTJOIN and FILTERXML functions, you can use a formula:
=TEXTJOIN(",",TRUE,IFERROR(XLOOKUP(FILTERXML("<t><s>" & SUBSTITUTE(#[IDs],",","</s><s>") & "</s></t>","//s"),Table2[IDs],Table2[IDv2]),"""--"""))
Note that, in your data, there are two ID's in A4 that do not match any ID's in Table 2. Although that may be a typo, I left them as is to demonstrate the error handling.
Table1
Table2
Here is a UDF that will do what you describe. Paste the code into a standard code module (not one already existing in the workbook but one that you create and that would have a name like Module1 before you change it to what you like best. You can also rename the function to give it a more suitable name.
Function ID_v2(Cell As Range) As String
' 035
Dim Fun As String ' function return value
Dim Sp() As String ' array of CSVs of CellVal
Dim VLRng As Range ' the lookup range
Dim VL As Variant ' result of VLookup
Dim i As Integer ' loop counter
' this is a range similar to your sample A10:D19
Set VLRng = ThisWorkbook.Names("Table2").RefersToRange
Sp = Split(Cell.Cells(1).Value, ",")
If UBound(Sp) >= 0 Then
For i = 0 To UBound(Sp)
On Error Resume Next
VL = Application.VLookup(Trim(Sp(i)), VLRng, 3, False)
If Err Then VL = "[ERROR]"
Fun = Fun & VL & ","
Next i
ID_v2 = Left(Fun, Len(Fun) - 1) ' remove final comma
End If
End Function
Call the function with syntax like built-in functions. For example,
= ID_v2(A3)
This can be copied down like any other function. But remember to save the workbook as macro-enabled.
Try this:
Option Explicit
Sub Cell2List()
Dim wF As WorksheetFunction: Set wF = Application.WorksheetFunction 'To user Transpose
Dim i As Range
Dim j As Range
Dim s As String: s = "," 'The separator of the list
'Ask the user for the cell where are the list with the commas
'Just need to select the cell
Set i = Application.InputBox("Select just one cell where the values are", "01. Selecte the values", , , , , , 8)
'Ask the for the separator. If you are completely sure the comma will never change just delete this line
s = Application.InputBox("Tell me, what is the character separator, just one character! (optional)", "02. Separator (comma semicolon colon or any other char)", , , , , , 2)
If s = "" Then s = "," 'Verifying...........
'Ask the user where want to put the list
'You need to get ready the cells to receive the list.
'If there any data will be lost, the macro will overwrite anything in the cells
Set j = Application.InputBox("Select just one cell where the values will go as a list, just one cell!", "03. Selecte the cell", , , , , , 8)
Dim myArr: myArr = (Split(i.Value, s)) 'Split the list into a Array
Range(Cells(j.Row, j.Column), Cells(j.Row + UBound(myArr), j.Column)).Value = wF.Transpose(myArr)
'j.Row is the row of the cell the user selected to put the cell
'j.Column the same, but the column
'j.Row + UBound(myArr) = UBound(myArr) is the total count of elements in the list
' +j.Row
' _______________
' the last cell of the new list!
'wF.Transpose(myArr) = we need to "flip" the array... Don't worry, but Don't change it!
End Sub
You can put this macro with a button tin the ribbons, or use it as you can see in the gif
And this will be the result: (with a bigger list)
EDIT
You can use this UDF:
Function Cells2List(List As Range, Pos As Integer) As String
Cells2List = Split(List, ",")(Pos - 1)
End Function
Just need to define and index this way:
To tell the function, what index you want to see. You can use the function using ROW()-# to define an 1 at the beginning and when the formula send a #VALUE! delete the formulas. Where $A$1 is where the list are, and D7 is where the index are.
I'm currently comparing 2 databases where ZIP codes have been entered manually. I need to compare the ZIP codes for hundreds of accounts in each database to check if anything is missing. I've ordered all the values in ascending order in excel but cant seem to find a quick way to check what's missing.
Column A: Database A ZIPS (The correct ZIPs)
14464, 14515, 14612, 14615, 14626
Column B: Database B ZIPS (Manually Entered)
14464, 14612, 14615, 14626
Column C: Missing ZIPs
14515
EDIT: I should have clarified, the data is stored in this manner.. each zip is not stored in a separate column, there are multiple Zips for each agent.
Image of worksheet
I know there must be a way to find this value using an excel VBA!
Thanks
Answer prior to seeing author's data format
Luckily the task is not too hard. You just need to simply use:
=IF(COUNTIF(list,value),"Output if it exists","Output if missing")
So in your case using the columns you define...
=IF(COUNTIF($B:$B,$A1),"",A1)
Then apply the formula for the length of the correct zip column.
see: https://exceljet.net/formula/find-missing-values
Example picture here
give this a go
Public Function ShowMissingZips(rngSource As Range, _
rngMatch As Range) As String
Dim colSource As Collection
Dim colMatch As Collection
Dim colOutput As Collection
Dim varSource As Variant
Dim varMatch As Variant
Dim varOutput As Variant
Dim intCounter As Integer
Dim blnMatched As Boolean
Dim strSource As String
Dim strMatch As String
Dim strOutput As String
Set colSource = New Collection
Set colMatch = New Collection
Set colOutput = New Collection
strSource = Replace(rngSource.Value, " ", "")
For Each varSource In Split(strSource, ",")
colSource.Add varSource
Next
' Clean up source data
strMatch = Replace(rngMatch.Value, " ", "")
For Each varSource In Split(strMatch, ",")
colMatch.Add varSource
Next
' Clean up match data
For Each varSource In colSource
blnMatched = False
For Each varMatch In colMatch
If varSource = varMatch Then
blnMatched = True
Exit For
End If
Next
' Note if it's not matched
If Not blnMatched Then
colOutput.Add varSource
End If
Next
' Only output if there's anything present
If colOutput.Count > 0 Then
For Each varOutput In colOutput
strOutput = strOutput & CStr(varOutput) & ", "
Next
strOutput = Left$(strOutput, Len(strOutput) - 2)
End If
ShowMissingZips = strOutput
End Function
To use it, press Alt-F11 to get to the VBA editor. Find your workbook in the project explorer (Ctrl-R if not visible) and on the menu at the top click Insert..., Module.
Paste this code in.
Go back to your workbook and assuming that you've kept the columns as before (A, B & C and with row 2 as the first data row), go to cell C2 and type
=ShowMissingZips(A2,B2)
You should see what you're after. It's not pretty and I'd normally add error handling, but it'll do for a quick fix.
When you save it, be sure to use the XLSM format (Excel 2007+) so that the VBA is retained.
I am a quite new to Excel VBA, and I come from a more... traditional programming background (Java, C). I am having issues with passing a Range Object as a parameter in my User-defined function (see below). The idea of my function is to take several parameters to complete a VLOOKUP of a filtered range.
I may have several syntax issues (I am unsure of my return type and my usage of VLOOKUP), and I would appreciate some guidance on this. See results, more information in my code:
Public Function GETVALUE(screen As String, strEvent As String, dataRange As Range, strDate As String) As String
'ASSUMPTION: dataRange has three columns; first column contains lookup values; Second
' column contains dates for filtering; Third column contains return values
Dim result As String
'remove irrelevant dates in dataRange; apply filter
'ASSUMPTION: This process should return a Range that is removes all Rows that does
'not have strDate in the second column
Dim newRange As Range
'RESULT: Returns #VALUE!. I know this is not the typical := syntax I see in many
'examples but this one apparently compiles, so I use it. I comment this line out
'and try to make the other lines below work with dummy parameters or fixed ranges
newRange = dataRange.AutoFilter(2, strDate)
'Now I try to use the newly filtered, "newRange" and use that in my VLOOKUP
'and return it.
result = [VLOOKUP("*" & screen & "/" & strEvent & "*", newRange, 3, False)]
'I can see an Error 2029 here on Result
GETVALUE = result
'RESULT: Returns #VALUE!
End Function
VLOOKUP ignores any filtering of your data. In other words VLOOKUP will also look in the hidden rows.
I would suggest two alternative approaches:
Copy the visible cells of the filtered range to a new sheet and perform the lookup there:
Set newRange = dataRange.AutoFilter(2, strDate).SpecialCells(xlCellTypeVisible)
set ws = worksheets.Add
ws.Range("A1").Resize(newRange.Rows.Count,newRange.Columns.Count).Value = newRange.Value
etc.
Note that this can not be done in a UDF, you would have to do it in a a Sub.
Store the values in dataRange in a variant array and loop to search for the required value:
Dim arr() as Variant
arr = dataRange.Value
For i = LBound(arr,1) to UBound(arr,1)
If (arr(i,2) = strDate) And (arr(i,1) LIKE "*" & screen & "/" & strEvent & "*"( Then
GETVALUE = arr(i,3)
Exit Function
End If
Next
This I think causes your problem:
result = [VLOOKUP("*" & screen & "/" & strEvent & "*", newRange, 3, False)]
Replace it with this instead:
result = Evaluate("VLOOKUP(*" & screen & "/" & strEvent _
& "*, " & newRange.Address & ", 3, False)")
[] which is shortcut for Evaluate doesn't work on variables.
If it is a direct VLOOKUP like below:
result = [VLOOKUP(D1,Sheet1!$A:$C,3,FALSE)]
it will work. But if you are working with variables as in your example, you have to explicitly state it.
And take note that Evaluate accepts Name argument in a form of string.
So you simply have to concatenate all your strings and then explicitly use Evaluate.
Edit1: Additional Inputs
This will not work as well: newRange = dataRange.AutoFilter(2, strDate).
To pass Objects to a Variable you need to use Set like this.
Set newrange = dataRange.AutoFilter(2, strDate)
On the other hand, AutoFilter method although returning a Range Object fails.
I'm not entirely sure if this can't really be done.
Moving forward, to make your code work, I guess you have to write it this way:
Edit2: Function procedures only returns values, not execute methods
Public Function GETVALUE(screen As String, strEvent As String, rng As Range)
GETVALUE = Evaluate("VLOOKUP(*" & screen & "/" & strEvent & "*, " _
& rng.Address & ", 3, False)")
End Function
To get what you want, use above function in a Sub Procedure.
Sub Test()
Dim dataRange As Range, strDate As String, myresult As String
Set dataRange = Sheet2.Range("A2:E65") 'Assuming Sheet2 as property name.
strDate = "WhateverDateString"
dataRange.AutoFilter 2, strDate
myresult = GETVALUE("String1", "String2", dataRange)
End Sub
Btw, for a faster and less complex way of doing this, try what Portland posted.
Basically you must write :
Getvalue = Application.VLookup( StringVar, RangeVar, ColumnNumberVar)
Vlookup needs your data to be previously ordered in alphabetical order, or it doesn't work.
Excel Developers's approach is a good one too, using a VBA Array.
I can also point the VBA functions FIND, and MATCH, wich will get you the row of searched data, and then you can pull what you need from the 3rd column of that row.
Wichever is faster depends of the size of your range.