Change Number Generation Sequence - excel

I am trying to write a VBA script where for every 5 counts of the number, the numbering starts back at 1. I'm taking the user input to loop through how many times the user wants to have the number set. Problem is, the numbers are not restarting from 1. Please help!!
Example: user enters 2 times, expected result is-
1
2
3
4
5
1
2
3
4
5
Code:
Dim myValue, myRows As Variant
Dim i, j, x, y, m, n, k As Integer
Sub Button1_Click()
'myValue = InputBox("Enter a starting number")
myRows = InputBox("Enter how times to repeat")
'Call repeat(myRows)
Call repeat2(myRows)
End Sub
Sub repeat2(rep1)
m = 0
n = 0
k = 1
For k = 1 To myRows
'k = k + 1
m = m + 5
n = n + 5
Call autogen2(k)
Next k
End Sub
Sub autogen2(z)
For k = 1 To m + 5
Range("F" & k).Select
ActiveCell.FormulaR1C1 = k
ActiveCell.Offset(1, 0).Select
Next k
End Sub

Your method seems overly complicated and loops through every cell instead of looping through groups of the target cells.
To simplify the math in calculating the target, I'd suggest Option Base 1 as a compiler directive in the code sheet's declarations area. This changes the default for an array from zero based to one based; i.e. from 0 to 4 to 1 to 5.
By using LBound and UBound, you can change the array without cahnging any of the other code that shapes the target.
Option Explicit
Option Base 1
Sub Button1_Click()
Dim myRows As Long, seq As Variant
'myValue = InputBox("Enter a starting number")
myRows = Application.InputBox(prompt:="Enter how times to repeat", Type:=xlNumbers)
If IsNumeric(myRows) Then
seq = Array(1, 2, 3, 4, 5)
autogen seq, myRows
End If
End Sub
Sub autogen(vals As Variant, n As Long)
Dim k As Long
For k = LBound(vals) To (n * UBound(vals)) Step UBound(vals)
Cells(1, "F").Offset(k - 1, 0).Resize(UBound(vals), 1) = _
Application.Transpose(vals)
Next k
End Sub

Number Generator (Fast)
Adjust the constants (Const) to fit your needs.
Option Explicit
Sub Button1_Click()
Dim myRows As Variant
myRows = InputBox("Enter how times to repeat")
If IsNumeric(myRows) Then
Repeat CLng(myRows)
End If
End Sub
Sub Repeat(Rows As Long)
Const cRange As String = "F1" ' First Cell Range Address
Const cStart As Long = 1 ' Starting Value
Const cEnd As Long = 5 ' Ending Value
Dim vntT As Variant ' Target Array
Dim i As Long ' Rows Counter
Dim k As Long ' Value Counter
Dim m As Long ' Target Array Row Counter
' Resize Target Array: Number of Values times Rows.
ReDim vntT(1 To (cEnd - cStart + 1) * Rows, 1 To 1)
' Loop through Rows.
For i = 1 To Rows
' Loop through Values.
For k = cStart To cEnd
' Count Target Array Row.
m = m + 1
' Write current Value to element in current Target Array Row.
vntT(m, 1) = k
Next
Next
With Range(cRange)
' Clear contents of Target Column Range from First Cell Range to
' bottom cell.
.Resize(.Worksheet.Rows.Count - .Row + 1).ClearContents
' Calculate Target Range: resize First Cell Range by Target Array Rows.
' Copy Target Array to Target Range.
.Resize(m) = vntT
End With
End Sub

One more:
Dim myRows As Long, tot As Long
myRows = Application.InputBox("Enter how times to repeat")
tot = myRows * 5
With ActiveSheet.Range("K1").Resize(tot, 1)
.FormulaArray = "=1+MOD(ROW(1:" & tot & ")-1,5)"
.Value = .Value
End With

Related

Pick random names from different lists excel VBA

