actually exploiting RemoveDuplicates()
method of Range
object would ask the very question:
Option Explicit
Sub AddSheets()
Dim myCell As Range
Dim Cities As Range
With Sheets("AllCities")
Set Cities = .Range("A2", .Cells(.Rows.Count, "A").End(xlUp)).SpecialCells(xlCellTypeConstants) '<~~ consider non blank cells of column "A" from cell "A2" down to last non blank cell
Cities.RemoveDuplicates Columns:=Array(1), Header:=xlNo '<~~ remove duplicates
End With
For Each myCell In Cities
Sheets.Add After:=Sheets(Sheets.Count)
ActiveSheet.Name = myCell.Value
Next myCell
End Sub
provided you don't care all duplicates values are lost for ever!
but it'd leave unhandled two important exceptions:
1) duplicate names with regards to sheets already existing prior to macro executing
2) invalid characters in sheet names
you could handle those with dedicated functions which would give green light to next steps, like follows:
Option Explicit
Sub AddSheets()
Dim myCell As Range
Dim Cities As Range
With Sheets("AllCities")
Set Cities = .Range("A2", .Cells(.Rows.Count, "A").End(xlUp)).SpecialCells(xlCellTypeConstants) '<~~ consider non blank cells of column "A" from cell "A2" down to last non blank cell
Cities.RemoveDuplicates Columns:=Array(1), Header:=xlNo '<~~ remove duplicates from list
End With
For Each myCell In Cities
If CheckSheetName(myCell.Value) Then '<~~ check for invalid sheet name...
If CheckSheetDuplicate(ActiveWorkbook, myCell.Value) Then '<~~ ... if valid name then check for duplicates in existent sheets...
Sheets.Add After:=Sheets(Sheets.Count) '<~~ ... if no duplicates sheets then finally add a new sheet...
ActiveSheet.Name = myCell.Value'<~~ ... and give it proper name
End If
End If
Next myCell
End Sub
Function CheckSheetName(shtName As String) As Boolean
Dim invalidChars As Variant
Dim myChar As Variant
invalidChars = Array(":", "/", "\", "?", "*", "[", "]")
'check shtName for forbidden characters
CheckSheetName = True
For Each myChar In invalidChars
If InStr(shtName, myChar) > 0 Then
CheckSheetName = False
Exit For
End If
Next myChar
End Function
Function CheckSheetDuplicate(wb As Workbook, shtName As String) As Boolean
CheckSheetDuplicate = True '<~~ set positive check result. it'll be turned to negative in case of problems ..
On Error Resume Next
CheckSheetDuplicate = wb.Sheets(shtName) Is Nothing '<~~ set negative check result in case of problems from any attempt to use a sheet with given name: for instance trying and use it as an object
End Function
of course you could make further enhancements in checking functions and have them:
Finally here is a very bold sub that (hopefully) consciously exploit the error handling removal to avoid checks and got to the final result
Sub BoldlyAddSheets()
Dim myCell As Range
Dim Cities As Range
Dim mysht As Worksheet
Dim currentShtName As String
With Sheets("AllCities")
Set Cities = .Range("A2", .Cells(.Rows.Count, "A").End(xlUp)).SpecialCells(xlCellTypeConstants) '<~~ consider non blank cells of column "A" from cell "A2" down to last non blank cell
End With
Application.DisplayAlerts = False '<~~ necessary not to have macro interrupted by any prompts risen by possible Delete() method over sheet objects
On Error Resume Next '<~~ ignore errors -> you must know what you are doing till the next "On Error GoTo 0" statement!
For Each myCell In Cities
Set mysht = Sheets(myCell.Value) '<~~ try setting a sheet object with the current cell value and ...
If mysht Is Nothing Then '<~~ ...if unsuccessful then there's no sheet with the wanted name already, so let's try adding it
Sheets.Add After:=Sheets(Sheets.Count) '<~~ 1) add a new sheet
currentShtName = ActiveSheet.Name '<~~ 2) store new sheet default name, to check for things to possibly go wrong...
ActiveSheet.Name = myCell.Value '<~~ 3) try setting the new name...
If ActiveSheet.Name = currentShtName Then ActiveSheet.Delete '<~~ ...if unsuccessful (sheet name with forbidden characters) delete the sheet
Else
Set mysht = Nothing '<~~ set it back to Nothing for subsequent loops
End If
Next myCell
Application.DisplayAlerts = True '<~~ at long last ... turn default alerts handling on...
On Error GoTo 0 '<~~ ... and turn default error handling on, too. this latter just for clarity since "On Error GoTo 0" is automatically done at exiting any sub or function
End Sub