Excel VBA to assign employees to tasks using loops - vba

Good afternoon,
I am working on an innovative project and cannot seem to figure out the logic of what I need to do. Essentially, I am trying to assign a number of employees to tasks (right now just filling in numbers instead of their names and the actual tasks). Here is a basic look at what the spreadsheet looks like
Task | Task Location | Task Materials | Difficulty | Assignee | Employee List
There are currently 45 tasks, and 30 employees. What I need to do is:
Randomly assort the list of employees to randomize who is doing what
Check to see IF the employee has already been scheduled for 2 tasks, as everybody should have AT LEAST one with nobody having less than two
Loop through the "Employee List" column, IF the Task is blank AND the Employee hasn't been scheduled for more than 2 tasks already, copy that current value from Employee List into Assignee column.
I know this is vague, but I really would appreciate the help. I think the steps are three fold:
Randomize Employee List column
Assign each employee once
Re-randomize employee list
Check to see which tasks still need assigning, and then check to see if the employee has already been scheduled for 2, and, if not, assign them
If they have, skip over and move to the next one
Could anybody help me devise a solution? Here is my current code, which sorts the column, and works well:
Sub ShufflePA()
Application.ScreenUpdating = False
Dim tempString As String, tempInteger As Integer, i As Integer, j As Integer, lastRow As Integer
With Sheets("Test")
lastRow = .Range("F" & .Rows.count).End(xlUp).Row
End With
For i = 6 To lastRow
Cells(i, 7).Value = WorksheetFunction.RandBetween(0, 1000)
Next i
For i = 6 To lastRow
For j = i + 1 To lastRow
If Cells(j, 7).Value < Cells(i, 7).Value Then
'change the string, which is the pa column...
tempString = Cells(i, 6).Value
Cells(i, 6).Value = Cells(j, 6).Value
Cells(j, 6).Value = tempString
tempInteger = Cells(i, 7).Value
Cells(i, 7).Value = Cells(j, 7).Value
Cells(j, 7).Value = tempInteger
End If
Next j
Next i
Worksheets("Test").Range("N:N").EntireColumn.Delete
Application.ScreenUpdating = True
End Sub
I recognize I'll likely need some more subs, and would be willing to work with anybody who could help me. In advance, thank you very much. I am struggling to develop the logic to accomplish what I need.

Try this method for randomly assigning your employees.
Note: You will need to assign your employee column to an array
Here is the Function that will take an array of your employees, and output a random name:
Function randomEmployee(ByRef employeeList As Variant) As String
'Random # that will determine the employee chosen
Dim Lotto As Long
Lotto = randomNumber(LBound(employeeList), UBound(employeeList))
randomEmployee = employeeList(Lotto)
'Remove the employee from the original array before returning it to the sub
Dim retArr() As Variant, i&, x&, numRem&
numRem = UBound(employeeList) - 1
ReDim retArr(numRem)
For i = 0 To UBound(employeeList)
If i <> Lotto Then
retArr(x) = employeeList(i)
x = x + 1
End If
Next i
Erase employeeList
employeeList = retArr
End Function
Notice how I used ByRef? This was intentional because it will replace the input array you provided with a new array that contains all the names, except the one that the function used to give you your random name.
You will also need this function to choose your random number that is called in the above function:
Function randomNumber(ByVal lngMin&, ByVal lngMax&) As Long
'Courtesy of https://stackoverflow.com/a/22628599/5781745
randomNumber = Int((lngMax - lngMin + 1) * Rnd + lngMin)
End Function
This was the test sub I had used. Obviously, you don't want to keep my empListArr, but I kept it there so you can see how this works.
Sub test()
Dim empListArr()
empListArr = Array("Bob", "Joe", "Erin", "Amber")
Debug.Print "Employee Chosen: " & randomEmployee(empListArr)
Dim i As Long
For i = 0 To UBound(empListArr)
Debug.Print "Remaining Employee: ";
Debug.Print empListArr(i)
Next
End Sub
Again, the Test() sub is not intended to be added to your code. It serves as a general guide on using your array of employees with the randomEmployee function.
So put your tasks in a loop, assigning each task one at a time with the randomEmployee function. This function will remove the employees as they are assigned.
Once your array of employees are exhausted you need to reassign your entire column of employees to your array again, so ensure you include a system that checks if your array is empty or not.
Edit:
I performed a test on the randomNumber function just to see how "random" it actually was, using a range between 0 to 10 on a million lines:
Each result hit roughly 9.1%, so it appears to be pretty reliable.

