Gradient color fill of specific cells - excel

I am using working code, but this code has one drawback. The problem is that the gradient fill color is snapped to Select / Selection.
If dic.Exists(arr(1, n)) Then Cells(87, n + 1).Select
The macro highlights the required cells + the cell that was selected with the mouse.
What / how should I change to select only the necessary cells (by condition)?
Sub G()
Dim dic As New Dictionary
Dim x, arr, iNum&, n&
Dim z As Range
Set z = [Q10]
iNum = Range("A85").Value2
Set wb = ThisWorkbook:
s1 = wb.Sheets("1").Range("C87").Value2
Windows("2.xlsx").Activate
n = Cells(Rows.Count, 48).End(xlUp).Row
arr = Range("A2:CD" & n).Value2
If Not IsArray(arr) Then Err.Raise xlErrNA
For n = 1 To UBound(arr, 1)
If arr(n, 77) = 1 Then
If arr(n, 37) = iNum And arr(n, 3) = s1 Then x = dic(arr(n, 73))
End If
Next n
ThisWorkbook.Activate
n = Cells(20, Columns.Count).End(xlToLeft).Column
arr = Cells(20, 2).Resize(1, n).Value2
z.Activate
For n = 1 To UBound(arr, 2)
If dic.Exists(arr(1, n)) Then Cells(87, n + 1).Select
With Selection.Interior
.Pattern = xlPatternLinearGradient
.Gradient.Degree = 0
.Gradient.ColorStops.Clear
End With
With Selection.Interior.Gradient.ColorStops.Add(0)
.Color = 65535
.TintAndShade = 0
End With
With Selection.Interior.Gradient.ColorStops.Add(1)
.Color = 10498160
.TintAndShade = 0
End With
Next n
End Sub

Related

Autofit won't run as part of a sub

