1

I'm trying to architect a macro to do the following steps:

  1. Compare two lists of data (in this case Column A against Column C)

enter image description here

  1. Output in B any cell that exists in both A and C. Line up the match next to its match in Column A.

enter image description here

  1. Sort both column A and B by their values so that the corresponding cells in A and B are still next to each other after the sort.

enter image description here

Desired result. Notice how the matches in column A and B are still together. This enables users of this macro to quickly eliminate data that only belongs to one of the respective columns and it allows us to retain any information that may be tied to column A, e.g., Column A contains email addresses, and there is a corresponding column next to it that contains phone #'s. We don't want to split that information up. This macro would enable that:

Pastebin of excel data I used: http://pastebin.com/mYuQRMjj

Desired result

This is the macro I've written, which uses a second macro:

Sub Macro()

        Range(Selection, Selection.End(xlDown)).Select
        Application.Run "macro4.xlsm!Find_Matches"
        Range("B1:B284").Select
        ActiveWorkbook.Worksheets("Sheet1").Sort.SortFields.Clear
        ActiveWorkbook.Worksheets("Sheet1").Sort.SortFields.Add Key:=Range("B1:B284") _
             , SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
        With ActiveWorkbook.Worksheets("Sheet1").Sort
             .SetRange Range("A1:B284")
             .Header = xlGuess
             .MatchCase = False
             .Orientation = xlTopToBottom
             .SortMethod = xlPinYin
             .Apply

    End With

End Sub

The second macro that does the comparison is literally ripped straight from Microsoft, with a little extra.

Sub Find_Matches()
Application.ScreenUpdating = False
     Dim CompareRange As Variant, x As Variant, y As Variant
     ' Set CompareRange equal to the range to which you will
     ' compare the selection.
     Set CompareRange = Range("C1:C500")
     ' NOTE: If the compare range is located on another workbook
     ' or worksheet, use the following syntax.
     ' Set CompareRange = Workbooks("Book2"). _
     '   Worksheets("Sheet2").Range("C1:C5")
     '
     ' Loop through each cell in the selection and compare it to
     ' each cell in CompareRange.
     For Each x In Selection
         For Each y In CompareRange
             If x = y Then x.Offset(0, 1) = x
         Next y
     Next x
 Application.ScreenUpdating = True
 End Sub

Using these two macros, I get exactly what I want, but I don't like using limited ranges. I want the macro to be smart enough to determine exactly what the range is, because the people who will be using this macro sometimes will be using a list of 200, sometimes a list of 2,000,000. I want this macro to be a "one size fits all" for range.

I looked into this and the command:

Range(Range("B1"),Range("A1").End(xlDown)).Select

gets exactly the selection I want after Find_Matches runs (I also realize that Find_Matches is using a finite compare range . . . solving my issue for this first Macro will solve that too).

The problem is that I am unsure how to plug that into my Macro. I've tried several implementations and I'm flat out stuck. I can't find an answer for something this specific, but I know I'm very close. Thank you for any help!

edit: This whole method is realllly slow on larger lists (20+ minutes on a list of 100k). If you can suggest some ways to speed it up that would be super helpful!

Community
  • 1
  • 1
  • If your sheets are clean of shapes then you can simply use `Worksheet.UsedRange.Columns("A:C")` for the range. – Mr. Mascaro Nov 05 '14 at 19:54
  • @jbarker2160 that runs the risk of having blank cells that Excel remembers you using and considers part of the `UsedRange`. – Degustaf Nov 05 '14 at 21:03
  • But it looks like this is a data dump, so it shouldn't be a problem with virgin data. – Mr. Mascaro Nov 05 '14 at 21:06
  • @CharlesSeverson Do columns `A` and `C` have the same number of rows? – Degustaf Nov 05 '14 at 21:07
  • @jbarker2160 unless a raw data file is copied/imported into a workbook that has the macro and is rerun regularly. – Degustaf Nov 05 '14 at 21:09
  • And without user intervention there can be no blank remembered cells... – Mr. Mascaro Nov 05 '14 at 21:12
  • @Degustaf, with the code he's using a few extra, blank rows will make abosolutely 0 difference to the outcome. – Mr. Mascaro Nov 05 '14 at 21:15
  • @Degustaf, good question. No. Ideally A will be shorter than C, as you are essentially asking the question "I have a small list A and a big list C, put whatever is in both in B". B and A could theoretically be the same length if every member of A was also in C. – Charles Severson Nov 05 '14 at 21:33
  • I'll throw in a couple pictures to show exactly what I'm trying to do. – Charles Severson Nov 05 '14 at 21:33
  • Pictures added. The macro should do all of those steps . . . and it does in it's current form, but I'd like for the range to be **unbounded**, so I don't have to manually change the range ever. – Charles Severson Nov 05 '14 at 21:42
  • Couldn't you just use an entire column range (i.e. `A:A` and `C:C`)? – Bobort Nov 05 '14 at 22:01

