Identifying Similar Column Headers using a VBA Macro

aryaden

Board Regular
Joined
Jun 9, 2021
Messages
101
Office Version
  1. 2019
Platform
  1. Windows
I Currently have a sheet with multiple columns: Monday1, Monday2, Monday3, Tuesday, Wednesday1, Wednesday2, Thursday, Friday...

I want to create a macro that can identify all the Columns with "Monday" and create a worksheet in the same workbook with those columns listed. Ideally I'd be able to do the same for Wednesday.

I have to deal with many workbooks with column titles that are similar that need to be selected and made into a new worksheet. I am looking to make a macro that can select and group similar column titles regardless of the actual text. For example, if column titles have the same first 5 letters, is there anyway to identify and select those?
 

Excel Facts

Create a chart in one keystroke
Select the data and press Alt+F1 to insert a default chart. You can change the default chart to any chart type
Welcome to the forum.

I got something to work with random data based on choosing the columns based on the first 5 characters as a start. I'm guessing that your headers are in the first row starting at A1. If not, the code will have to be revised.

Note that this requires adding a module file from Chip Pearson's website. In the introduction on the page, you will find a link to a zip file with a .bas file that you can import into your workbook. Once you unzip the file, you can drag and drop it into the VBAProject for your workbook or us File->Insert file to find it. It will put in a module called modFindAll64 into your VBE project.

If you do not include this file, the code will not compile at the Set rTemp = FindAll line.

VBA Code:
Sub headers()
    Dim c1 As Collection
    Dim cHeaders As Collection
    Dim rHeaders As Range, rTemp As Range
    Dim a As Variant
    Dim i As Integer
    Dim left5 As String
    Dim shOld As Worksheet, shNew As Worksheet
    
    'Get header row
    Set rHeaders = Range("A1").Resize(1, Range("A1").CurrentRegion.Columns.Count)
    'Fill array with array of header names
    a = rHeaders.Value
    Set c1 = New Collection
    Set cHeaders = New Collection
    'Fill first collection with header values
    '(using a collection makes it easy below to delete used values)
    For i = LBound(a, 2) To UBound(a, 2)
        c1.Add a(1, i)
    Next
    'For each item in the collection, get the first 5 letters of item 1
    'and add it to the cHeaders collection; any header with the same 5
    'letters is removed
    Do While c1.Count > 0
        left5 = Left(c1(1), 5)
        cHeaders.Add left5
        For i = c1.Count To 2 Step -1
            If Left(c1.Item(i), 5) = left5 Then
                c1.Remove i
            End If
        Next i
        c1.Remove 1
    Loop
    'cHeaders should now hold 1 entry for each unique 5-letter header
    Set shOld = ActiveSheet
    'Create a new sheet for each cHeader and copy the data from the
    'respective columns
    'Requires "modFindAll64" module file from http://www.cpearson.com/excel/findall.aspx
    For i = 1 To cHeaders.Count
        Set shNew = Worksheets.Add(After:=ActiveWorkbook.Worksheets(ActiveWorkbook.Worksheets.Count))
        shNew.Name = cHeaders(i)
        shOld.Select
        Set rTemp = FindAll(rHeaders, cHeaders(i), , xlPart)
        rTemp.EntireColumn.Copy shNew.Range("A1")
    Next i
    shOld.Select
End Sub

This is my test data:
Headers to sheets.xlsm
ABCDEFGH
1Monday1Monday2Monday3TuesdayWednesday1Wednesday2ThursdayFriday
23798116245142626110296
3388411313172355263445217
471297332416427488113177
53441972409230158425449
647753278296356500119250
72483299318526711020837
843824838061177150240428
94011131963275743274339
10139402412622232142259
113642637764374362187205
123134247235198118179189
13654551891877187112370
1411220640278168169488120
15187272199324320403113169
16129448219297275203301396
17157350282119131428416331
18356182388198491462142475
Sheet1


The code generated these sheets:
Untitled.png