I'm working on a sub where, after doing a whole bunch of other things, it just selects all the cells in the active sheet and sets the rows to auto-fit height. For some reason it won't work! I've tried to the autofit row height command in various different places, but it seems that so long as it's part of the larger sub, or called within it, it won't work. However, if I write a separate sub that is run separately, it will work just fine. Does anyone know why this could be?
Below is the sub where it's misbehaving, plus the other sub I made that I can run separately. Any suggestions on how to make this all more efficient is also very welcome! (I also kind of learned coding in the wild, so I don't really know best practices...)
Option Explicit
Sub WriteToIndex(ByRef rowsArray() As Variant, ByRef Indexes() As Integer, ByRef HeaderNames() As String, myTable As ListObject, sheetName As String)
Debug.Print sheetName
Sheets(sheetName).Activate
Dim i, j As Variant
Dim count As Integer
'If no rows, no write
If (Not Not rowsArray) <> 0 Then
Else:
'If the index is longer than 5000 rows, this will need to be updated!
Range("B4:L5000").Clear
MsgBox "Sheet " & sheetName & " was not populated because there is nothing attributed to it"
Exit Sub
End If
'If nothing in Int/Ext section, no write
count = 0
For i = LBound(rowsArray) To UBound(rowsArray)
For j = LBound(Indexes) To UBound(Indexes)
If myTable.DataBodyRange(rowsArray(i), Indexes(j)).Value = "n" Then count = count + 1
Next
Next
If count = 0 Then
'If the index is longer than 5000 rows, this will need to be updated!
Range("B4:L5000").Clear
MsgBox "Sheet " & sheetName & " was not populated because there is nothing attributed to it"
Exit Sub
End If
Sheets(sheetName).Activate
'Pulls desired index fonts and sizes from Settings tab
Dim IndexFont As String, HeaderFSize, BodyFSize As Integer, HeaderBold As Boolean
IndexFont = Worksheets("Settings").Cells(10, 11).Value
HeaderFSize = Worksheets("Settings").Cells(11, 11).Value
HeaderBold = Worksheets("Settings").Cells(12, 11).Value
BodyFSize = Worksheets("Settings").Cells(13, 11).Value
'Remove headers from array if there are no items for the index
Dim loopno, pos, zeroloops(), zeroloopstart As Integer
count = 0
zeroloopstart = 0
loopno = 1
ReDim Preserve zeroloops(zeroloopstart)
For i = LBound(Indexes) To UBound(Indexes)
For j = LBound(rowsArray) To UBound(rowsArray)
If myTable.DataBodyRange(rowsArray(j), Indexes(i)).Text = "n" Then count = count + 1
Next
If count = 0 Then
pos = loopno - 1
ReDim Preserve zeroloops(0 To zeroloopstart)
zeroloops(zeroloopstart) = pos
zeroloopstart = zeroloopstart + 1
End If
count = 0
loopno = loopno + 1
Next
'If a header is in the zeroloops array, it gets removed from the Header array
If IsEmpty(zeroloops(0)) Then
Debug.Print "Empty"
Else
For i = LBound(zeroloops) To UBound(zeroloops)
For j = zeroloops(i) To UBound(Indexes) - 1
Indexes(j) = Indexes(j + 1)
Next j
For j = zeroloops(i) To UBound(HeaderNames) - 1
HeaderNames(j) = HeaderNames(j + 1)
Next j
For j = LBound(zeroloops) To UBound(zeroloops)
zeroloops(j) = zeroloops(j) - 1
Next j
Debug.Print
ReDim Preserve Indexes(0 To (UBound(Indexes) - 1))
ReDim Preserve HeaderNames(0 To (UBound(HeaderNames) - 1))
Next i
End If
'If the index is longer than 5000 rows, this will need to be updated!
Range("B4:L5000").Clear
'Apply preferred font to entire sheet
Cells.Font.Name = IndexFont
Dim KeyIDCol, DescCol, SourceCol, ProductCol, CatCol, ColorCol, FinishCol, SizeCol, ContactCol, SpecCol, RemarkCol As Integer
'Index for each value to report
'If additional column needs to be reported, add the line and swap out the name in the Listcolumns definition
KeyIDCol = Worksheets("Database").ListObjects("Database").ListColumns("KEYNOTE #").Index
DescCol = Worksheets("Database").ListObjects("Database").ListColumns("KEYNOTE DESCRIPTION").Index
SourceCol = Worksheets("Database").ListObjects("Database").ListColumns("SOURCE").Index
ProductCol = Worksheets("Database").ListObjects("Database").ListColumns("PRODUCT").Index
CatCol = Worksheets("Database").ListObjects("Database").ListColumns("CAT. NO.").Index
ColorCol = Worksheets("Database").ListObjects("Database").ListColumns("COLOR").Index
FinishCol = Worksheets("Database").ListObjects("Database").ListColumns("FINISH").Index
SizeCol = Worksheets("Database").ListObjects("Database").ListColumns("SIZE").Index
ContactCol = Worksheets("Database").ListObjects("Database").ListColumns("CONTACT").Index
SpecCol = Worksheets("Database").ListObjects("Database").ListColumns("SECTION #").Index
RemarkCol = Worksheets("Database").ListObjects("Database").ListColumns("REMARKS").Index
'Definitions for write loop
Dim NextWriteRow, HeaderListIndex As Integer
Dim ArrayItem As Variant
Dim WriteStartCell, Cell As Range
NextWriteRow = 4
HeaderListIndex = 0
i = 1 ' for moving to the next KeyID
j = 0 ' start counter for steps
Set WriteStartCell = Cells(NextWriteRow, 2)
Dim k As Variant
'Outer loop puts in headers
For Each ArrayItem In Indexes
With Cells(NextWriteRow, 2)
.Value = HeaderNames(HeaderListIndex)
.VerticalAlignment = xlBottom
.Font.Size = HeaderFSize
.Font.Bold = HeaderBold
End With
HeaderListIndex = HeaderListIndex + 1
'Second loop puts in KeynoteID with all pertinent info
For k = LBound(rowsArray) To UBound(rowsArray)
If myTable.DataBodyRange(rowsArray(k), ArrayItem).Value = "n" Then
With WriteStartCell
.Offset(i, 0).Value = myTable.DataBodyRange(rowsArray(k), ArrayItem).Offset(, KeyIDCol - ArrayItem).Value
.Offset(i, 1).Value = myTable.DataBodyRange(rowsArray(k), ArrayItem).Offset(, DescCol - ArrayItem).Value
.Offset(i, 2).Value = myTable.DataBodyRange(rowsArray(k), ArrayItem).Offset(, SourceCol - ArrayItem).Value
.Offset(i, 3).Value = myTable.DataBodyRange(rowsArray(k), ArrayItem).Offset(, ProductCol - ArrayItem).Value
.Offset(i, 4).Value = myTable.DataBodyRange(rowsArray(k), ArrayItem).Offset(, CatCol - ArrayItem).Value
.Offset(i, 5).Value = myTable.DataBodyRange(rowsArray(k), ArrayItem).Offset(, ColorCol - ArrayItem).Value
.Offset(i, 6).Value = myTable.DataBodyRange(rowsArray(k), ArrayItem).Offset(, FinishCol - ArrayItem).Value
.Offset(i, 7).Value = myTable.DataBodyRange(rowsArray(k), ArrayItem).Offset(, SizeCol - ArrayItem).Value
.Offset(i, 8).Value = myTable.DataBodyRange(rowsArray(k), ArrayItem).Offset(, ContactCol - ArrayItem).Value
With .Offset(i, 9)
.NumberFormat = "000000"
.HorizontalAlignment = xlCenter
.Value = myTable.DataBodyRange(rowsArray(k), ArrayItem).Offset(, SpecCol - ArrayItem).Value
End With
.Offset(i, 10).Value = myTable.DataBodyRange(rowsArray(k), ArrayItem).Offset(, RemarkCol - ArrayItem).Value
End With
With Range(WriteStartCell.Offset(i, 0), WriteStartCell.Offset(i, 10))
.VerticalAlignment = xlTop
.WrapText = True
.Borders(xlDiagonalDown).LineStyle = xlNone
.Borders(xlDiagonalUp).LineStyle = xlNone
.Font.Size = BodyFSize
With .Borders
.LineStyle = xlContinuous
.ColorIndex = xlAutomatic
.TintAndShade = 0
.Weight = xlThin
End With
End With
i = i + 1
j = j + 1
End If
Next
j = j + 2
NextWriteRow = NextWriteRow + j
i = i + 2
j = 0
Next
'This is the autofit that won't work for some reason
Cells.Rows.Autofit
Debug.Print "Works"
End Sub
'---
Sub AutofitRowHeight()
Dim sheetnames() As String
ReDim sheetnames(0 To 17)
sheetnames(0) = "SF-ALL-I"
sheetnames(1) = "SF-ALL-E"
sheetnames(2) = "SF-H-I"
sheetnames(3) = "SF-H-E"
sheetnames(4) = "SF-CUP-I"
sheetnames(5) = "SF-CUP-E"
sheetnames(6) = "SF-GB-I"
sheetnames(7) = "SF-GB-E"
sheetnames(8) = "LM-ALL-I"
sheetnames(9) = "LM-ALL-E"
sheetnames(10) = "LM-H-I"
sheetnames(11) = "LM-H-E"
sheetnames(12) = "LM-CC-I"
sheetnames(13) = "LM-CC-E"
sheetnames(14) = "LM-SCC-I"
sheetnames(15) = "LM-SCC-E"
sheetnames(16) = "LM-GB-I"
sheetnames(17) = "LM-GB-E"
Dim i As Variant
For i = LBound(sheetnames) To UBound(sheetnames)
Sheets(sheetnames(i)).Activate
Cells.Rows.AutoFit
Next
Sheets("Database").Activate
Cells(1, 1).Select
End Sub