Related

How to integrate a MAX and IF statement into a FOR loop in VBA?

Trying to include an if statement into the MAX function and create a FOR loop in VBA.
What I want the code to do is to return the MAX value from column B into a new column when the value in column A equals the value in column I. I also have more than 1,000 rows in the data set and so I need a loop.
Here is a screenshot of data set of the data set I'm working with:
When I execute the following code (max_no_loop) on my data set, I get the output that I am looking for. However, I want to loop over 1,000 rows and so I need I2 to be changing with each step of the integration.
Sub max_no_loop()
Range("K2").FormulaArray = "=MAX(IF(A:A=I2,B:B))"
End Sub
After thinking about it, I came up with the function below(max_loop) where I changed I2 to Cells(i, 9), however, when I run the function on my data, I get name errors (#NAME?) and don't get the desired outcome.
Sub max_loop():
Dim i As Integer
For i = 2 To 11
Cells(i, 11).FormulaArray = "=MAX(IF(A:A=Cells(i, 9),B:B))"
Next i
End Sub
Why am I unable to make the function work when I integrate it into a for loop?
You need to pull out the variable part completely like below:
Sub max_loop():
Dim i As Integer
For i = 2 To 11
Cells(i, 11).FormulaArray = "=MAX(IF(A:A=" & Cells(i, 9).Address & ",B:B))"
Next i
End Sub
You need to take your variable i outside the " of the formula.
Try the code below:
Sub max_loop():
Dim i As Integer
For i = 2 To 11
Cells(i, 11).FormulaArray = "=MAX(IF(A:A=Cells(" & i & ", 9),B:B))"
Next i
End Sub

VBA to find string in cell and copy to different cell

I have data that it's not in a consistent position in the cell, sometimes it has a semicolon, sometimes it is to the right or the left of the semicolon. The end result I'm looking is to have in column B all "students" (defined by not being teacher) and in Column C, all Teachers. If no student or teacher is found, then the corresponding cell should be blank.
Currently I'm doing a text to columns to separate both columns then using the following formulas to have the student and teacher separate:
=IF(SUMPRODUCT(--ISNUMBER(SEARCH({"Arts and Music","Math and Science"},A2)))>0,B2,C2)
=IF(SUMPRODUCT(--ISNUMBER(SEARCH("Teacher",A2)))>0,B2,C2)
I still have to do a manual Find and replace to remove the parenthesis and text and leave only the student/teacher name.
IS there any VBA macro that can help me to get from Column A to my expected result in columns B and C? Thank you.
You can use regular expressions to do this. See this post on how to enable them in excel.
Sub FindStrAndCopy()
Dim regEx As New RegExp
regEx.Pattern = "\s*(\w+)\s*\((.+)\)"
With Sheets(1):
Dim arr() As String
Dim val As String
Dim i As Integer, j As Integer
Dim person As String, teachOrSubject As String
Dim mat As Object
For i = 2 To .Cells(.Rows.Count, "A").End(xlUp).Row:
val = Cells(i, "A").Value
arr = Split(val, ";")
For j = 0 To UBound(arr):
Set mat = regEx.Execute(arr(j))
If mat.Count = 1 Then
person = mat(0).SubMatches(0)
teachOrSubject = mat(0).SubMatches(1)
If teachOrSubject = "Teacher" Then
Cells(i, "C").Value = person
Else
Cells(i, "B").Value = person
End If
End If
Next
Next
End With
End Sub
The macro splits the string on a semicolon and stores either 1 or 2 substrings in the 'arr' array. It then does a regular expression on each one. If the string inside the parenthesis is "Teacher" then the preceding person's name is stored in column "C" otherwise it's a student and the name is stored in column "B".
I create a button that read all the registers you have on column A
then put the students on column B
then put the Teacher on column C
Check that I used "(Teacher)" to know when a teacher is in the String
I used the sheet Called "Sheet1"
And I don't use the first row because is the header row.
If you have any question please contact me.
Private Sub CommandButton1_Click()
'---------------------------------Variables-----------------------------
Dim total, i, j As Integer
'--------------Counting the number of the register in column A----------
ThisWorkbook.Sheets("Sheet1").Range("XDM1").Formula = "=COUNTA(A:A)"
total = CInt(ThisWorkbook.Sheets("Sheet1").Range("XDM1").Value)
'---------------------Creating arrays to read the rows------------------
Dim rows(0 To 1000) As String
Dim columnsA() As String
'------------Searching into the rows to find teacher or student---------
For i = 2 To total
columnsA = Split(ThisWorkbook.Sheets("Sheet1").Range("A" & i).Value, ";")
first = LBound(columnsA)
last = UBound(columnsA)
lenghtOfArray = last - first
MsgBox lenghOfArray
For j = 0 To lenghtOfArray
If InStr(columnsA(j), "(Teacher)") > 0 Then
MsgBox columnsA(j)
ThisWorkbook.Sheets("Sheet1").Range("C" & i).Value = columnsA(j)
Else
ThisWorkbook.Sheets("Sheet1").Range("B" & i).Value = columnsA(j)
End If
Next j
Next i
'--------------------------------Finishing------------------------------
End Sub

redefining an integer vba

I have difficulty fixing the following problem I am facing:
Lets say I have this code
i_GroupNumberA = Application.WorksheetFunction.CountIf(Sheets("SheetX").Range("G2:G500"), "Red")
i_GroupNumberB = Application.WorksheetFunction.CountIf(Sheets("SheetX").Range("G2:G500"), "Green")
For i = 2 To LastRow
For j = 2 To LastCol
groupNumber = Sheets("Sheet1").Cells(i, j).Value
i_GroupNumberA = 35 'this number a integer that I have got from a cells.value
i_GroupNumberB = 39
If groupNumber = Sheets("Sheet1").Cells(i, j).Value Then
i_Variable = "i_" + groupNumber + "AlphabeticLetter"
MsgBox i_Variable
End If
Next j
Next i
As a result I get i_Variable as a string in a messagebox as an outcome:
i_groupNumberA
I want to have the following result:
35
What I am asking is how can I make get a new variable of a string functioning as a integer.
I am not sure if I am asking this right?
I did as #engineersmnky said, but no effect. I check sites and made some adjustments together with your code. But Still I can't get the needed number in return. So far I got this:
Dim Group As New Collection
i_GroupNumberA = Application.WorksheetFunction.CountIf(Sheets("SheetX").Range("G2:G500"), "Red") 'lets assume it is an number 35
i_GroupNumberB = Application.WorksheetFunction.CountIf(Sheets("SheetX").Range("G2:G500"), "Green") 'lets assume it is an number 39
Group.Add i_GroupNumberA
Group.Add i_GroupNumberB
For i = 2 To LastRow
For j = 2 To LastCol
groupNumber = Sheets("Sheet1").Cells(i, j).Value
i_Variable = "i_" + groupNumber + "AlphabeticLetter"
Group(i_Variable)
Next j
Next i
Group(i_variable) I cant figure it out? for some reason it is not working.
Since you cannot get a variable value from a string in vba I would suggest creating a Collection Object or a Dictionary if you need something more diverse
dim groups AS New Collection
'Value, Key
groups.Add 35, "i_GroupNumberA"
groups.Add 39, "i_GroupNumberB"
Then in your loop instead of the message box you can use
groups(i_Variable)
This will return 35 for "i_GroupNumberA"
Update Collections use an Item and Key structure for Collection(Key) to return the value you want you must specify the key
You may have to build a collection dynamically with your needed group numbers as I am unsure what these are e.g.
Function buildCollection(val As Variant,alpha AS String) AS Collection
Dim groups As New Collection
For i = 2 To LastRow
For j = 2 To LastCol
groupNumber = Sheets("Sheet1").Cells(i, j).Value
i_Variable = "i_" & CStr(groupNumber) & alpha
groups.Add val, i_Variable
Next j
Next i
set buildCollection = groups
End
But without more info about your structure I can't really help beyond basic examples. What is group number? Is it an actual integer? What is A vs B? Please advise as this may be simpler than I originally thought. If you put some sample data or something I am sure I could get this working for you seems like a fairly simple loop. Although you are looping through all the cells and stating that they are all group numbers which seems confusing to me.

VBA Filter Function for dynamic array doesn't seem to be filtering on occasion

I am writing a subroutine in VBA to cycle through all the listed job numbers in a multi-tab time sheet and create a list of all job numbers that have been used (so it takes the original list (with possibly multiple job number occurrences) and creates a list with only one occurrence of each job number. The job numbers on each sheet are found in range("A8:A30"). The code below seems to work for the first several job names on the sample that I'm testing, but then seems to stop filtering. A8:A21 of the first sheet is:
14GCI393
14GCI393
13GCI373
13GCI373
13GCI388
13GCI367:2
14GCI408
14GCI408
13GCI373
13GCI388
14GCI415
14GCI415
00GCI000
And the code is:
Sub listusedjobs()
Dim usedjobs() As String
Dim nextjob As String
Dim i, m, n, lastsheetindexnumber As Integer
Application.ScreenUpdating = False
lastsheetindexnumber = ThisWorkbook.Sheets.Count
m = 0
ReDim usedjobs(m)
usedjobs(m) = "initialize"
For i = 1 To lastsheetindexnumber
Sheets(i).Activate
For n = 8 To 30
nextjob = Range("A" & n).Value
If Not IsInArray(nextjob, usedjobs) Then 'determine if nextjob is already in usedjobs()
ReDim usedjobs(m)
usedjobs(m) = nextjob 'Add each unique job to array "usedjobs"
Sheets(lastsheetindexnumber).Cells(m + 40, 1).Value = nextjob 'Print job name that was just added
m = m + 1
End If
Next n
Next i
Application.ScreenUpdating = True
End Sub
Function IsInArray(stringToBeFound As String, arr As Variant) As Boolean
IsInArray = (UBound(Filter(arr, stringToBeFound, , vbTextCompare)) > -1)
End Function
Any help figuring out what is going wrong will be much appreciated! The current output I get for this code is below and contains multiple doubles.
14GCI393
13GCI373
13GCI388
13GCI367:2
14GCI408
13GCI373
13GCI388
14GCI415
00GCI000
I think that your problem may be not using ReDim Preserve inside your If Not

Looped If/Then Functions in Excel VBA

I'm just starting to learn Excel VBA and I'm running into problems with a particular exercise. Given a column of 20 randomly generated integers between 0 and 100 in a column, I want to write a VBA program that writes in the column next to it "pass" if the number is greater than or equal to 50 and "fail" if the number is less than 50.
My approach involved using a looping function from i = 1 to 20 with an If statement for each cell (i,1) which would write pass or fail in (i,2).
Sub CommandButton1_Click()
'Declare Variables
Dim score As Integer, result As String, i As Integer
'Setup Loop function, If/Then function
For i = 1 To 20
score = Sheet1.Cells(i, 1).Value
If score >= 60 Then result = "pass"
Sheet1.Cells(i, 2).Value = result
Next i
End If
End Sub
Could I get some insight into what I'm doing wrong?
Thanks in advance!
Try something like this...
Sub CommandButton1_Click()
'Declare Variables
Dim score As Integer, result As String, i As Integer
'Setup Loop function, If/Then function
For i = 1 To 20
score = Sheets("Sheet1").Cells(i, 1).Value
If score >= 60 Then
result = "pass"
Else
result = "fail"
End If
Sheets("Sheet1").Cells(i, 2).Value = result
Next i
End Sub
You need to properly specify the worksheet your working with like Sheets("Sheet1").Cells(...
Add an else clause to set result to fail when the value is less than 60. otherwise it never changes after the first 'pass'
Move the End if inside the for loop, immediately after the score check...
The correction with the least amount of changes is the following :
Sub CommandButton1_Click()
'Declare Variables
Dim score As Integer, result As String, i As Integer
'Setup Loop function, If/Then function
For i = 1 To 20
score = Sheet1.Cells(i, 1).Value
If score >= 60 Then
result = "pass"
Sheet1.Cells(i, 2).Value = result
End If
Next i
End If
End Sub
Keep in mind that, in VBA, variables are global to the function; not local to the loop. As it was mentionned, you could also have wrote something like :
result = ""
if score >= 60 then result = "pass"
sheet1....