"Monda" looks like this:
Headers to sheets.xlsm
ABC
1Monday1Monday2Monday3
237981162
3388411313
471297332
53441972
647753278
724832993
8438248380
9401113196
10139402412
1136426377
1231342472
1365455189
1411220640
15187272199
16129448219
17157350282
18356182388
Monda
 
Upvote 0
Different approach
Code:
Sub Maybe_So()
Dim lr As Long, lc As Long, i As Long, x As Long
Dim wsArr, transf As String
lr = ActiveSheet.Cells.Find("*", , , , xlByRows, xlPrevious).Row
lc = ActiveSheet.Cells(1, ActiveSheet.Columns.Count).End(xlToLeft).Column
wsArr = ActiveSheet.Cells(1, 1).Resize(lr, lc).Value
transf = Application.InputBox("Enter the day to be transferred.", "Column Headers Input.", , , , , , 2)
ThisWorkbook.Sheets.Add(, Sheets(Sheets.Count)).Name = WorksheetFunction.Proper(transf)
x = 1
    For i = LBound(wsArr, 2) To UBound(wsArr, 2)
        If Left(LCase(wsArr(1, i)), Len(transf)) = LCase(transf) Then
            Cells(1, x).Resize(UBound(wsArr)) = Application.Index(wsArr, , i)
            x = x + 1
        End If
    Next i
End Sub
 
Upvote 0
Welcome to the MrExcel board!

Yet another possibility.
I have assumed the original data is on sheet 'Main'

VBA Code:
Sub GenerateSheet()
  Dim Pref As String
  Dim c As Long
 
  Pref = InputBox("Enter start of common name (as many characters as you like)")
  If Len(Pref) > 0 Then
    Application.ScreenUpdating = False
    Sheets("Main").Copy After:=Sheets("Main")
    With Sheets(Sheets("Main").Index + 1)
      For c = .Cells(1, Columns.Count).End(xlToLeft).Column To 1 Step -1
        If Not LCase(.Cells(1, c).Text) Like (LCase(Pref) & "*") Then .Columns(c).Delete
      Next c
      On Error Resume Next
      .Name = Pref
      On Error GoTo 0
    End With
    Application.ScreenUpdating = True
  End If
End Sub
 
Upvote 0
Insert a UserForm and on it add a Label and a Listbox named ListBox1
Copy following code into the UserForm Code Window.
Code:
Private Sub ListBox1_Click()
Dim lr As Long, lc As Long, i As Long, x As Long
Dim wsArr, transf As String, nm As String
nm = ActiveSheet.Name
lr = ActiveSheet.Cells.Find("*", , , , xlByRows, xlPrevious).Row
lc = ActiveSheet.Cells(1, ActiveSheet.Columns.Count).End(xlToLeft).Column
wsArr = ActiveSheet.Cells(1, 1).Resize(lr, lc).Value
transf = ListBox1
Unload Me
Application.ScreenUpdating = False
ThisWorkbook.Sheets.Add(, Sheets(Sheets.Count)).Name = WorksheetFunction.Proper(transf)
x = 1
    For i = LBound(wsArr, 2) To UBound(wsArr, 2)
        If Left(LCase(wsArr(1, i)), Len(transf)) = LCase(transf) Then
            Cells(1, x).Resize(UBound(wsArr)) = Application.Index(wsArr, , i)
            x = x + 1
        End If
    Next i
Sheets(nm).Activate
Application.ScreenUpdating = True
End Sub

Code:
Private Sub UserForm_Initialize()
    ListBox1.List = Array("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday")
End Sub

Put the following code in a regular Module
Code:
Sub Get_It()
    UserForm1.Show
End Sub

Run the "UserForm1.Show" Macro, select a day from the listbox and that's it, finished.

You can set the dimensions of the UserForm, ListBox and Label in the Items properties windows of each.
I have the following:
Userform: Caption "Select a day.", Height 180, Left 0, Top 0, Width 126.
Label: Caption "Select a day to transfer below.", Height 18, Left 6, Top 6, Width 108.
ListBox1: Font Tahoma Bold Size 10, Height 110, Left 6, Top 30, Width 108.
 
