1

I have a manual selection process that I have tried but failed to automate, so I am reaching out for help. I have attached an image of my Excel sheet as a visual guide when reading my process. Excel Snapshot.

I select cell "L2" and run the code below. It finds the first instance of the value within "A2:J1501" and cuts the whole row. It pastes the row onto the sheet named Lineups. Then it highlights each of the values of the cut row in column "L:L" to let me know that value has been used. I then manually select the next non-highlighted value (in the image example it would be "L2") and run the code again, and again, and again, until every row of L:L is highlighted. This process can take some time depending on the number of rows in L:L so I was hoping I can get some help to automate.

Thank you very much.

Sub ManualSelect()

Dim rng As Range
Set rng = Range("A1:J1501")

Dim ac As Range
Set ac = Application.ActiveCell

rng.Find(what:=ac).Select
Range("A" & ActiveCell.Row).Resize(1, 10).Cut
ActiveWindow.ScrollRow = 1

Sheets("Lineups").Select
nextRow = Cells(Rows.Count, 1).End(xlUp).Row + 1
Cells(nextRow, 1).Select
ActiveSheet.Paste
Sheets("Data").Select

Dim wsData As Worksheet
Dim wsLineups As Worksheet
Dim rngToSearch As Range
Dim rngLineupSet As Range
Dim rngPlayerID As Range
Dim Column As Long
Dim Row As Long
Dim LastRow As Long

Set wsData = Sheets("Data")
Set wsLineups = Sheets("Lineups")
Set rngPlayerID = wsData.Range("L2:K200")
Set rngToSearch = rngPlayerID

LastRow = wsLineups.Cells(Rows.Count, 1).End(xlUp).Row

For Row = 2 To LastRow
    For Column = 1 To 10
        Set rngLineupSet = rngPlayerID.Find(what:=wsLineups.Cells(Row, Column), LookIn:=xlValues)
        If Not rngLineupSet Is Nothing Then rngLineupSet.Interior.Color = 65535
    Next Column
Next Row

