VBA Excel concatenating two variable values to form a new variable - vba

I am trying to write a code that reads in multiple entities, catorgorizes and sorts them. Each entity has a type (A, B, C, etc.) that should determine what sheet it gets put into and all of them get put into my "All" sheet. Each time I find an entity of any given type I'd also like to increment a variable specific to that type.
What I'd like to do if find the type and do two things:
Set the current sheet to that type.
Set the counter variable to that type.
Example:
Dim x As Integer, FindSlot As Integer
Dim CurrentSheet As String, CurrentPropertyNumb As String
Dim APropertyNumb As String, BPropertyNumb As String
Dim CPropertyNumb As String
For x = 1 to 2
If x = 1 Then
CurrentSheet = "All"
Else
CurrentSheet = Range("B" & FindSlot)
CurrentPropertyNumb = CurrentSheet & PropertyNumb
End If
Next x
In the else block, CurrentSheet will get set to "A", "B", "C" or whatever the type is. Then I'd like CurrentPropertyNumb to get set to "APropertyNumb" or "BPropertyNumb" etc. Obviously I could do this with several If statements but it would end up being 12 of them which I'd rather avoid plus I think this would be cool! :)
Is there any way to do this or am I being too lofty with my goals?

If you have a series of values which you'd like to index using a string value then a Dictionary is a good fit:
Dim x As Integer, FindSlot As Integer
Dim CurrentSheet As String, CurrentPropertyNumb As String
Dim PropNums as Object
Dim CPropertyNumb As String
Set PropNums = CreateObject("scripting.Dictionary")
For x = 1 to 2
If x = 1 Then
CurrentSheet = "All"
Else
CurrentSheet = Range("B" & FindSlot)
If Not PropNums.Exists(CurrentSheet) Then
PropNums.Add CurrentSheet, 1 '? what are the initial values here?
Else
PropNums(CurrentSheet) = PropNums(CurrentSheet) +1
End If
CurrentPropertyNumb = PropNums(CurrentSheet)
End If
Next x

Related

Check values in Array via VBA Dictionary

I am trying to store an array of unique string values from a certain column. However I can't seem to be able to access the values in the array while the code is running. Would you be able to tell me what additional line of code would I need, to check what is being stored in the array when it is in fact running, and eventually copy those unique values to another sheet? Code below:
Find unique values in Column END DATE so to check whether multiple dates need to be imported:
Sub Find_Unique_Values()
Dim PaceData(), UniqueValues As Variant, r As Long
With PaceDataSheet
PaceData = .UsedRange.value
Set UniqueValues = CreateObject("Scripting.Dictionary")
For r = 2 To UBound(PaceData)
UniqueValues(PaceData(r, datecolumn.Column)) = Empty
Next r
End With
End Sub
Any help is super appreciated. Thanks very much!
However I can't seem to be able to access the values in the array while the code is running. - here is a way to do this easily, using .UsedRange, as in your code:
Option Explicit
Sub TestMe()
Dim PaceData As Variant
Dim someRange As Range
Set someRange = Worksheets(1).UsedRange 'A1:C10
PaceData = someRange
Dim myRows As Long: myRows = someRange.Rows.Count
Dim myCol As Long: myCol = someRange.Columns.Count
Dim cnt1 As Long
Dim cnt2 As Long
For cnt1 = 1 To myRows
For cnt2 = 1 To myCol
Debug.Print PaceData(cnt1, cnt2)
Next cnt2
Next cnt1
End Sub
The UsedRange is "translated" as a 2-dimensional array like this:
and knowing the Rows.Count and the Columns.Count of the passed range, we get the dimensions.

Excel VBA - Formula Counting Unique Value error

I am trying to calculate the count of Unique values based on a condition.
For example,
For a value in column B, I am trying to count the Unique values in Column C through VBA.
I know how to do it using Excel formula -
=SUMPRODUCT((B2:B12<>"")*(A2:A12=32)/COUNTIF(B2:B12,B2:B12))
that value for 32 is dynamic - Programmatically I am calling them inside my vba code as Name
This is my code :
Application.WorksheetFunction.SumProduct((rng <> "") * (rng2 = Name) / CountIfs(rng, rng))
This is the sample data with the requirement
Alternatively, I Concatenated both the columns for keeping it simple and hoping to identify the Unique values which starts with name* method.
I don't know where I am going wrong. Kindly share your thoughts.
You may try something like this...
Function GetUniqueCount(Rng1 As Range, Lookup As String) As Long
Dim x, dict
Dim i As Long, cnt As Long
Set dict = CreateObject("Scripting.Dictionary")
x = Rng1.Value
For i = 1 To UBound(x, 1)
If x(i, 1) = Lookup Then
dict.Item(x(i, 1) & x(i, 2)) = ""
End If
Next i
GetUniqueCount = dict.Count
End Function
Then you can use it like below...
=GetUniqueCount($A$2:$B$10,C2)
Where A2:B10 is the data range and C2 is the name criteria.
I'd put the values into an array, create a temporary 2nd array and only add values to this array if they are not already present, and then replace the original array. Then it's just a simple matter to sum the unique values:
Sub Unique
dim arr(10) as variant, x as variant
dim arr2() as variant
for x = 1 to 10 ' or whatever
arr(x) = cells(x, 1) ' or whatever
next x
arr2 = UniqueValuesArray(arr)
' now write some code to count the unique values, you get the idea
End Sub
Function UniqueValuesArray(arr As Variant) As Variant()
Dim currentRow, arrpos As Long
Dim uniqueArray() As Variant
Dim x As Long
arrpos = 0
ReDim uniqueArray(arrpos)
For x = 0 To UBound(arr)
If UBound(Filter(uniqueArray, arr(x))) = -1 Then
ReDim Preserve uniqueArray(arrpos)
uniqueArray(arrpos) = arr(x)
arrpos = arrpos + 1
End If
Next x
UniqueValuesArray = uniqueArray
End Function