I would like to pick random names from columns in excel like this :
-In the first sheet "Inscrp" is where the lists are, and the second sheet "Tirage" is where the results of the picking.
-Column A in the sheet "Tirage" should pick random names from column A in the sheet "Inscrp" and the same for the column B, C , till the number of columns I chose
I managed to do this with only the first column and here is the code :
Sub PickNamesAtRandom()
Dim HowMany As Integer
Dim NoOfNames As Long
Dim RandomNumber As Integer
Dim Names() As String 'Array to store randomly selected names
Dim i As Byte
Dim CellsOut As Long 'Variable to be used when entering names onto worksheet
Dim ArI As Byte 'Variable to increment through array indexes
Application.ScreenUpdating = False
HowMany = 5
CellsOut = 8
ReDim Names(1 To HowMany) 'Set the array size to how many names required
NoOfNames = Application.CountA(Worksheets("Inscrp").Range("A3:A100")) - 1 ' Find how many names in the list
i = 1
Do While i <= HowMany
RandomNo:
RandomNumber = Application.RandBetween(3, NoOfNames + 1)
'Check to see if the name has already been picked
For ArI = LBound(Names) To UBound(Names)
If Names(ArI) = Worksheets("Inscrp").Cells(RandomNumber, 1).Value Then
GoTo RandomNo
End If
Next ArI
Names(i) = Worksheets("Inscrp").Cells(RandomNumber, 1).Value ' Assign random name to the array
i = i + 1
Loop
'Loop through the array and enter names onto the worksheet
For ArI = LBound(Names) To UBound(Names)
Worksheets("Tirage").Cells(CellsOut, 1) = Names(ArI)
CellsOut = CellsOut + 1
Next ArI
Application.ScreenUpdating = True
End Sub
Please, test the next code. If I correctly understand your nee, it will extract HowMany random numbers from each column (nrCol) of "Inscrip" sheet and placed starting from CellsOut in sheet "Tirage". The already extracted name is eliminated from the array where it used to exist (to avoid repeated names). The ranges ar placed in arrays and due to that, the code should be very fast mostly working in memory, even for large ranges:
Sub PickNamesAtRandom()
Dim shI As Worksheet, lastR As Long, shT As Worksheet, HowMany As Long
Dim rndNumber As Integer, Names() As String, i As Long, CellsOut As Long
HowMany = 5: CellsOut = 8
Set shI = Worksheets("Inscrp")
Set shT = Worksheets("Tirage")
Dim col As Long, arrCol, filt As String, nrCol As Long
nrCol = 2 'number of columns to be returned. It can be changed and also be calculated...
For col = 1 To nrCol
lastR = shI.cells(shI.rows.count, col).End(xlUp).Row 'last row in column to be processed
If lastR >= HowMany + 2 Then '+ 2 because the range is build starting with the third row...
arrCol = Application.Transpose(shI.Range(shI.cells(3, col), shI.cells(lastR, col)).Value2) 'place the range in a 1D array
ReDim Names(1 To HowMany) 'Set the array size to how many names required
For i = 1 To UBound(Names)
tryAgain:
Randomize
rndNumber = Int((UBound(arrCol) - LBound(arrCol) + 1) * Rnd + LBound(arrCol))
If arrCol(rndNumber) = "" Then GoTo tryAgain
Names(i) = arrCol(rndNumber)
filt = arrCol(rndNumber) & "##$$#": arrCol(rndNumber) = filt
arrCol = filter(arrCol, filt, False) 'eliminate the already used name from the array
Next i
shT.cells(CellsOut, col).Resize(UBound(Names), 1).Value2 = Application.Transpose(Names)
End If
Next col
MsgBox "Ready..."
End Sub
If something unclear, do not hesitate to ask for clarifications...

How to create a function that returns an range