2 Answers2

0

See Error in finding last used cell in VBA for the best way to find the last row of data.

Find the last row and then change your range selection to:

Range("C1:C"&Trim(CStr(lastrow)))

To speed up your macro execution start your macro with:

Application.Calculation = xlCalculationManual
Application.ScreenUpdating = False

and to restore autocalc and screen updates, end your macro with:

Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
Application.Calculate
Community
  • 1
  • 1
ergohack
  • 1,268
  • 15
  • 27
  • I think I see where you're trying to point me, but I'm new to VBA. I'm unsure how this solves my problem and I'm unsure what it accomplishes :/ Any help? Thanks! – Charles Severson Nov 06 '14 at 15:20
  • Looking at your problem, I would approach the solution differently. Since you mentioned that you were happy with your macros, I left them alone. Let me post a separate, more involved answer on how I would approach a solution. – ergohack Nov 06 '14 at 21:08
  • Definitely don't let me convince you I'm happy with my macros . . . they get the job done, but it's ugly and slow, and I don't want to have to define the range. – Charles Severson Nov 07 '14 at 21:58
0
Sub MatchNSort()
Dim lastrow As Long

    'Tell Excel to skip the calculation of all cells and the screen
    Application.Calculation = xlCalculationManual
    Application.ScreenUpdating = False

    'Find the last row in the data
    With ActiveSheet
        If Application.WorksheetFunction.CountA(.Cells) <> 0 Then
            lastrow = .Cells.Find(What:="*", _
                After:=.Range("A1"), _
                Lookat:=xlPart, _
                LookIn:=xlFormulas, _
                SearchOrder:=xlByRows, _
                SearchDirection:=xlPrevious, _
                MatchCase:=False).Row
        Else
            lastrow = 1
        End If
    End With

    'Force a formula in column B to match a from c
    ActiveSheet.Range("B1:B" & lastrow).Formula = _
    "=IFERROR(IF(MATCH(C[-1],C[1]:C[1],0)>0,C[-1],""""),"""")"

    'Force a recalculate
    Application.Calculate

    'Sort columns B and A
    With ActiveSheet
        .Range("A1:B" & lastrow).Select
        .Sort.SortFields.Clear

        'First key sorts column B
        .Sort.SortFields.Add Key:=Range("B1:B" & lastrow) _
        , SortOn:=xlSortOnValues, Order:=xlAscending _
        , DataOption:=xlSortNormal

        'Second key (optional) sort column A, after defering to column B
        .Sort.SortFields.Add Key:=Range("A1:A" & lastrow) _
        , SortOn:=xlSortOnValues, Order:=xlAscending _
        , DataOption:=xlSortNormal

        .Sort.SetRange Range("A1:B" & lastrow)
        .Sort.Header = xlGuess
        .Sort.MatchCase = False
        .Sort.Orientation = xlTopToBottom
        .Sort.SortMethod = xlPinYin
        .Sort.Apply

    End With

    'Return autocalulation and screen updates
    Application.Calculation = xlCalculationAutomatic
    Application.ScreenUpdating = True
    Application.Calculate

End Sub
ergohack
  • 1,268
  • 15
  • 27