How to create dynamic variable names VBA

I am trying to create a dynamic number of variables in VBA based on the value in a cell.
Essentially what I'd like to end up with is something like Team1, Team2... to TeamX.
Any help is greatly appreciated
Dim i, x As Integer
Set x = Range("J4").Value
Dim Team(1 To x) As String
Dim Manager(1 To x) As String
Range("A3").Select
For i = 1 To x
Dim Team(i) As Integer
A dictionary would probably help in this case, it's designed for scripting, and while it won't let you create "dynamic" variables, the dictionary's items are dynamic, and can serve similar purpose as "variables".
Dim Teams as Object
Set Teams = CreateObject("Scripting.Dictionary")
For i = 1 To x
Teams(i) = "some value"
Next
Later, to query the values, just call on the item like:
MsgBox Teams(i)
Dictionaries contain key/value pairs, and the keys must be unique. Assigning to an existing key will overwrite its value, e.g.:
Teams(3) = "Detroit"
Teams(3) = "Chicago"
Debug.Print Teams(3) '## This will print "Chicago"
You can check for existence using the .Exist method if you need to worry about overwriting or not.
If Not Teams.Exist(3) Then
Teams(3) = "blah"
Else:
'Teams(3) already exists, so maybe we do something different here
End If
You can get the number of items in the dictionary with the .Count method.
MsgBox "There are " & Teams.Count & " Teams.", vbInfo
A dictionary's keys must be integer or string, but the values can be any data type (including arrays, and even Object data types, like Collection, Worksheet, Application, nested Dictionaries, etc., using the Set keyword), so for instance you could dict the worksheets in a workbook:
Dim ws as Worksheet, dict as Object
Set dict = CreateObject("Scripting.Dictionary")
For each ws in ActiveWorkbook.Worksheets
Set dict(ws.Name) = ws
Next
This will get you started. But before you start I recommend watching these WiseOwlTutorials tutorial on Youtube:
Selecting Cells (Range, Cells, Activecell, End, Offset)
Worksheets, Charts and Sheets
Variables
Arrays
Dim i, x As Integer
x = Range("J4").Value
Dim Team() As Integer
Dim Manager() As String
ReDim Team(1 To x) As Integer
ReDim Manager(1 To x) As String
Range("A3").Select
For i = 1 To x
Team(i) = i
Next

Selecting one column from each row in a table