I am looking to create a function that will take 2 ranges (of the same dimensions), and take the difference between the cell from one range and the corresponding cell in the other range, and then create a new range with all of the differences. Are there any obvious problems? If i select and crtl + sht + enter, the range fills with "#Value!"
This is what i have so far (assuming the ranges are 4 by 4s):
Function Compare_Ranges(range_1 As Range, range_2 As Range) As Range
Dim output_data As Range
Dim i As Integer
Dim j As Integer
Dim col As String
For i = 1 To 4 'looping through the columns
col = Col_Letter(i)
For j = 1 To 4 'looping through the rows
Set output_data(Col_Letter(i) & j) = range_1(Col_Letter(i) & j).Value - range_2(Col_Letter(i) & j).Value
Next j
Next i
Compare_Ranges = output_data
End Function
Where the function Col_Letter returns the correponding letter of the alphabet:
Function Col_Letter(lngCol As Integer) As String
Dim vArr
vArr = Split(Cells(1, lngCol).Address(True, False), "$")
Col_Letter = vArr(0)
End Function
Here is a version of your function that takes two ranges of the same size and returns an array with the same dimensions that holds the difference between each corresponding cell in the input ranges.
Function Compare_Ranges(range_1 As Range, range_2 As Range) As Variant
Dim output_data() As Variant
Dim c As Integer
Dim r As Integer
ReDim output_data(1 To range_1.Rows.Count, 1 To range_1.Columns.Count)
For c = 1 To UBound(output_data, 2) 'looping through the columns
For r = 1 To UBound(output_data, 1) 'looping through the rows
output_data(r, c) = range_1.Cells(r, c).Value - range_2.Cells(r, c).Value
Next
Next
Compare_Ranges = output_data
End Function
If you want to put this in a cell, you will need to press CTRL+ENTER after entiering the following in a cell:
=Compare_Ranges(A1:A7,B1:B7)
The function returns an array, so if you want to catch it's results by calling it in another sub procedure, you need the following"
Dim data as variant
data = Compare_Ranges(range("a1:a7"),range("b1:b7"))
I am not sure if I got this right but I hope at least will help you to get there. The function takes any two ranges and calculate the difference between them and store the result into an array.
Function Compare_Ranges(range_1 As Range, range_2 As Range, ByVal y As Long) As Variant
Dim j As Long
Dim col As String
Dim one As Object, two As Object, three As Variant
Set one = CreateObject("Scripting.Dictionary")
Set two = CreateObject("Scripting.Dictionary")
j = 0
For Each cell In range_1
one.Add Key:=j, Item:=cell.Value
j = j + 1
Next
j = 0
For Each cell In range_2
two.Add j, cell.Value
j = j + 1
Next
ReDim three(0 To j) As Variant
For i = 0 To j
three(i) = one(i) - two(i)
Next
Compare_Ranges = three(y)
End Function
Then you can use the code in the sub to populate them in any range you like.
Sub result()
Dim one As Range, two As Range, three As Range
Dim j As Long
Set one = Worksheets("Sheet1").Range("A1:A4")
Set two = Worksheets("Sheet1").Range("B1:B4")
Set result = Worksheets("Sheet1").Range("D8:D11")
j = 0
For i = three.Row To ((result.Row + result.Rows.Count) - 1)
Worksheets("Sheet1").Cells(i, result.Column) = Compare_Ranges(one, two, j)
j = j + 1
Next
End Sub

Do a loop with multiple constant condition VBA