Upvote 0
I have to deal with many workbooks with column titles that are similar that need to be selected and made into a new worksheet. I am looking to make a macro that can select and group similar column titles regardless of the actual text. For example, if column titles have the same first 5 letters, is there anyway to identify and select those?
I think that I may not have addressed that correctly with my previous code. This lets you decide how many leading characters should be matched and splits off a sheet based on that number of leading characters.

VBA Code:
Sub GenerateSheets_v2()
  Dim d As Object
  Dim Ky As Variant
  Dim wsOrig As Worksheet
  Dim Pref As String
  Dim c As Long
  
  Const OrigShtName As String = "Main"  '<- Edit to suit
  
  Pref = InputBox("Match first how many characters?")
  If IsNumeric(Pref) Then
    Application.ScreenUpdating = False
    Set d = CreateObject("Scripting.Dictionary")
    d.compareMode = 1
    Set wsOrig = Sheets(OrigShtName)
    With wsOrig
      For c = .Cells(1, Columns.Count).End(xlToLeft).Column To 1 Step -1
        If Len(.Cells(1, c).Text) > 0 Then d(Left(.Cells(1, c).Text, Pref)) = Empty
      Next c
    End With
    For Each Ky In d.Keys
      wsOrig.Copy After:=wsOrig
      With Sheets(wsOrig.Index + 1)
        For c = .Cells(1, Columns.Count).End(xlToLeft).Column To 1 Step -1
          If Not LCase(.Cells(1, c).Text) Like LCase(Ky) & "*" Then .Columns(c).Delete
        Next c
        On Error Resume Next
        .Name = Ky & "_"
        On Error GoTo 0
      End With
    Next Ky
    Application.ScreenUpdating = True
  End If
End Sub

With your sample headings in post #1 for example, and entering 5 in the input box, sheets 'Monda_', 'Tuesd_', ... , 'Frida_' would be created.
If the headings were full month names and 3 was entered then the sheets would be 'Jan_', Feb_' etc
 
Upvote 0
Thank you all for the solutions! This has been very useful as I am new to VBA. I am receiving the data from another person and after a meeting today the format in which I receive the data has changed and my problem has changed a little. I created a mock sheet to demonstrate.

Book1
ABCDEFGHIJ
1DescriptionMondayMondayMondayTuesdayWednesdayThursdayFridayFridaySaturday
2123111121
3AAAAXXX
4BBBBXXXX
5CCCCXXXX
6DDDDXXXX
7EEEEXXX
8FFFFXXXXX
9GGGGXXX
Sheet1


Is there anyway for me to identify all duplicates in Row 1, identify the text in Column A that at least one of the duplicates has an "X" in, and make a new sheet with the descriptions in Colum A that are relevant to the duplicate values in Row 1?

These are what the output sheets would be for the example sheet I included:

For Monday: The descriptions which used to be "BBBB" and "EEEE" are omitted as none of the Mondays have "X"s in these rows
Book1
ABCD
1DescriptionMondayMondayMonday
2123
3AAAAX
4CCCCXX
5DDDDX
6FFFFX
7GGGGX
8
Sheet2


For Friday: The descriptions which used to be "CCCC" and "GGGG" are omitted as none of the Fridays have "X"s in these rows
Book1
ABC
1DescriptionFridayFriday
212
3AAAAX
4BBBBX
5DDDDX
6EEEEX
7FFFFX
Sheet3


Note: The Description values and rows (days in the example included) change in every workbook I get. I am a college intern that would really appreciate the help as I have about 40 different workbooks like these to sort and will probably be getting more in the future!

Thank you so much!
 
Upvote 0
Assuming that the data is on the left hand (or only) worksheet in the workbook, try this with a copy.