I have a table structured (Table Name: Table2) like below:
Using VBA, I want to select ONLY a single column value of the current row by iterating over each row.
Here is the code and I wrote:
Function findColumnValue(strColCombIdent As String, strColumnName As String) As String
On Error Resume Next
Dim strRetResult As String
Dim wsMapMasterRefSheet As Worksheet
'Referes to the table Table2.
Dim loMapMaster As ListObject
Set wsMapMasterRefSheet = ThisWorkbook.Worksheets("Sheet3")
Set loMapMaster = wsMapMasterRefSheet.ListObjects("Table2")
'All rows of the table Table2
Dim rAllRows As Range
Set rAllRows = loMapMaster.DataBodyRange
'Holds one row from the databody range for processing.
Dim rCurrRow As Range
'Process data
Dim strTemp As String
For Each rCurrRow In rAllRows
strTemp = rCurrRow.Columns(2)
Debug.Print strTemp
Next rCurrRow
findColumnValue = strRetResult
End Function
I was hoping to get results like below (ONLY the value of the column 2):
1.5
1.5
1.8
4
3
3
1
2
10
12
5
7
Instead I end up with something like this (All values from column#2 onwards, for each processing row.)
1.5
0.045150462962963
1.5
4.52083333333333E-02
1.8
4.72685185185185E-02
4
0.168090277777778
3
3.1
3
8.47800925925926E-02
1
4.16666666666667E-02
2
8.33449074074074E-02
10
10.1.1.1
12
1.3.4.5
5
0.212511574074074
7
8.54166666666667E-02
Using
strTemp = rCurrRow.Columns(1, 2)
instead of
strTemp = rCurrRow.Columns(2)
Causes runtime error 1004
Since each iteration points to a range object in the For loop; I was thinking using
rCurrRow.Columns(2)
will point to current Row's column#2 and hence print out only the column's value.
Is my logic misplaced?
One additional question:
Why does the MSDN Excel Reference guide describes Columns as a Property; where as clearly the "Columns" usage clearly takes parameters
Here is the link I referred:
http://msdn.microsoft.com/en-us/library/office/ff197454(v=office.15).aspx
Either specify you want to iterate rows:
For Each rCurrRow In rAllRows.Rows
or only look at the ListRows in the first place:
Function findColumnValue(strColCombIdent As String, strColumnName As String) As String
On Error Resume Next
Dim strRetResult As String
Dim wsMapMasterRefSheet As Worksheet
'Referes to the table Table2.
Dim loMapMaster As ListObject
Set wsMapMasterRefSheet = ThisWorkbook.Worksheets("Sheet3")
Set loMapMaster = wsMapMasterRefSheet.ListObjects("Table2")
'All rows of the table Table2
Dim rAllRows As ListRows
Set rAllRows = loMapMaster.ListRows
'Holds one row from the databody range for processing.
Dim rCurrRow As ListRow
'Process data
Dim strTemp As String
For Each rCurrRow In rAllRows
strTemp = rCurrRow.Range(, 2)
Debug.Print strTemp
Next rCurrRow
findColumnValue = strRetResult
End Function
You can call your variable rCurrRow all you want; VBA still won't know that you mean for it to contain an entire row of range rAllRows. It just assumes that rCurrRow represents one cell, such that For Each rCurrRow In rAllRows means "for each individual cell in this range".
What you need to do is limit the range being looped through. This should work; not tested.
For Each rCurrRow In rAllRows.Columns(2)
strTemp = rCurrRow
Debug.Print strTemp
Next rCurrRow
In fact I wouldn't call that variable rCurrRow at all; if you're going to use it in this way, call it e.g. cell instead.
EDIT: now that you have clarified your question in a comment below, you could do this:
For i = 1 To rAllRows.Rows.Count
Set rCurrRow = rAllRows.Rows(i)
strTemp = rCurrRow.Cells(1,2)
Debug.Print strTemp
Next i
But even better and faster would be to load the entire range to a two-dimensional Variant array at once, and loop over that array — much faster than looping over many cells.
Dim v As Variant
v = rAllRows ' load entire range to a 2D array
For i = 1 To UBound(v,1)
strTemp = v(i,2)
Debug.Print strTemp
Next i
Why does the MSDN Excel Reference guide describes Columns as a Property; where as clearly the "Columns" usage clearly takes parameters
Both methods and properties can take parameters. The distinction is more or less as follows:
Properties are things that you can get (like a range's Address, which takes no parameter, or subrange such as Column or Row or Cells, which do) and/or set (like a range's .Interior.Color, or .Hidden status). They are usually nouns.
Methods are things that do something to/with the range, and as such are usually verbs. Like .Select (takes no parameters) or .Copy (takes one parameter) or even .Speak.

How to find and replace multiple values in Excel?

I have for example a column of 18000 domains and another list with 210.000 domain which those 18000 domains can be found. I need to do smth like CTRL + H, for find and replace, and in the find field I need to add the entire 18000 domains: smth like * domain1.com *, * domain2.com *, * domain3.com * and replace them with blank space. Tried this with find and replace from excel but it doesn't work to add more than 1 value in the Find field. How can i do it for multiple values?
VBA solution
You will need to change the two sheet references (data and edit sheet) data = source, edit = destination. I've also set the replace string to a variable so you can change this from an empty string if required.
If you need any other logic (ie Trim the strings before compare or a change to the strings case comparison) the code should be reasonably easy to tweak.
Hope this helps.
Sub ReplaceValues()
Dim dataSht As Worksheet
Dim editSht As Worksheet
Dim dataRange As Range
Dim dataColumn As Long
Dim editColumn As Long
Dim dataEndRow As Long
Dim editEndRow As Long
'sheet that holds all the values we want to find
Set dataSht = Sheet2
'sheet we want to edit
Set editSht = Sheet1
Dim replaceValue As String
'replace value is empty string
replaceValue = ""
'set the column of the data sheet to A
dataColumn = 1
'set the colmun of the sheet to edit to A
editColumn = 1
dataEndRow = dataSht.Cells(dataSht.Rows.count, dataColumn).End(xlUp).Row
editEndRow = editSht.Cells(editSht.Rows.count, editColumn).End(xlUp).Row
'this is the range of the data that we're looking for
Set dataRange = dataSht.Range(dataSht.Cells(1, dataColumn), dataSht.Cells(dataEndRow, dataColumn))
Dim count As Long
Dim val As String
For i = 1 To editEndRow
val = editSht.Cells(i, editColumn).Value
count = Application.WorksheetFunction.CountIf(dataRange, val)
If count > 0 And Trim(val) <> "" Then
editSht.Cells(i, editColumn).Value = replaceValue
End If
Next i
End Sub
You can use wildcards with find/replace. So in your situation you should be able to use something like
domain* in the find what Field and nothing in the Replace field