I am trying to do a loop but I'm a little stuck.
Sub Macro()
Range("A392: A401").Value = Range("N2")
Range("A402: A411").Value = Range("N3")
Range("A412: A421").Value = Range("N4")
Range("A422: A431").Value = Range("N5")
....
I need to repeat this logic ( On column A to set a value for each 10 rows) this value will be from Column N from 1 to 1 until it finds an empty row ...
I'm not being able to do the loop with these multiples conditions, would you please help me ?
Thanks a lot!
Range.Offset is a great method to manipulate ranges. Using it, we can automate the ranges to move down the sheet with each loop.
Sub Macro()
Dim i As Long
While Range("N2").Offset(i) <> ""
'Offset will shift N2 down by one each loop
'Offset will shift the 10 cell range down by 10 on each loop
Range("A392: A401").Offset(i * 10).Value = Range("N2").Offset(i)
i = i + 1
Wend
End Sub
I'm not quite sure what's your actual intention:
Repeat all values in column N2:N5 10 times and append the whole data block to the first free cell in column A.
Repeat all non-empty values in column N 10 times and write them to a fixed target starting with cell A392.
In both cases you can prefill an array and write it to the defined target in column A. Looping through an array has some speed advantages whereas looping through a range by means of VBA can be time consuming.
The direct (untested) copying of whole blocks as shown by #Toddleson can improve this behaviour, depending on total range sizes.
Case 1
Sub Example1()
Const RowsCount As Long = 10
With Sheet1 ' << change to your project's sheet (Code)Name
'get values to repeat & count them
Dim vals: vals = .Range("N2:N5")
Dim cnt As Long: cnt = UBound(vals)
'provide for 1-based 2-dim results array
Dim results
ReDim results(1 To RowsCount * cnt, 1 To 1)
'fill array with repeated values
Dim i As Long, j As Long
For i = 1 To cnt
For j = 1 To RowsCount
results((i - 1) * RowsCount + j, 1) = vals(i, 1)
Next j
Next i
'append above data block
Dim nxtRow As Long
nxtRow = .Range("A" & .Rows.Count).End(xlUp).Row + 1
.Range("A" & nxtRow).Resize(UBound(results), 1) = results
End With
End Sub
Case 2
Sub Example2()
Const RowsCount As Long = 10
With Sheet1 ' << change to your project's sheet (Code)Name
'get values to repeat & count them
Dim lastRow As Long
lastRow = .Range("N" & .Rows.Count).End(xlUp).Row
Dim vals: vals = .Range("N2:N" & lastRow)
Dim ValsCount As Long: ValsCount = UBound(vals)
'provide for 1-based 2-dim results array
Dim results
ReDim results(1 To RowsCount * ValsCount, 1 To 1)
'fill array with repeated values
Dim i As Long, j As Long, ii As Long
For i = 1 To ValsCount
If Len(vals(i, 1)) > 0 Then ' check if non-empty value in column N
ii = ii + 1
For j = 1 To RowsCount
results((ii - 1) * RowsCount + j, 1) = vals(i, 1)
Next j
End If
Next i
'write data block to fixed target starting with A392
.Range("A392").Resize(UBound(results), 1) = results
End With
End Sub

Get the Row Positions without Looping

Is it possible to get the row positions in a large table in excel without performing a loop?
What I am trying to do is to click on a particular ID, and then the last 3 records from the same ID will be shown in UI.
I am beginner in programming and have no idea how to do this aside from looping method (which is very resource and memory-intensive considering we are looping a large and growing table of 100k rows in every single click from user).
For example: If user is clicking "A123" then we know that their row positions are : 5, 8 , 10
Same as CDP1802 posted but faster finding of last 3 rows.
Sub FilteredAdvanced()
Const nValues As Long = 3 'amount of rows you want to find from the end
Dim ar() As Long
ReDim ar(nValues - 1) As Long
' apply filter
With Sheet1
.AutoFilterMode = False
.UsedRange.AutoFilter 1, "A123"
Dim rng As Range
Set rng = .UsedRange.Columns(1).SpecialCells(xlCellTypeVisible)
.UsedRange.AutoFilter 'remove filter
End With
Dim n As Long
n = nValues - 1
Dim iArea As Long
For iArea = rng.Areas.Count To 1 Step -1
Dim iRow As Long
For iRow = rng.Areas(iArea).Rows.Count To 1 Step -1
ar(n) = rng.Areas(iArea).Rows(iRow).Row
n = n - 1
If n < 0 Then Exit For
Next iRow
If n < 0 Then Exit For
Next iArea
Dim j As Long
For j = 0 To nValues - 1
Debug.Print ar(j)
Next
End Sub
Looping filtered a list
Option Explicit
Sub Filtered()
Dim rng as Range, ID As Range, a As Range
Dim ar(2) As Long, i As Integer, j As Integer
' apply filter
With Sheet1
.AutoFilterMode = False
.UsedRange.AutoFilter 1, "A123"
Set rng = .UsedRange.Columns(1).SpecialCells(xlCellTypeVisible)
End With
' count
For Each a In rng.Areas
For Each ID In a.Cells
If ID.Row > 1 Then
i = (i + 1) Mod 3
ar(i) = ID.Row
End If
Next
Next
For j = 1 To 3
Debug.Print ar((j + i) Mod 3)
Next
End Sub

Populate unique values into a VBA array from Excel

