I want to separate text (names) from numbers (IDs), but there are some exceptions.
Code separates text from numbers but some of the IDs have a letter at the beginning.
How do I obtain full ID with a letter, if applicable?
Option Explicit
Sub NamesandID()
Dim RowNum As Long
Dim eChar As Integer
RowNum = 2
Do Until Cells(RowNum, 1).Value = ""
For eChar = 1 To Len(Cells(RowNum, 1))
If IsNumeric(Mid(Cells(RowNum, 1), eChar, 1)) = True Then
Cells(RowNum, 3).Value = Cells(RowNum, 3).Value _
& Mid(Cells(RowNum, 1), eChar, 1)
Else
Cells(RowNum, 2).Value = Cells(RowNum, 2).Value _
& Mid(Cells(RowNum, 1), eChar, 1)
End If
Next
RowNum = RowNum + 1
Loop
End Sub
My two cents.
1): Through formulae:
Formula in B2:
=LET(X,TEXTAFTER(TEXTBEFORE(A2:A5,")"),"("),HSTACK(SUBSTITUTE(A2:A5," ("&X&")","",1),X))
2) Through VBA:
Sub Test()
Dim arr As Variant: arr = Array("Ann Smith (A123456)", "Tom Ford(2453234)", "Alex Mohammet(4447434)(Text)", "Gerard Kowalski(A6739263)")
With CreateObject("vbscript.regexp")
.Pattern = "^(.+?)\s*\(([A-Z]?\d+)\)(.*)$"
For Each el In arr
tmp = .Replace(el, "$1$3|$2")
Debug.Print Split(tmp, "|")(0) 'Print name
Debug.Print Split(tmp, "|")(1) 'Print ID
Next
End With
End Sub
For those interested in a breakdown of the regular expression used, follow this link.
Another option with VBA is to use Split(), for example:
Sub Test()
Dim arr As Variant: arr = Array("Ann Smith (A123456)", "Tom Ford (2453234)", "Alex Mohammet (4447434)(Text)", "Gerard Kowalski (A6739263)")
Dim tmp As String
For Each el In arr
tmp = Split(Split(el, "(")(1), ")")(0)
Debug.Print Application.Trim(Replace(el, "(" & tmp & ")", "")) 'Print Name
Debug.Print tmp 'Print ID
Next
End Sub
Both options would print:
You can do this with a formula:
Name-column: =MID([#worker],1,FIND("(", [#worker])-1)
ID-column: =MID([#worker],FIND("(",[#worker])+1,FIND(")",[#worker])-FIND("(",[#worker])-1)
If you are on the Beta-Channel of excel 365 than you might already have TEXTSPLIT and TEXTBEFORE.
Sub NamesandID()
Dim RowNum As Long
RowNum = 2
Do Until Cells(RowNum, 1).Value = ""
'f you need parenthesis in the name concatenate them at the end, something like below
'Range("B" & RowNum).Value = Split(Range("A" & RowNum), " (")(0) & " ()"
Range("B" & RowNum).Value = Split(Range("A" & RowNum), " (")(0) 'no parenthesis at the end
Range("C" & RowNum).Value = Split(Split(Range("A" & RowNum), " (")(1), ")")(0)
RowNum = RowNum + 1
Loop
End Sub
Related
I'm new using VBA and I'm trying to code into VBA but it didn't work so far, my timestamp data is not common and I got 10000+ rows to do the same formula (sometime excel just crash so i would like to try VBA)
timestamp that I tried split
Edit : add code
Sub Split_text_3()
Dim p As String
For x = 1 To 6 '---How do it until last cell?
Cells(x, 2).Value = Mid(Cells(x, 1).Value, 9, 2) 'combind in same cell
Cells(x, 3).Value = Mid(Cells(x, 1).Value, 5, 3) 'combind in same cell
Cells(x, 4).Value = Mid(Cells(x, 1).Value, 21, 4) 'combind in same cell
Cells(x, 5).Value = Mid(Cells(x, 1).Value, 12, 8)
Next x End Sub
and the data look like this (I tried to separate it first and then might try to combine them later)
image
Please, try the next function:
Function extractDateTime(strTime As String) As Variant
Dim arrD, d As Date, t As Date
arrD = Split(strTime, " ")
d = CDate(arrD(2) & "/" & arrD(1) & "/" & arrD(4))
t = CDate(arrD(3))
extractDateTime = Array(d, t)
End Function
It can be tested in the next way:
Sub testExtractDate()
Dim x As String, arrDate
x = "WED SEP 08 08:13:52 2021"
arrDate = extractDateTime(x)
Debug.Print arrDate(0), arrDate(1)
End Sub
If it returns as you need (I think, yes...), you can use the next function to process the range. It assumes that the column keeping the strings are A:A, and returns in C:D:
Sub useFunction()
Dim sh As Worksheet, lastR As Long, Arr, arrDate, arrFin, i As Long
Set sh = ActiveSheet
lastR = sh.Range("A" & sh.rows.count).End(xlUp).row
Arr = sh.Range("A2:A" & lastR).Value
If IsArray(Arr) Then
ReDim arrFin(1 To UBound(Arr), 1 To 2)
For i = 1 To UBound(Arr)
If Arr(i, 1) <> "" Then
arrDate = extractDateTime(CStr(Arr(i, 1)))
arrFin(i, 1) = arrDate(0): arrFin(i, 2) = arrDate(1)
End If
Next i
sh.Range("C2").Resize(UBound(arrFin), 2).Value = arrFin
Else
sh.Range("C2:D2").Value = extractDateTime(CStr(sh.Range("A2").Value))
End If
End Sub
I think I have another solution (not bulletproof) but it is simplier, quicker and code less solution (no offense FraneDuru!):
Sub DateStamp()
Dim arr, arr_temp, arr_new() As Variant
Dim i As long
'Take cells from selected all the way down to 1st blank cell
'and assign values to an array
arr = ThisWorkbook.ActiveSheet.Range(Selection, Selection.End(xlDown)).Value
ReDim Preserve arr_new(1 To UBound(arr), 1 To 2)
For i = 1 To UBound(arr)
'Make another array by spliting input string by whitespace delimiter (default)
arr_temp = Split(arr(i, 1))
'Construct values in desired "format"
arr_new(i, 1) = "'" & arr_temp(2) & "/" & arr_temp(1) & "/" & arr_temp(4)
arr_new(i, 2) = arr_temp(3)
Next i
'Paste result into Excel
Selection.Offset(0, 1).Resize(UBound(arr), 2) = arr_new
End Sub
All you have to do is to select the cell toy want to start with and run the macro! :)
Bellow also a picture with watches, so you can catch-up what is going on:
Some of my Excel files have lots of Excel named ranges. How can I easily list them all in one place? The information should include things like the Name, Address, Sheet that contains it, etc.
While the Excel file is open, you can go to the Formulas area of the ribbon and select Use in Formulas in the Defined Names tab. At the very bottom is a Paste Names option. But it just gives you a list of Names and Values with no Headers. Plus, if you try to sort them by address, then you get B1, B10, B11, B2, B20, B21, B3, etc.
I found examples of VBA code on OzGrid.com and MrExcel.com, but none of them did exactly what I wanted.
In the answer below is some VBA code that will create a sheet that contains columns for the Sheet Name, Range Name, Absolute and Relative Addresses, the Column and Row and an Original Row so you can always get back to the original list after sorting. It also includes a count SubTotal formula for filtering.
Note that any named ranges that include more than one cell are skipped (e.g. Filters).
Paste the following code into a module and run it.
Option Explicit
' Assumes a sheet with the CodeName RangeNames.
Private Const m_CountCol As String = "A"
Private Const m_FormulaCol As String = "B"
Private Const m_SheetCol As String = "A"
Private Const m_NameCol As String = "B"
Private Const m_AbsoluteAddressCol As String = "C"
Private Const m_RelativeAddressCol As String = "D"
Private Const m_ColumnCol As String = "E"
Private Const m_RowCol As String = "F"
Private Const m_ValueCol As String = "G"
' Allows for sorting and still always being able to get back to the original order.
Private Const m_OrigRowCol As String = "H"
Private Const m_IndexCol As String = "I"
Private Sub LoadNamedRanges()
Dim lRow As Long, lEndCol As Long, lCounter As Long, lSkipped As Long, lIndex As Long, lRangeNameRow As Long
Dim sRefersTo As String, sSheet As String, sAbsoluteAddress As String, sRelativeAddress As String, sColumn As String, sMessage As String
Dim vColumns As Variant
Dim oName As Name
' Alternatively use the following instead.
' With ThisWorkbook.Sheets("Your Sheet Name")
' This assumes a sheet with the CodeName RangeNames
With RangeNames
.Cells.EntireColumn.Clear
.Cells.EntireColumn.ClearComments
.Cells.EntireColumn.ClearFormats
' Delete any skinny rows from prior runs.
Call .Rows("1:1000").Delete(shift:=xlUp)
lRow = 1
.Cells(lRow, m_NameCol).Value = "Name"
.Cells(lRow, m_SheetCol).Value = "Sheet"
.Cells(lRow, m_AbsoluteAddressCol).Value = "Absolute Addr"
.Cells(lRow, m_RelativeAddressCol).Value = "Relative Addr"
.Cells(lRow, m_ColumnCol).Value = "Col"
.Cells(lRow, m_RowCol).Value = "Row"
.Cells(lRow, m_ValueCol).Value = "Value"
.Cells(lRow, m_OrigRowCol).Value = "OrigRow"
''.Cells(lRow, m_IndexCol).Value = "Index"
For Each oName In Names
' Skip named ranges in our Named Range sheet.
If InStr(1, oName.Name, .Name, vbTextCompare) > 0 Then
lSkipped = lSkipped + 1
' Skip named ranges that contain more than one cell (e.g. Print ranges).
ElseIf InStr(oName.RefersTo, ":") > 0 Then
lSkipped = lSkipped + 1
Else
lRow = lRow + 1
lCounter = lCounter + 1
sRefersTo = Replace$(Replace$(oName.RefersTo, "=", vbNullString), "'", vbNullString)
sSheet = Left$(sRefersTo, InStr(sRefersTo, "!") - 1)
sAbsoluteAddress = Mid$(sRefersTo, InStr(sRefersTo, "!") + 1, 1000)
sRelativeAddress = Replace$(Mid$(sRefersTo, InStr(sRefersTo, "!") + 1, 1000), "$", vbNullString, 1, 1)
sColumn = Left$(sRelativeAddress, InStr(sRelativeAddress, "$") - 1)
lRangeNameRow = CLng(Mid$(sRelativeAddress, InStr(sRelativeAddress, "$") + 1, 1000))
sRelativeAddress = Replace$(sRelativeAddress, "$", vbNullString)
.Cells(lRow, m_NameCol).Value = oName.Name
.Cells(lRow, m_SheetCol).Value = sSheet
.Cells(lRow, m_AbsoluteAddressCol).Value = sAbsoluteAddress
.Cells(lRow, m_RelativeAddressCol).Value = sRelativeAddress
.Cells(lRow, m_ColumnCol).Value = sColumn
.Cells(lRow, m_RowCol).Value = lRangeNameRow
.Cells(lRow, m_ValueCol).Value = "'" & oName.Value
.Cells(lRow, m_OrigRowCol).Value = lCounter
''.Cells(lRow, m_IndexCol).Value = oName.Index
Call LoadColumnsArray(vColumns, sColumn)
End If
Next
lEndCol = .Cells(1, Application.Columns.Count).End(xlToLeft).Column
.Range(.Cells(1, "A"), .Cells(lRow, lEndCol)).AutoFilter
.Cells.EntireColumn.AutoFit
' Add a Subtotal formula for filtered counts.
' It will include the header column plus one additional blank row.
lRow = lRow + 1
.Rows(lRow).RowHeight = 3
sAbsoluteAddress = "$B$1:$B$" & lRow
lRow = lRow + 1
.Cells(lRow, m_CountCol) = "Count"
.Cells(lRow, m_CountCol).HorizontalAlignment = xlRight
.Cells(lRow, m_FormulaCol).Formula = "=SubTotal(103, " & sAbsoluteAddress & ") - 1"
.Cells(lRow, m_FormulaCol).HorizontalAlignment = xlLeft
.Cells(lRow, m_FormulaCol).NumberFormat = "###,###,##0"
' Insert blank row at top and copy SubTotal formula cell.
lRow = lRow + 1
Call .Rows(1).Insert(shift:=xlDown)
.Cells(1, m_CountCol) = "Count"
.Cells(1, m_CountCol).HorizontalAlignment = xlRight
.Cells(1, m_FormulaCol).Formula = "=" & m_FormulaCol & lRow
.Cells(1, m_FormulaCol).HorizontalAlignment = xlLeft
.Cells(1, m_FormulaCol).NumberFormat = "###,###,##0"
.Activate
.Cells(3, "A").Select
End With
With ActiveWindow
.FreezePanes = False
.FreezePanes = True
End With
sMessage = GetMessage(lCounter, lSkipped, vColumns)
Call MsgBox(sMessage, vbInformation, "Info")
Set oName = Nothing
End Sub
Private Sub LoadColumnsArray(vColumns As Variant, sColumn As String)
Dim lIndex As Long
Dim bMatched As Boolean
If IsEmpty(vColumns) Then
ReDim vColumns(1, 0)
vColumns(0, 0) = sColumn
vColumns(1, 0) = 0
Else
For lIndex = LBound(vColumns, 2) To UBound(vColumns, 2)
If vColumns(0, lIndex) = sColumn Then
bMatched = True
Exit For
End If
Next
If Not bMatched Then
ReDim Preserve vColumns(1, UBound(vColumns, 2) + 1)
vColumns(0, UBound(vColumns, 2)) = sColumn
vColumns(1, UBound(vColumns, 2)) = 0
End If
End If
vColumns(1, lIndex) = vColumns(1, lIndex) + 1
End Sub
Private Function GetMessage(lCounter As Long, lSkipped As Long, vColumns As Variant) As String
Dim lIndex As Long
Dim sMessage As String
sMessage = "Processed " & lCounter & " named range(s)," & vbNewLine & _
"Skipped " & lSkipped & " named range(s), " & vbNewLine
For lIndex = LBound(vColumns, 2) To UBound(vColumns, 2)
sMessage = sMessage & "Col: " & vColumns(0, lIndex) & " " & vColumns(1, lIndex) & ", " & vbNewLine
''Debug.Print vColumns(0, lIndex), vColumns(1, lIndex)
Next
sMessage = Trim$(sMessage)
sMessage = Trim$(Left$(sMessage, Len(sMessage) - Len(vbNewLine)))
sMessage = Left$(sMessage, Len(sMessage) - 1) & "."
GetMessage = sMessage
End Function
We have the column "following ID", which contains IDs of predecessors in the format "1; 2; 3; 4". Now I want to find the ID inside a specific cell. My problem is, that if I f.e. searching for "1", it also is true when there is a "11, 21, 13, 14, ..." inside the cell. Is there a way to search for the "ID" in "following ID", without getting true when the ID is part of an other ID?
For i = 2 To 250
Dim tmp As String
tmp = ""
If Cells(i, 1) = "" Then Exit For
For j = 2 To 250
If Cells(j, 1) = "" Then
Exit For
End If
If Cells(j, 11) = Cells(i, 1) Then
If tmp = "" Then
tmp = Cells(j, 1)
Else
tmp = tmp & "; " & Cells(j, 1)
End If
End If
Next j
Cells(i, 10) = tmp
Next i
Picture of Data
What you try to do can be described minimally with the following:
- Try to search for "1" in the string "1;2;3;4;11;12;13", returning only "1" and not "11", "12", "13".
This is a way to do it:
split the string to array by ";"
search in the array
The code would look like this:
Option Explicit
Public Sub TestMe()
Dim inputA As String
Dim cnt As Long
Dim arr As Variant
inputA = "1;2;3;4;11;12;13"
arr = Split(inputA, ";")
For cnt = LBound(arr) To UBound(arr)
If 1 = arr(cnt) Then
Debug.Print "Found in position " & cnt
Exit For
End If
Next cnt
End Sub
You can create a UDF like below:
Public Function FindID(rngToCheck As Range, strVal As String) As Boolean
If InStr(1, ";" & Replace(rngToCheck.Value, " ", "") & ";", ";" & strVal & ";", vbTextCompare) > 0 Then FindID = True
End Function
And then check the cell like below assuming your data is in Cell A2:
=FindID(A2,"1")
I am struggling with the following problem.
I want to do following operations on Input Col A and produce output in col B:
1.Remove Duplicates if any ( It was easy and completed )
2.Remove Leading and/or Trailing spaces from the string (It was easy as well and it's done )
3.COLLECT THE DIFFERENT TRANSLATIONS OF A WORD IN SAME CELL - AVOID DUPLICATES ( It's hard and I don't know how to proceed with this problem )
To understand this point have a look at input/output example.
Input:
A
absolution
absolution
absolutism
absolutism, absolute rule
absolutist
absolutist
absorb
absorb
absorb, bind
absorb, take up
absorb
absorb, imbibe, take up
absorb, sorb
absorb, take up
absorb, take up
absorb, imbibe
absorb
absorb
absorber
absorber
absorber
Output:
col B
absolution
absolutism, absolute rule
absolutist
absorb, bind, imbibe, take up, sorb
absorber
I tried with the following code but I am stuck on the third point/step
Option Explicit
Sub StrMac()
Dim wk As Worksheet
Dim i, j, l, m As Long
Dim strc, strd, fstrc, fstrd As String
Dim FinalRowC, FinalRowD As Long
Set wk = Sheet1
wk.Columns(1).Copy Destination:=wk.Columns(3)
wk.Columns(2).Copy Destination:=wk.Columns(4)
wk.Range("$C:$C").RemoveDuplicates Columns:=1, Header:=xlNo
wk.Range("$D:$D").RemoveDuplicates Columns:=1, Header:=xlNo
FinalRowC = wk.Range("C1048576").End(xlUp).Row
FinalRowD = wk.Range("D1048576").End(xlUp).Row
If FinalRowC >= FinalRowD Then
j = FinalRowC
Else
j = FinalRowD
End If
For i = 1 To j
If wk.Range("C" & i).Text <> "" Then
strc = wk.Range("C" & i).Text
strc = Replace(strc, Chr(160), "")
strc = Application.WorksheetFunction.Trim(strc)
wk.Range("C" & i).Value = strc
Else: End If
If wk.Range("D" & i).Text <> "" Then
strd = wk.Range("D" & i).Text
strd = Replace(strd, Chr(160), "")
strd = Application.WorksheetFunction.Trim(strd)
wk.Range("D" & i).Value = strd
Else: End If
Next i
Dim Cet, Det, Fet, Met, s As Variant
Dim newstr
Dim pos, cos As Long
s = 1
For i = 1 To j
If wk.Range("D" & i).Text <> "" Then
l = 2
strd = wk.Range("D" & i).Text
newstr = strd
For m = i + 1 To j
pos = 1100
cos = 2300
fstrd = wk.Range("D" & m).Text
cos = InStr(1, fstrd, ",")
pos = InStr(1, fstrd, strd, vbTextCompare)
If wk.Range("D" & m).Text <> "" And Len(fstrd) > Len(strd) And m <= j And cos <> 2300 And pos = 1 Then
l = 5
newstr = newstr & "," & fstrd
wk.Range("D" & m) = ""
Else: End If
Next m
wk.Range("E" & s) = newstr
s = s + 1
Else: End If
Next i
End Sub
Assuming your input is column A and you want the output in column B (as stated in your question), the following should work for you:
Sub tgr()
Dim ws As Worksheet
Dim rData As Range
Dim aData As Variant
Dim vData As Variant
Dim vWord As Variant
Dim aResults() As String
Dim sUnq As String
Dim i As Long
Set ws = ActiveWorkbook.Sheets("Sheet1")
Set rData = ws.Range("A1", ws.Cells(Rows.Count, "A").End(xlUp))
If rData.Cells.Count = 1 Then
'Only 1 cell in the range, check if it's no blank and output it's text
If Len(Trim(rData.Text)) > 0 Then ws.Range("B1").Value = WorksheetFunction.Trim(rData.Text)
Else
'Remove any extra spaces and sort the data
With rData
.Value = Evaluate("index(trim(" & .Address(external:=True) & "),)")
.Sort .Cells, xlAscending, Header:=xlNo
End With
aData = rData.Value 'Load all values in range to array
ReDim aResults(1 To rData.Cells.Count, 1 To 1) 'Ready the results array
For Each vData In aData
'Get only unique words
If InStr(1, vData, ",", vbTextCompare) = 0 Then
If InStr(1, "," & sUnq & ",", "," & vData, vbTextCompare) = 0 Then
sUnq = sUnq & "," & vData
If i > 0 Then aResults(i, 1) = Replace(aResults(i, 1), ",", ", ")
i = i + 1
aResults(i, 1) = vData
End If
Else
'Add unique different translations for the word
For Each vWord In Split(vData, ",")
If InStr(1, "," & aResults(i, 1) & ",", "," & Trim(vWord) & ",", vbTextCompare) = 0 Then
aResults(i, 1) = aResults(i, 1) & "," & Trim(vWord)
End If
Next vWord
End If
Next vData
End If
'Output results
If i > 0 Then ws.Range("B1").Resize(i).Value = aResults
End Sub
I'm looking at a spreadsheet in excel
Name | Paperwork | Paperwork 1 | Paperwork 2
Joe | 1 | 1 | 1
Jane | 0 | 1 | 0
I'm trying to find the 0 in the spreadsheet, and output something like
There is an error in the Paperwork assigned to Jane for Paperwork 2
The VBA code I have is:
Private Sub CommandButton1_Click()
Dim i As Integer, j As Integer, Staff As String, Consumer As String, Error As String, CurCell As String
MsgBox "Starting the routine..."
For i = 2 To 3
If Cells(i, 2).Value = 0 Then
For j = 3 To 4
If Cells(i, j).Value = 1 Then
CurCell = i & ", " & j
Else
CurCell = i & ", " & j
MsgBox CurCell
End If
Next j
End If
Next i
End Sub
I'm trying to scan paperwork; its a column that says whether the paperwork is completed. Because Joe completed his paperwork, the algorithm moves past it. Jane however is missing paperwork 2. So when the algorithm arrives at position (Jane, Paperwork) it begins to look in row (paperwork)
For (Jane, Paperwork 1) the algorithm sees a 1, and moves to increment
For (Jane, Paperwork 2) the algorithm sees a 0, and what I want to do is display:
"Jane is missing" + Paperwork 2.
I would like to do something at that point like setting the Staff string variable to be = Cell(row i, j).value, and then outputting 'Staff' to somewhere on the spreadsheet, but I don't know the VBA syntax to be able to do so.
Sub ZeroError()
Dim rng As Range
Dim rowREF As Integer 'row reference
Dim colREF As Integer ' column reference
Dim eName As String 'name holder for employee
Dim wAssignment As String 'assignment holder e.g. Paperwork
Dim colLOCATION As Integer ' this is the column you want to put your results in
colLOCATION = 1 ' placing everying in column note that i add 6 in CELLS
rowREF = 1
colREF = 1
eName = ""
wAssignment = ""
Set rng = ActiveSheet.UsedRange
For Each cell In rng
If cell.Value = 0 Then
rowREF = cell.Row
colREF = cell.Column
eName = Cells(rowREF, 1)
wAssignment = Cells(1, colREF)
If (eName <> "" And wAssignment <> "") Then
If Cells(rowREF, colLOCATION + 6) <> "" Then
colLOCATION = colLOCATION + 1
Else
colLOCATION = 1
End If
Cells(rowREF, colLOCATION + 6) = eName & " " & "is missing" & " " & wAssignment
End If
End If
If cell.Value <> 0 Then
rowREF = cell.Row
colREF = cell.Column
eName = Cells(rowREF, 1)
wAssignment = Cells(1, colREF)
If (eName <> "" And wAssignment <> "") Then
If Cells(rowREF, colLOCATION + 6) <> "" Then
colLOCATION = colLOCATION + 1
Else
colLOCATION = 1
End If
Cells(rowREF, colLOCATION + 6) = eName & " " & "has completed" & " " & wAssignment
End If
End If
Debug.Print colLOCATION
Next
End Sub
Adjusted to your settings in the below answer - sorry I am not the most efficient coder but it should work out for you.
Using the .CurrentRegion as a starting point, you should be able to offset and loop through each of the numbered cells. I've put the results into an unused column to the right. this is the best that I could figure out from your narrative.
Sub lost_Paperwork()
Dim iStaffCol As Long, rng As Range
With ActiveSheet 'define this worksheet peoperly!
With .Cells(1, 1).CurrentRegion
iStaffCol = .Columns.Count + 2
For Each rng In .Offset(1, 1).Resize(.Rows.Count - 1, .Columns.Count - 1)
If rng.Value = 0 Then _
.Cells(Rows.Count, iStaffCol).End(xlUp).Offset(1, 0) = _
.Cells(rng.Row, 1).Value & ", missing " & .Cells(1, rng.Column).Value
Next rng
End With
.Cells(1, iStaffCol) = "Staff"
End With
End Sub
Your results should resemble the following.