VBA Code:
Sub Make_Sheets()
  Dim d As Object
  Dim Ky As Variant
  Dim wsOrig As Worksheet
  Dim cell As Range
  Dim col As Long, LastCol As Long
  
  Set wsOrig = Sheets(1)
  Set d = CreateObject("Scripting.Dictionary")
  With wsOrig
    LastCol = .Cells(1, Columns.Count).End(xlToLeft).Column
    For Each cell In .Range("B1").Resize(, LastCol - 1)
      d(cell.Value) = Empty
    Next cell
  End With
  Application.ScreenUpdating = False
  For Each Ky In d.Keys
    wsOrig.Copy After:=Sheets(Sheets.Count)
    With Sheets(Sheets.Count)
      For col = LastCol To 2 Step -1
        If Not .Cells(1, col).Text = Ky Then .Columns(col).Delete
      Next col
      With .UsedRange
        For col = 2 To .Columns.Count
          .AutoFilter Field:=col, Criteria1:=""
        Next col
        .Offset(1).EntireRow.Delete
      End With
      .Name = Ky
      .AutoFilterMode = False
    End With
  Next Ky
  wsOrig.Activate
  Application.ScreenUpdating = True
End Sub
 
Upvote 0
Thank you so much! That worked and the code was concise and easy to understand!
 
Upvote 0
Assuming that the data is on the left hand (or only) worksheet in the workbook, try this with a copy.

VBA Code:
Sub Make_Sheets()
  Dim d As Object
  Dim Ky As Variant
  Dim wsOrig As Worksheet
  Dim cell As Range
  Dim col As Long, LastCol As Long
 
  Set wsOrig = Sheets(1)
  Set d = CreateObject("Scripting.Dictionary")
  With wsOrig
    LastCol = .Cells(1, Columns.Count).End(xlToLeft).Column
    For Each cell In .Range("B1").Resize(, LastCol - 1)
      d(cell.Value) = Empty
    Next cell
  End With
  Application.ScreenUpdating = False
  For Each Ky In d.Keys
    wsOrig.Copy After:=Sheets(Sheets.Count)
    With Sheets(Sheets.Count)
      For col = LastCol To 2 Step -1
        If Not .Cells(1, col).Text = Ky Then .Columns(col).Delete
      Next col
      With .UsedRange
        For col = 2 To .Columns.Count
          .AutoFilter Field:=col, Criteria1:=""
        Next col
        .Offset(1).EntireRow.Delete
      End With
      .Name = Ky
      .AutoFilterMode = False
    End With
  Next Ky
  wsOrig.Activate
  Application.ScreenUpdating = True
End Sub

Hi Peter_SSs,

Is there anyway to modify this code so that the days there aren't duplicates of, such as Tuesday, Wednesday, Thursday, and Saturday (from the example), would not have the column with the Xs in the output Sheet?

So for Tuesday it would be:
Example Sheet.xlsm
AB
1Description
2
3BBBB
4CCCC
5FFFF
6GGGG
Sheet5


But Monday would remain:
Example Sheet.xlsm
ABCD
1DescriptionMondayMondayMonday
2123
3AAAAX
4CCCCXX
5DDDDX
6FFFFX
7GGGGX
8
Sheet5


Thank you again for all the help!
 
Upvote 0

Forum statistics

Threads
1,214,957
Messages
6,122,466
Members
449,086
Latest member
kwindels

We've detected that you are using an adblocker.

We have a great community of people providing Excel help here, but the hosting costs are enormous. You can help keep this site running by allowing ads on MrExcel.com.
Allow Ads at MrExcel

Which adblocker are you using?

Disable AdBlock

Follow these easy steps to disable AdBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the icon in the browser’s toolbar.
2)Click on the "Pause on this site" option.
Go back

Disable AdBlock Plus

Follow these easy steps to disable AdBlock Plus

1)Click on the icon in the browser’s toolbar.
2)Click on the toggle to disable it for "mrexcel.com".
Go back

Disable uBlock Origin

Follow these easy steps to disable uBlock Origin

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back

Disable uBlock

Follow these easy steps to disable uBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back
Back
Top