Can anyone give me VBA code that will take a range (row or column) from an Excel sheet and populate a list/array with the unique values,
i.e.:
table
table
chair
table
stool
stool
stool
chair
when the macro runs would create an array some thing like:
fur[0]=table
fur[1]=chair
fur[2]=stool
Sub GetUniqueAndCount()
Dim d As Object, c As Range, k, tmp As String
Set d = CreateObject("scripting.dictionary")
For Each c In Selection
tmp = Trim(c.Value)
If Len(tmp) > 0 Then d(tmp) = d(tmp) + 1
Next c
For Each k In d.keys
Debug.Print k, d(k)
Next k
End Sub
In this situation I always use code like this (just make sure delimeter you've chosen is not a part of search range)
Dim tmp As String
Dim arr() As String
If Not Selection Is Nothing Then
For Each cell In Selection
If (cell <> "") And (InStr(tmp, cell) = 0) Then
tmp = tmp & cell & "|"
End If
Next cell
End If
If Len(tmp) > 0 Then tmp = Left(tmp, Len(tmp) - 1)
arr = Split(tmp, "|")
Combining the Dictionary approach from Tim with the variant array from Jean_Francois below.
The array you want is in objDict.keys
Sub A_Unique_B()
Dim X
Dim objDict As Object
Dim lngRow As Long
Set objDict = CreateObject("Scripting.Dictionary")
X = Application.Transpose(Range([a1], Cells(Rows.Count, "A").End(xlUp)))
For lngRow = 1 To UBound(X, 1)
objDict(X(lngRow)) = 1
Next
Range("B1:B" & objDict.Count) = Application.Transpose(objDict.keys)
End Sub
This is the old-school way of doing it.
It will execute faster than looping through cells (e.g. For Each cell In Selection) and will be reliable no matter what, as long you have a rectangular selection (i.e. not Ctrl-selecting a bunch of random cells).
Sub FindUnique()
Dim varIn As Variant
Dim varUnique As Variant
Dim iInCol As Long
Dim iInRow As Long
Dim iUnique As Long
Dim nUnique As Long
Dim isUnique As Boolean
varIn = Selection
ReDim varUnique(1 To UBound(varIn, 1) * UBound(varIn, 2))
nUnique = 0
For iInRow = LBound(varIn, 1) To UBound(varIn, 1)
For iInCol = LBound(varIn, 2) To UBound(varIn, 2)
isUnique = True
For iUnique = 1 To nUnique
If varIn(iInRow, iInCol) = varUnique(iUnique) Then
isUnique = False
Exit For
End If
Next iUnique
If isUnique = True Then
nUnique = nUnique + 1
varUnique(nUnique) = varIn(iInRow, iInCol)
End If
Next iInCol
Next iInRow
'// varUnique now contains only the unique values.
'// Trim off the empty elements:
ReDim Preserve varUnique(1 To nUnique)
End Sub
Profiting from the MS Excel 365 function UNIQUE()
In order to enrich the valid solutions above:
Sub ExampleCall()
Dim rng As Range: Set rng = Sheet1.Range("A2:A11") ' << change to your sheet's Code(Name)
Dim a: a = rng
a = getUniques(a)
arrInfo a
End Sub
Function getUniques(a, Optional ZeroBased As Boolean = True)
Dim tmp: tmp = Application.Transpose(WorksheetFunction.Unique(a))
If ZeroBased Then ReDim Preserve tmp(0 To UBound(tmp) - 1)
getUniques = tmp
End Function
OK I did it finally:
Sub CountUniqueRecords()
Dim Array() as variant, UniqueArray() as variant, UniqueNo as Integer,
Dim i as integer, j as integer, k as integer
Redim UnquiArray(1)
k= Upbound(array)
For i = 1 To k
For j = 1 To UniqueNo + 1
If Array(i) = UniqueArray(j) Then GoTo Nx
Next j
UniqueNo = UniqueNo + 1
ReDim Preserve UniqueArray(UniqueNo + 1)
UniqueArray(UniqueNo) = Array(i)
Nx:
Next i
MsgBox UniqueNo
End Sub
one more way ...
Sub get_unique()
Dim unique_string As String
lr = Sheets("data").Cells(Sheets("data").Rows.Count, 1).End(xlUp).Row
Set range1 = Sheets("data").Range("A2:A" & lr)
For Each cel In range1
If Not InStr(output, cel.Value) > 0 Then
unique_string = unique_string & cel.Value & ","
End If
Next
End Sub
This VBA function returns an array of distinct values when passed either a range or a 2D array source
It defaults to processing the first column of the source, but you can optionally choose another column.
I wrote a LinkedIn article about it.
Function DistinctVals(a, Optional col = 1)
Dim i&, v: v = a
With CreateObject("Scripting.Dictionary")
For i = 1 To UBound(v): .Item(v(i, col)) = 1: Next
DistinctVals = Application.Transpose(.Keys)
End With
End Function
The old school method was my favourite option. Thank you. And it was indeed fast. But I didn't use redim. Here though is my real world example where I accumulate values for each unique "key" found in a column and move it into a array (say for an employee and values are hours worked per day). Then I put each key with its final values into a totals area on the active sheet. I've commented extensively for anyone who wants painful detail on what is happening here. Limited error checking is done by this code.
Sub GetActualTotals()
'
' GetActualTotals Macro
'
' This macro accumulates values for each unique employee from the active
' spreadsheet.
'
' History
' October 2016 - Version 1
'
' Invocation
' I created a button labeled "Get Totals" on the Active Sheet that invokes
' this macro.
'
Dim ResourceName As String
Dim TotalHours As Double
Dim TotalPercent As Double
Dim IsUnique As Boolean
Dim FirstRow, LastRow, LastColumn, LastResource, nUnique As Long
Dim CurResource, CurrentRow, i, j As Integer
Dim Resource(1000, 2) As Variant
Dim Rng, r As Range
'
' INITIALIZATIONS
'
' These are index numbers for the Resource array
'
Const RName = 0
Const TotHours = 1
Const TotPercent = 2
'
' Set the maximum number of resources we'll
' process.
'
Const ResourceLimit = 1000
'
' We are counting on there being no unintended data
' in the spreadsheet.
'
' It won't matter if the cells are empty though. It just
' may take longer to run the macro.
' But if there is data where this macro does not expect it,
' assume unpredictable results.
'
' There are some hardcoded values used.
' This macro just happens to expect the names to be in Column C (or 3).
'
' Get the last row in the spreadsheet:
'
LastRow = Cells.Find(What:="*", _
After:=Range("C1"), _
LookAt:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Row
'
' Furthermore, this macro banks on the first actual name to be in C6.
' so if the last row is row 65, the range we'll work with
' will evaluate to "C6:C65"
'
FirstRow = 6
Rng = "C" & FirstRow & ":C" & LastRow
Set r = Range(Rng)
'
' Initialize the resource array to be empty (even though we don't really
' need to but I'm old school).
'
For CurResource = 0 To ResourceLimit
Resource(CurResource, RName) = ""
Resource(CurResource, TotHours) = 0
Resource(CurResource, TotPercent) = 0
Next CurResource
'
' Start the resource counter at 0. The counter will represent the number of
' unique entries.
'
nUnique = 0
'
' LET'S GO
'
' Loop from the first relative row and the last relative row
' to process all the cells in the spreadsheet we are interested in
'
For i = 1 To LastRow - FirstRow
'
' Loop here for all unique entries. For any
' new unique entry, that array element will be
' initialized in the second if statement.
'
IsUnique = True
For j = 1 To nUnique
'
' If the current row element has a resource name and is already
' in the resource array, then accumulate the totals for that
' Resource Name. We then have to set IsUnique to false and
' exit the for loop to make sure we don't populate
' a new array element in the next if statement.
'
If r.Cells(i, 1).Value = Resource(j, RName) Then
IsUnique = False
Resource(j, TotHours) = Resource(j, TotHours) + _
r.Cells(i, 4).Value
Resource(j, TotPercent) = Resource(j, TotPercent) + _
r.Cells(i,5).Value
Exit For
End If
Next j
'
' If the resource name is unique then copy the initial
' values we find into the next resource array element.
' I ignore any null cells. (If the cell has a blank you might
' want to add a Trim to the cell). Not much error checking for
' the numerical values either.
'
If ((IsUnique) And (r.Cells(i, 1).Value <> "")) Then
nUnique = nUnique + 1
Resource(nUnique, RName) = r.Cells(i, 1).Value
Resource(nUnique, TotHours) = Resource(nUnique, TotHours) + _
r.Cells(i, 4).Value
Resource(nUnique, TotPercent) = Resource(nUnique, TotPercent) + _
r.Cells(i, 5).Value
End If
Next i
'
' Done processing all rows
'
' (For readability) Set the last resource counter to the last value of
' nUnique.
' Set the current row to the first relative row in the range (r=the range).
'
LastResource = nUnique
CurrentRow = 1
'
' Populate the destination cells with the accumulated values for
' each unique resource name.
'
For CurResource = 1 To LastResource
r.Cells(CurrentRow, 7).Value = Resource(CurResource, RName)
r.Cells(CurrentRow, 8).Value = Resource(CurResource, TotHours)
r.Cells(CurrentRow, 9).Value = Resource(CurResource, TotPercent)
CurrentRow = CurrentRow + 1
Next CurResource
End Sub
The VBA script below looks for all unique values from cell B5 all the way down to the very last cell in column B… $B$1048576. Once it is found, they are stored in the array (objDict).
Private Const SHT_MASTER = “MASTER”
Private Const SHT_INST_INDEX = “InstrumentIndex”
Sub UniqueList()
Dim Xyber
Dim objDict As Object
Dim lngRow As Long
Sheets(SHT_MASTER).Activate
Xyber = Application.Transpose(Sheets(SHT_MASTER).Range([b5], Cells(Rows.count, “B”).End(xlUp)))
Sheets(SHT_INST_INDEX).Activate
Set objDict = CreateObject(“Scripting.Dictionary”)
For lngRow = 1 To UBound(Xyber, 1)
If Len(Xyber(lngRow)) > 0 Then objDict(Xyber(lngRow)) = 1
Next
Sheets(SHT_INST_INDEX).Range(“B1:B” & objDict.count) = Application.Transpose(objDict.keys)
End Sub
I have tested and documented with some screenshots of the this solution. Here is the link where you can find it....
http://xybernetics.com/techtalk/excelvba-getarrayofuniquevaluesfromspecificcolumn/
If you don't mind using the Variant data type, then you can use the in-built worksheet function Unique as shown.
sub unique_results_to_array()
dim rng_data as Range
set rng_data = activesheet.range("A1:A10") 'enter the range of data here
dim my_arr() as Variant
my_arr = WorksheetFunction.Unique(rng_data)
first_val = my_arr(1,1)
second_val = my_arr(2,1)
third_val = my_arr(3,1) 'etc...
end sub
If you are not interested in the count function, then you could simplify the dictionary approach by using empty quotes for the dictionary value instead of the counter. The following code assumes the first cell containing data is "A1". Alternatively, you could use the Selection (though I understand that is generally frowned upon) or the sheet's UsedRange attribute as your range.
Both of the following examples assume that you want to omit blank values from your array of unique values.
Note that to utilize dictionary objects as follows, you must have the Microsoft Scripting Runtime library active in your references. Also note that by declaring dict as a New Dictionary instead of a Dictionary in the beginning, you can forgo the step of setting it equal to a Scripting Dictionary later. Also, dictionary keys must be unique, and this method does not result in errors when setting the value corresponding to a given dictionary key, so there is no risk of having unique keys.
Sub GetUniqueValuesInRange()
Dim cll As Range
Dim rng As Range
Dim dict As New Dictionary
Dim vArray As Variant
Set rng = Range("A1").CurrentRegion.Columns(1)
For Each cll In rng.Cells
If Len(cll.Value) > 0 Then
dict(cll.Value) = ""
End If
Next cll
vArray = dict.Keys
End Sub
The prior example is a slower method, as it is generally preferred to move the values into an array in the beginning, so that all calculations can be performed in the memory. The following should work faster for larger data sets:
Sub GetUniqueValuesInRange2()
Dim vFullArray As Variant
Dim var As Variant
Dim dict As New Dictionary
Dim vUniqueArray As Variant
vFullArray = Range("A1").CurrentRegion.Columns(1).Value
For Each var In vFullArray
If Len(var) > 0 Then
dict(var) = ""
End If
Next var
vUniqueArray = dict.Keys
End Sub

Resources