Merge cells with same year in a row

I need to merge the cells one above the months.
Cells Should be merged from 01 to 12 showing year in cell.
Look for the picture for more clarification.
I have below code, but which show months after run in cell row1.
My idea is to convert above cells to years through vba and apply merge same year at the end.
which is shown in desired output.
Note.
ROW 4 and 5 are just my thinking, which will help year to merge.
Dim a(), i As Long, j As Long, m As Long, x As Range
With Range("b1:qaz1")
.MergeCells = False
.ClearContents
a() = .Offset(1).Value
m = Month(a(1, 1))
j = UBound(a, 2)
Set x = .Cells(1)
For i = 2 To j
If m <> Month(a(1, i)) Or i = j Then
With Range(x, .Cells(i - IIf(i = j, 0, 1)))
.MergeCells = True
.HorizontalAlignment = xlCenter
End With
x.Value = Format(DateSerial(2000, m, 1), "MMMM")
m = Month(a(1, i))
Set x = .Cells(i)
End If
Next
End With
End Sub
After running new program output look like
Since you have true dates in your caption row the month and year can be extracted from there. However, the code below converts dates that might have been created using formulas to hard dates before processing them.
Sub MergeCaptionsByYear()
' 031
Const CapsRow As Long = 1 ' change to suit
Const StartClm As Long = 2 ' change to suit
Dim Rng As Range ' working range
Dim Tmp As Variant ' current cell's value
Dim Cl As Long ' last used column
Dim Cstart As Long ' first column in Rng
Dim C As Long ' working column
Dim Yr As Integer ' year
Cl = Cells(CapsRow, Columns.Count).End(xlToLeft).Column
Range(Cells(CapsRow, StartClm), Cells(CapsRow, Cl)).Copy
Cells(CapsRow, StartClm).PasteSpecial xlValues
Application.CutCopyMode = False
C = StartClm - 1
Application.DisplayAlerts = False
Do
Tmp = Cells(CapsRow, C + 1).Value
If Not IsDate(Tmp) And (C <> Cl) Then
MsgBox "Cell " & Cells(CapsRow, C + 1).Address(0, 0) & _
" doesn't contain a date." & vbCr & _
"This macro will be terminated.", _
vbInformation, "Invalid cell content"
Exit Do
End If
If (Yr <> Year(CDate(Tmp))) Or (C = Cl) Then
If Yr Then
Set Rng = Range(Cells(CapsRow, Cstart), _
Cells(CapsRow, C))
With Rng
.Merge
.HorizontalAlignment = xlCenter
.NumberFormat = "yyyy"
End With
SetBorder Rng, xlEdgeLeft
SetBorder Rng, xlEdgeRight
End If
If C > (Cl - 1) Then Exit Do
Cstart = C + 1
Yr = Year(Tmp)
End If
C = C + 1
Loop
Application.DisplayAlerts = True
End Sub
Private Sub SetBorder(Rng As Range, _
Bord As XlBordersIndex)
' 031
With Rng.Borders(Bord)
.LineStyle = xlContinuous
.ColorIndex = xlAutomatic
.TintAndShade = 0
.Weight = xlMedium ' xlThin
End With
End Sub
Assuming the months range is "B5:AH5"
Sub test()
Dim monthsRng As Range
Set monthsRng = Range("B5:AH5")
monthsRng.Cells(1, 1).Offset(-1, 0).Select
For j = 1 To Int((monthsRng.Cells.Count / 12) + 2)
If ActiveCell.Offset(1, 0) <> 0 Then
For i = 1 To 12
ActiveCell.Value = Year(ActiveCell.Offset(1, 0))
If Year(ActiveCell.Offset(1, i)) = ActiveCell Then
Selection.Resize(1, i + 1).Select
Else
Exit For
End If
Next
With Selection
.HorizontalAlignment = xlCenter
.MergeCells = True
End With
Selection.Offset(0, 1).Select
Else
Exit For
End If
Next
End Sub
Replacing the inner for loop with below code will work irrespective of whether the dates in the Range("B5:AH5") in above procedure are formatted as dates or not.
For i = 1 To 12
ActiveCell.Value = Right(Format(ActiveCell.Offset(1, 0), "DD.MM.YYYY"), 4)
If Right(Format(ActiveCell.Offset(1, i), "DD.MM.YYYY"), 4) = Format(ActiveCell, Text) Then
Selection.Resize(1, i + 1).Select
Else
Exit For
End If
Next
However, in any case you need to format the output in excel as number (without 1000 separator and decimal places) and not date.