End Sub
safo2238
  • 15
  • 6
  • 2
    Note: In general, you want to [avoid using Select](https://stackoverflow.com/questions/10714251/how-to-avoid-using-select-in-excel-vba) in your code. – cybernetic.nomad Apr 25 '22 at 17:49
  • Yeah, I know. That is how my brain is programmed though, hopefully that will change the more I learn! Thanks. – safo2238 Apr 25 '22 at 18:15

2 Answers2

4

This should be pretty close:

Sub ManualSelect()

    Dim wsData As Worksheet, c As Range, dict As Object, v, rw As Range
    Dim wsLineups As Worksheet, c2 As Range, f As Range
    
    Set dict = CreateObject("scripting.dictionary") 'for tracking already-seen values
    
    Set wsLineups = ThisWorkbook.Worksheets("Lineups")
    Set wsData = ThisWorkbook.Worksheets("Data")
    
    For Each c In wsData.Range("L2", wsData.Cells(Rows.Count, "L").End(xlUp))
        v = c.Value
        If dict.exists(CStr(v)) Then
            c.Interior.Color = vbYellow  'already seen this value in L or a data row
        Else
            'search for the value in
            Set f = wsData.Range("A2:J1501").Find(v, lookat:=xlWhole, LookIn:=xlValues, searchorder:=xlByRows)
            If Not f Is Nothing Then
                Set rw = f.EntireRow.Columns("A").Resize(1, 10) 'A to J
                For Each c2 In rw.Cells    'add all values from this row to the dictionary
                    dict(CStr(c2)) = True
                Next c2
                rw.Cut Destination:=wsLineups.Cells(Rows.Count, "A").End(xlUp).Offset(1)
                c.Interior.Color = vbYellow
            Else
                'will there always be a match?
                c.Interior.Color = vbRed 'flag no matching row
            End If
        End If     'haven't already seen this col L value
    Next c         'next Col L value

End Sub
Tim Williams
  • 154,628
  • 8
  • 97
  • 125
  • You are right, this is very close! I think there are (2) problems. The code `dict(CStr(c2)) = True` is adding values that are not in the row that was previously cut and `c.Interior.Color = vbYellow` is only highlighting the value in column L:L that is searched for. Let me know if this makes sense. – safo2238 Apr 25 '22 at 18:43
  • Had the `Cut` in the wrong place. Fixed above. – Tim Williams Apr 25 '22 at 18:57
1

I believe this should do it (updated):

Sub AutoSelect()

Dim wsData As Worksheet, wsLineups As Worksheet
Dim rng As Range, listIDs As Range

Set wsData = ActiveWorkbook.Sheets("Data")
Set wsLineups = ActiveWorkbook.Sheets("Lineups")

Set rng = wsData.Range("A2:J1501")

'get last row col L to define list
LastRowL = wsData.Range("L" & Rows.Count).End(xlUp).Row

Set listIDs = wsData.Range("L2:L" & LastRowL)

'loop through all cells in list
For i = 1 To listIDs.Rows.Count

    myCell = listIDs.Cells(i)
    
    'retrieve first mach in listID
    checkFirst = Application.Match(myCell, listIDs, 0)
    
    'only check first duplicate in list
    If checkFirst = i Then
    
        'get new row for target sheet as well (if sheet empty, starting at two)
        newrow = wsLineups.Range("A" & Rows.Count).End(xlUp).Row + 1
    
        'check if it is already processed
        Set processedAlready = wsLineups.Cells(2, 1).Resize(newrow - 1, rng.Columns.Count).Find(What:=myCell, lookat:=xlWhole, LookIn:=xlValues)
    
        'if so, color yellow, and skip
        If Not processedAlready Is Nothing Then
        
            listIDs.Cells(i).Interior.Color = vbYellow
    
        Else
    
            'get fist match for value, if any (n.b. "xlWhole" ensures whole match)
            Set foundMatch = rng.Find(What:=myCell, lookat:=xlWhole, LookIn:=xlValues)
        
            'checking for a match
            If Not foundMatch Is Nothing Then
            
                'get the row
                foundRow = foundMatch.Row - rng.Cells(1).Row + 1
                
                'specify target range and set it equal to vals from correct row in rng
                wsLineups.Cells(newrow, 1).Resize(1, rng.Columns.Count).Value2 = rng.Rows(foundRow).Value
        
                'clear contents rng row
                rng.Rows(foundRow).ClearContents
        
                'give a color to cells that actually got a match
                listIDs.Cells(i).Interior.Color = vbYellow
        
            Else
                
                'no match
                listIDs.Cells(i).Interior.Color = vbRed
        
            End If
        
        End If

    Else

        'duplicate already handled, give same color as first
        listIDs.Cells(i).Interior.Color = listIDs.Cells(checkFirst).Interior.Color

    End If

Next i

End Sub

Also, I think, slightly faster than the other solution offered (because of the nested loop there?). Update: I got a bit confused about the nested loop in the answer by Tim Williams, but I missed that you also want to "accept" the values in the list that matched on a row that is already gone. I fixed this in the updated version by checking if a value that fails to match on the data range has already been transferred to Lineups. Provided that doing so is permissible, this method avoids the nested loop.

I checked both methods for speed (n = 50) on a list (n = 200) for the full data range, ended up with average of 1.70x faster... But maybe speed is not such a big deal, if you're coming from manual labor :)

ouroboros1
  • 9,113
  • 3
  • 7
  • 26
  • Thank you very much. You are right that speed may not be very important when talking about a couple seconds compared to doing it manually, but I do like having options. And I really enjoy seeing multiple answers so I can learn, which is the ultimate goal! I will give it a try and let you know how it works. – safo2238 Apr 25 '22 at 21:02
  • You are right, your code is super-fast but there is one issue. It is giving me a result for a value in column L that has already shown up in a previous result. If we are searching for the value in L2 and it is found, I don't want to search for any of the values in that row. I want to skip them because they are already used. – safo2238 Apr 25 '22 at 21:35
  • Wait, not sure if I understand correctly. Let's say: L2 = 1, L3 = 2 and there's a row with 1, 2 in there. The first match (L2) will write the row away (and become yellow); but you wanted L3 to still become yellow as well, right? Or: you want to skip it and have it become red? Cos, the latter was actually my first solution :) – ouroboros1 Apr 25 '22 at 21:42
  • Correct, in that scenario I would want to skip searching for L3 (because it was already found in the search for L2) and I want it to be highlighted yellow. The next search should be a value that has not shown up in any of the matches. The code you provided was searching for every value, whether it was previously found in a match or not. – safo2238 Apr 25 '22 at 22:01
  • You are quite right, apologies for that. It should of course check for `processedAlready` *before* trying to find a match... Should be fixed now in the updated code. – ouroboros1 Apr 25 '22 at 22:33
  • It works perfectly now, thank you very much. I learned a lot from these two answers, and I really appreciate it. – safo2238 Apr 26 '22 at 13:08