How would I convert this code to something shorter? Potentially through an Array?

I have working code for my need, however now that I have learned this much, I want to go back and make it more streamlined.
I have attempted arrays, but apparently do not understand them..
Sub addGreenx(newbook)
Set newbook = ActiveWorkbook
myrow = 1
mycolumn = "M"
For Each r In Intersect(newbook.Sheets("Sheet1").Range("AQ:AQ"), newbook.Sheets("Sheet1").UsedRange)
If InStr(r, "Green") And InStr(r, "red") = 0 Then
newbook.Sheets("Sheet1").Range(mycolumn + Mid(Str(myrow), 2)) = "X"
End If
myrow = myrow + 1
Next r
End Sub
Sub addBluex(newbook)
Set newbook = ActiveWorkbook
myrow = 1
mycolumn = "O"
For Each r In Intersect(newbook.Sheets("Sheet1").Range("AQ:AQ"), newbook.Sheets("Sheet1").UsedRange)
If InStr(r, "Blue") And InStr(r, "Red") = 0 Then
newbook.Sheets("Sheet1").Range(mycolumn + Mid(Str(myrow), 2)) = "X"
End If
myrow = myrow + 1
Next r
End Sub
Sub addTealx(newboox)
Set newbook = ActiveWorkbook
myrow = 1
mycolumn = "O"
For Each r In Intersect(newbook.Sheets("Sheet1").Range("AQ:AQ"), newbook.Sheets("Sheet1").UsedRange)
If InStr(r, "Teal") And InStr(r, "Red") = 0 Then
newbook.Sheets("Sheet1").Range(mycolumn + Mid(Str(myrow), 2)) = "X"
End If
myrow = myrow + 1
Next r
End Sub
This can be made shorter I'm sure, the item in "mycolumn" may have both blue and green, or just one or the other, however if it is blue the x goes in one column, if green in another, and if both in both.
If you always run these together then you only need a single loop and inside that loop test for each condition.
Sub CheckForColors()
Dim r As Range, sht As Worksheet, v
Set sht = ActiveWorkbook.Sheets("Sheet1")
For Each r In Intersect(sht.Range("AQ:AQ"), sht.UsedRange)
v = r.Value
If InStr(v, "Red") = 0 Then
If InStr(v, "Green") > 0 Then r.EntireRow.columns("M").Value = "x"
If InStr(v, "Blue") > 0 Or InStr(v, "Teal") > 0 Then _
r.EntireRow.columns("O").Value = "x"
End If
Next r
End Sub

How to create a macro which will copy data from row to column, using conditions?

I am using currently v lookup to find and place values against the specific item. however, I am looking for help for a VB macro which will out the data in defined outcome.
please see 1st screen shot of raw data
second screen shot, should be the outcome.
Please note the "site" is not constant it can be any value, so I have put all site in column A .
currently V look is doing the job well. but makes the file crash sometime.
You can solve this with a Pivot Table using your original data source with NO changes in the table layout.
Drag the columns as shown below (you'll want to rename them from the default names): For Columns, drag the Date field there first. The Σ Values field will appear after you've dragged two Fields to the Values area, and should be below Date.
And with some formatting changes from the default, the result can look like:
Can you change your source data?
If you change your data to look like the table "Changed Source Data" below you can solve your issue with a pivot table.
Solution with a Pivot Table
Changed Source Data
There question can easily solved with pivot table. For practice i have create the below.
Let us assume that:
Data appears in Sheet "Data"
Results will be populated in sheet "Results"
Option Explicit
Sub Allocation()
Dim LastRow As Long, Row As Long, Column As Long, Invetory As Long, Sold As Long, Remaining As Long, LastRowRes As Long, LastColRes As Long, CurrentCol As Long, CurrentRow As Long, i As Long, y As Long
Dim iDate As Date
Dim Site As String
Dim wsData As Worksheet, wsResults As Worksheet
Dim ExcistSite As Boolean, ExcistDate As Boolean
Set wsData = ThisWorkbook.Worksheets("Data")
Set wsResults = ThisWorkbook.Worksheets("Results")
LastRow = wsData.Cells(wsData.Rows.Count, "A").End(xlUp).Row
wsResults.UsedRange.Clear
For Row = 2 To LastRow
iDate = wsData.Cells(Row, 1).Value
Site = wsData.Cells(Row, 2).Value
Invetory = wsData.Cells(Row, 3).Value
Sold = wsData.Cells(Row, 4).Value
Remaining = wsData.Cells(Row, 5).Value
If Row = 2 Then
With wsResults.Range("B1:D1")
.Merge
.Value = iDate
End With
wsResults.Range("A2").Value = "Site"
wsResults.Range("A2").Offset(1, 0).Value = Site
wsResults.Range("B2").Value = "Invetory"
wsResults.Range("B2").Offset(1, 0).Value = Invetory
wsResults.Range("C2").Value = "Sold"
wsResults.Range("C2").Offset(1, 0).Value = Sold
wsResults.Range("D2").Value = "Remaining"
wsResults.Range("D2").Offset(1, 0).Value = Remaining
Else
'Check if Site appears
LastRowRes = wsResults.Cells(wsResults.Rows.Count, "A").End(xlUp).Row
For i = 3 To LastRowRes
ExcistSite = False
If wsResults.Cells(i, 1).Value = Site Then
CurrentRow = i
ExcistSite = True
Exit For
Else
CurrentRow = i + 1
End If
Next i
If ExcistSite = False Then
wsResults.Cells(CurrentRow, 1).Value = Site
End If
'Check if date appears
LastColRes = wsResults.Cells(1, wsResults.Columns.Count).End(xlToLeft).Column
For y = 2 To LastColRes
ExcistDate = False
If wsResults.Cells(1, y).Value = iDate Then
CurrentCol = y
ExcistDate = True
Exit For
Else
CurrentCol = y + 1
End If
Next y
If ExcistDate = False Then
wsResults.Cells(2, CurrentCol + 2).Value = "Invetory"
wsResults.Cells(i, CurrentCol + 2).Value = Invetory
wsResults.Cells(2, CurrentCol + 3).Value = "Sold"
wsResults.Cells(i, CurrentCol + 3).Value = Sold
wsResults.Cells(2, CurrentCol + 4).Value = "Remaining"
wsResults.Cells(i, CurrentCol + 4).Value = Remaining
With wsResults.Range(Cells(1, LastColRes + 3), Cells(1, LastColRes + 5))
.Merge
.Value = iDate
End With
Else
wsResults.Cells(CurrentRow, CurrentCol).Value = Invetory
wsResults.Cells(CurrentRow, CurrentCol + 1).Value = Sold
wsResults.Cells(CurrentRow, CurrentCol + 2).Value = Remaining
End If
End If
Next Row
LastColRes = wsResults.Cells(1, wsResults.Columns.Count).End(xlToLeft).Column
LastRowRes = wsResults.Cells(wsResults.Rows.Count, "A").End(xlUp).Row
With wsResults.Range(Cells(1, 2), Cells(1, LastColRes))
With .Font
.Bold = True
.ThemeColor = xlThemeColorDark1
End With
With .Interior
.ThemeColor = xlThemeColorAccent1
End With
End With
With wsResults.Cells(2, 1)
With .Font
.Bold = True
.ThemeColor = xlThemeColorDark1
End With
With .Interior
.ThemeColor = xlThemeColorLight1
End With
End With
For i = 2 To LastColRes Step 3
With wsResults.Range(Cells(2, i), Cells(LastRowRes, i))
With .Interior
.ThemeColor = xlThemeColorAccent1
.TintAndShade = 0.399975585192419
End With
End With
Next i
For i = 3 To LastColRes + 3 Step 3
With wsResults.Range(Cells(2, i), Cells(LastRowRes, i + 1))
With .Font
.ThemeColor = xlThemeColorDark2
.TintAndShade = -0.249977111117893
End With
End With
Next i
With wsResults.UsedRange
.Borders.LineStyle = xlContinuous
.HorizontalAlignment = xlCenter
.VerticalAlignment = xlCenter
.EntireColumn.AutoFit
End With
End Sub

Shuffling a 2D array

I have the follow script to put a list of people with there know skills in an array and then match the first match with a customer with the same skill. Every time it runs the results are the same. I would like to have it be a random order of the array, but keeping the two columns in the array together. How can I shuffle(rearrange) the array that keeps the rows in the array the same? Or would it be better to erase the array, randomly sort the columns and set the array back up?
Sub Assign()
Dim arOne()
ReDim arOne(1000, 15)
Dim o As Integer
Dim p As Integer
Dim StartTime As Double
Dim MinutesElapsed As String
p = 0
o = 0
For i = 2 To 920
If Cells(i, 12).Value <> Cells(i - 1, 12) Then
p = p + 1
arOne(p, 0) = Cells(i, 12).Value
arOne(p, 1) = Cells(i, 13).Value
o = 2
Else
arOne(p, o) = Cells(i, 13).Value
o = o + 1
End If
Next
For i = 2 To 612
For o = LBound(arOne, 1) + 1 To UBound(arOne, 1)
If arOne(o, 0) <> "" Then
iUsed = Application.WorksheetFunction.CountIf(Range("C2:C" & i), "=" & arOne(o, 0))
If iUsed < Application.WorksheetFunction.VLookup(arOne(o, 0), Range("Q2:R62"), 2, False) Then
For j = LBound(arOne, 2) + 1 To UBound(arOne, 2)
If arOne(o, j) = Cells(i, 2).Value Then
Cells(i, 3).Value = arOne(o, 0)
ActiveSheet.Calculate
GoTo NextIR
End If
Next j
End If
End If
Next o
NextIR:
Next i
End Sub
Multiple loops and multiple access to range objects makes your code very, very slow (I don't know if performance is important).
I would read all necessary data to arrays and use filter and rnd to get a random person with the relevant skill:
Option Explicit
Sub PeopleBusiness()
Dim People, Customers, FilterArray
Dim I As Long, Idx As Long
People = Application.Transpose([L2:L920 & "|" & M2:M8])
Customers = Range("A2:C612").Value2
For I = 1 To UBound(Customers, 1)
FilterArray = Filter(People, Customers(I, 2))
If UBound(FilterArray) > -1 Then
Idx = Round(Rnd() * UBound(FilterArray), 0)
Customers(I, 3) = Left(FilterArray(Idx), InStr(1, FilterArray(Idx), "|") - 1)
End If
Next I
Range("A2:C612").Value = Customers
End Sub
I was able to get done what I needed by erasing the array and redimming it after sorting the data based on a rand() number in the table. It takes about 15 minutes to run 7000 assignment but it is a lot better than 7+ hours it takes to do manually.
Sub Assign()
Dim arOne()
ReDim arOne(1000, 15)
Dim o As Integer
Dim p As Integer
Dim StartTime As Double
Dim MinutesElapsed As String
Application.Calculation = xlAutomatic
StartTime = Timer
NextIR:
ReDim arOne(1000, 15)
p = 0
o = 0
QAlr = Sheets("Sheet1").Range("L" & Rows.Count).End(xlUp).Row
For I = 2 To QAlr
If Cells(I, 12).Value <> Cells(I - 1, 12) Then
p = p + 1
arOne(p, 0) = Cells(I, 12).Value
arOne(p, 1) = Cells(I, 13).Value
o = 2
Else
arOne(p, o) = Cells(I, 13).Value
o = o + 1
End If
Next
AQAlr = Sheets("Sheet1").Range("C" & Rows.Count).End(xlUp).Row
AgtLr = Sheets("Sheet1").Range("A" & Rows.Count).End(xlUp).Row
For I = AQAlr + 1 To AgtLr
For o = LBound(arOne, 1) + 1 To UBound(arOne, 1)
If arOne(o, 0) <> "" Then
iUsed = Application.WorksheetFunction.CountIf(Range("C2:C" & I), "=" & arOne(o, 0))
If iUsed < Application.WorksheetFunction.VLookup(arOne(o, 0), Range("Q2:R62"), 2, False) Then
For j = LBound(arOne, 2) + 1 To UBound(arOne, 2)
If arOne(o, j) = Cells(I, 2).Value Then
Cells(I, 3).Value = arOne(o, 0)
ActiveSheet.Calculate
Erase arOne()
ActiveWorkbook.Worksheets("Sheet1").ListObjects("Table1").Sort.SortFields.Clear
ActiveWorkbook.Worksheets("Sheet1").ListObjects("Table1").Sort.SortFields.Add _
Key:=Range("Table1[[#All],[Random '#]]"), SortOn:=xlSortOnValues, Order:= _
xlDescending, DataOption:=xlSortTextAsNumbers
With ActiveWorkbook.Worksheets("Sheet1").ListObjects("Table1").Sort
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
GoTo NextIR
End If
Next j
End If
End If
Next o
Next I
MinutesElapsed = Format((Timer - StartTime) / 86400, "hh:mm:ss")
MsgBox "Assignments completed in " & MinutesElapsed & " minutes", vbInformation
End Sub
Not entirely sure I got your set-up right but you can try this:
Option Explicit
Sub Assign()
Randomize
Range("C2", Range("C" & Rows.Count).End(xlUp)).ClearContents
Dim R1 As Range: Set R1 = Range("L2:M920") 'People skill table
Dim R2 As Range: Set R2 = Range("A2:B612") 'Cusotmers skill talbe
Dim D0 As Object: Set D0 = CreateObject("scripting.dictionary")
Dim i As Integer, j As Integer, Rand as Integer
For i = 1 To R2.Rows.Count
Rand = Int(R1.Rows.Count * Rnd + 1)
For j = 1 To R1.Rows.Count
If R1.Cells(Rand, 2) = R2(i, 2) And Not D0.exists(Rand) Then
R2.Cells(i, 2).Offset(0, 1) = R1(Rand, 1)
D0.Add Rand, Rand
Exit For
End If
Rand = (Rand Mod R1.Rows.Count) + 1
Next j
Next i
End Sub
The idea is to check the people skill list starting from a random point and making sure a key is not used twice.
EDIT:
According to your comment I assume a "people / skill" can then be assigned more than once as there are 7000+ customers ?
Code below randomly assign with a fairly good distribution 1500 peoples to 7000 customers in +/- 1 second.
Have a try and see if you can adapt it to your project.
Option Explicit
Sub Assign()
Application.ScreenUpdating = False
Dim Start: Start = Timer
Randomize
Range("C2:C99999").ClearContents
Dim D1 As Object
Dim R1 As Range: Set R1 = Range("L2", Range("M" & Rows.Count).End(xlUp))
Dim R2 As Range: Set R2 = Range("A2", Range("B" & Rows.Count).End(xlUp))
Dim T1: T1 = R1
Dim T2: T2 = R2
Dim T3()
Dim a As Integer: a = 1
Dim i As Integer, j As Integer, k As Integer, Rnd_Val As Integer, j_loop As Integer
For i = 1 To (Int(R2.Rows.Count / R1.Rows.Count) + 1)
Set D1 = CreateObject("scripting.dictionary")
For j = (R1.Rows.Count * i - R1.Rows.Count + 1) To R1.Rows.Count * i
ReDim Preserve T3(1 To j)
Rnd_Val = Int(Rnd * R1.Rows.Count + 1)
For k = 1 To R1.Rows.Count
If T1(Rnd_Val, 2) = T2(j, 2) And Not D1.exists(Rnd_Val) And T3(j) = "" Then
T3(j) = T1(Rnd_Val, 1)
D1.Add Rnd_Val, Rnd_Val
Exit For
End If
Rnd_Val = (Rnd_Val Mod R1.Rows.Count) + 1
Next k
If T3(j) = "" Then
For k = 1 To R1.Rows.Count
If T1(Rnd_Val, 2) = T2(j, 2) Then
T3(j) = T1(Rnd_Val, 1)
Exit For
End If
Rnd_Val = (Rnd_Val Mod R1.Rows.Count) + 1
Next k
End If
a = a + 1
If a > R2.Rows.Count Then GoTo EndLoop
Next j
Set D1 = Nothing
Next i
EndLoop:
Range("C2").Resize(UBound(T3), 1) = Application.Transpose(T3)
Debug.Print Timer - Start
Application.ScreenUpdating = True
End Sub

Resources