Change value of a range of cells, when a row limit is met - excel

The results I need is shown in this image.
Previous data, and results needed
I have 1 column of data with multiple repeating Values, and need to CHANGE these Values if they repeat more times than a limit I set.
For example, there are "Andre" 3 times in the column, "Bruno" 2 times in the column, and "Charlie" 7 times in the column. The repeat limit is 2. The result I need would be:
Andre 1
Andre 1
Andre 2
Bruno
Bruno
Charlie 1
Charlie 1
Charlie 2
Charlie 2
Charlie 3
Charlie 3
Charlie 4
Bruno is left the same, as it does not EXCEED the limit I set, which is 2. Is this possible to do with VBA?
Note: very early first time learner of VBA.

Append Indexes in a Particular Way
Option Explicit
Sub AppendIndexes()
' Define constants.
' Source
Const sName As String = "Sheet1"
Const sFirstCellAddress As String = "B2"
Const sRowLimitCellAddress As String = "D7"
' Destination
Const dName As String = "Sheet1"
Const dFirstCellAddress As String = "B16"
Const dDelimiter As String = " "
' Reference the workbook ('wb').
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
' Reference the source worksheet ('sws').
Dim sws As Worksheet: Set sws = wb.Worksheets(sName)
' Attempt to store the row limit in a variable ('sRowLimit').
Dim sRowLimit As Long
On Error Resume Next
sRowLimit = sws.Range(sRowLimitCellAddress).Value
On Error GoTo 0
' Validate the row limit.
If sRowLimit < 1 Then
MsgBox "The row limit needs to be an integer greater than 0.", vbCritical
Exit Sub
End If
' Reference the first source cell ('sfCell').
Dim sfCell As Range: Set sfCell = sws.Range(sFirstCellAddress)
Dim srg As Range
' Reference the source (one-column) range ('srg').
With sfCell.CurrentRegion.Columns(sfCell.Column)
Set srg = sfCell.Resize(.Row + .Rows.Count - sfCell.Row)
End With
' Note that there are many different ways to do it.
' To see if it is the correct range you can use e.g.:
'Debug.Print srg.Address(0, 0)
' or:
'MsgBox srg.Address(0, 0)
' Store the number of rows of the source range in a variable ('rCount').
Dim rCount As Long: rCount = srg.Rows.Count
Dim Data() As Variant
' Store the values from the source range
' in a 2D one-based one-column array, the data array ('Data').
If rCount = 1 Then
ReDim Data(1 To 1, 1 To 1): Data(1, 1) = srg.Value
Else
Data = srg.Value
End If
' Define a new dictionary (dict).
' Its 'keys' will hold the unique strings from the source range.
' Its 'items' will hold the rows in the unique counts array.
Dim dict As Object: Set dict = CreateObject("Scripting.Dictionary")
dict.CompareMode = vbTextCompare ' case-insensitive i.e. 'A = a'
' Define the unique counts array ('uCounts'), a 1D one-based array
' with the same number of rows as the number of rows in the data array,
' to hold the count of each unique string.
Dim uCounts() As Long: ReDim uCounts(1 To rCount)
' The size of the array is probably too big but the 'mrCount' variable
' will determine the number of rows of interest, the mapping rows count.
Dim r As Long ' Current Data Array Row
Dim mr As Long ' Current Mapping Row
Dim mrCount As Long ' Current and Final Mapping Rows Count
Dim cString As String ' Current Data Array Value Converted to a String
' Loop through the rows of the data array...
For r = 1 To rCount
' Retrieve the current value from the data array converted to a string.
cString = CStr(Data(r, 1))
' Replace the value with the string.
Data(r, 1) = cString
' Check if the string exists in the 'keys' of the dictionary.
If Not dict.Exists(cString) Then
mrCount = mrCount + 1 ' increment the mapping rows count...
dict(cString) = mrCount ' ... and write it to the associated 'item'
mr = mrCount ' retrieve the current mapping row
Else
mr = dict(cString) ' retrieve the current mapping row
End If
' In the current mapping row of the unique counts array,
' increment the number by 1.
uCounts(mr) = uCounts(mr) + 1
Next r
' Define the unique indexes array ('uIndexes'), a 1D one-based array
' with the same number of rows as the mapping rows count ('mrCount'),
' to hold the current index.
Dim uIndexes() As Long: ReDim uIndexes(1 To mrCount)
' Loop through the elements of the unique array and for each value
' greater than the row limit, write 1 to it.
For mr = 1 To mrCount
If uCounts(mr) > sRowLimit Then
uIndexes(mr) = 1
End If
Next mr
Erase uCounts
' Define the indexes counts array ('uIndexes'), a 1D one-based array
' with the same number of rows as the mapping rows count ('mrCount'),
' to hold the current indexes count, a number from 1 to the row limit.
Dim iCounts() As Long: ReDim iCounts(1 To mrCount)
Dim iCount As Long ' Current Index Count
Dim uIndex As Long ' Current Unique Index
' Write the resulting strings to the data array.
' Loop through the rows of the data array.
For r = 1 To rCount
' Retrieve the string from the current row of the data aray.
cString = Data(r, 1)
' Retrieve the mapping row for the current string.
mr = dict(cString)
' Retrieve the unique index for the current mapping row.
uIndex = uIndexes(mr)
If uIndex > 0 Then
' Increment the current index count by 1.
iCount = iCounts(mr) + 1
' Check if the current index count is greater than the row limit.
If iCount > sRowLimit Then ' it is greater
iCount = 1 ' reset the current index count
uIndex = uIndex + 1 ' increment the 'uIndex' by 1, and...
uIndexes(mr) = uIndex ' ... write it to the unique indexes array
'Else ' the current count is not greater than the row limit
End If
iCounts(mr) = iCount ' write the count to the indexes counts array
' Build and write the resulting string to the current row
' of the data array (overwriting the (previous) string).
Data(r, 1) = cString & dDelimiter & CStr(uIndex)
End If
Next r
Erase iCounts
Erase uIndexes
Set dict = Nothing
' Reference the destination worksheet ('dws').
Dim dws As Worksheet: Set dws = wb.Worksheets(dName)
' Reference the first destination cell ('dfCell').
Dim dfCell As Range: Set dfCell = dws.Range(dFirstCellAddress)
' Reference the destination (one-column) range ('drg').
Dim drg As Range: Set drg = dfCell.Resize(rCount)
' Write the strings from the data array to the destination range.
drg.Value = Data
' Clear below.
drg.Resize(dws.Rows.Count - drg.Row - rCount + 1).Offset(rCount).Clear
' Inform.
MsgBox "Indexes appeded.", vbInformation
End Sub

If you must use VBA:
Sub NumberIfVariableRowLimit()
Dim I As Long 'Iterate
Dim SrcRG As Range 'Source Range
Dim SrcArray() 'Source Array
Dim DestArray() 'Destination array
Dim CellCnt As Long 'Count of cells in range
Dim ItemCnt As Long 'Count keeping track of item number for each name
Dim TempCnt As Long 'Count of occurances of this item in source array
Dim TempStr As String 'Most recently checked item
Dim RowLimit As Long 'Row limit
RowLimit = 3 'Source this data however you like.
'Range/Array Setup
Set SrcRG = ActiveSheet.Range("B1:B28") 'Source Range
CellCnt = SrcRG.Count 'Count cells in source range
SrcArray = SrcRG 'Store range values in array
ReDim DestArray(1 To CellCnt) 'Resize output array
TempStr = "%&%" 'Initial Value
For I = 1 To CellCnt 'Iterate through array
ItemCnt = CountIfArray(SrcArray, SrcArray(I, 1)) 'Get occurances of item in array
'change -or- notchange value, move to output array
If ItemCnt > RowLimit Then
If TempStr = SrcArray(I, 1) Then
TempCnt = TempCnt + 1
Else
TempCnt = RowLimit
End If
DestArray(I) = SrcArray(I, 1) & " " & Application.WorksheetFunction.RoundDown(TempCnt / RowLimit, 0)
Else
DestArray(I) = SrcArray(I, 1)
End If
TempStr = SrcArray(I, 1) 'Store item value for next iteration
Next I
Sheet1.Range("C1").Resize(CellCnt, 1).Value = Application.Transpose(DestArray()) 'output
End Sub
Function CountIfArray(ARR(), vItem) As Long
Dim X As Long
Dim Y As Long
CountIfArray = 0
For X = LBound(ARR, 1) To UBound(ARR, 1)
For Y = LBound(ARR, 2) To UBound(ARR, 2)
If ARR(X, Y) = vItem Then
CountIfArray = CountIfArray + 1
End If
Next Y
Next X
End Function
Example of output:
Example using variable rowlimit:

Related

When appending unique values in array to an excel range, highlight the cell if it is a duplicate

I have an array of values result that I got from a REST API call - result = [1,2,3,4,5] and they are parsed in as variant in the AppendUnique function.
What I want to do:
AppendUnique function appends unique values from a growing result array to a range in excel. I want to add a new feature to AppendUnique, where the repeated values in the result array will be highlighted in the excel cell.
Explanation on my current code:
In the beginning
I input each value in the array result to populate cells from A1 to A5 (the range is dynamic, based on the number of values in the array, so might not be A5 all the time).
So, if the range (A1-A100) is empty, we populate the cells normally.
^ this part is completed
As the result array grows
Since the result will increase as we run the Macro again, for example, 15 minutes later the result may become [1,2,3,4,5,6,7,8]
So, if the range (A1-A5) is not empty, we append the array's additional items at the back of the cell range, if they do not appear in the range (meaning they are additional ones)
^ this part is completed
The result may also contain duplicates, for example, 30 minutes later, the result may become [1,2,3,4,5,6,7,8,3], where 3 is the duplicate.
If there is duplicate - 3, the cell A3 (where we populated 3) needs to be highlighted.
^ this question is about this part
My current code:
Sub AppendUnique( _
Arr() As Variant, _
ByVal ws As Worksheet, _
ByVal FirstCellAddress As String, _
Optional ByVal OverWrite As Boolean = False)
' Write the data from the source range to the source array ('sData').
' Reference the first destination cell ('fCell').
If ws.FilterMode Then ws.ShowAllData
Dim fCell As Range: Set fCell = ws.Range(FirstCellAddress)
Dim sData() As Variant, srCount As Long
With fCell
Dim lCell As Range: Set lCell = .Resize(ws.Rows.Count - .Row + 1) _
.Find("*", , xlFormulas, , , xlPrevious)
If Not lCell Is Nothing Then
srCount = lCell.Row - .Row + 1
If srCount = 1 Then
ReDim sData(1 To 1, 1 To 1): sData(1, 1) = .Value
Else
sData = .Resize(srCount).Value
End If
If Not OverWrite Then Set fCell = lCell.Offset(1)
End If
End With
' Write the unique data from the source array to a dictionary.
Dim dict As Object: Set dict = CreateObject("Scripting.Dictionary")
dict.CompareMode = vbTextCompare
Dim sr As Long
For sr = 1 To srCount: dict(CStr(sData(sr, 1))) = Empty: Next sr
Erase sData
' Define the destination array ('dData').
Dim lb As Long: lb = LBound(Arr)
Dim ub As Long: ub = UBound(Arr)
Dim dData() As Variant: ReDim dData(1 To ub - lb + 1, 1 To 1)
' Check the values from the given array ('Arr') against the values
' in the dictionary and write the non-matches to the destination array.
Dim dr As Long, c As Long, cString As String
For c = lb To ub
cString = CStr(Arr(c))
If Len(cString) > 0 Then ' is not blank
If Not dict.Exists(cString) Then ' is not in the dictionary
dict(cString) = Empty ' prevent dupes from the given array
dr = dr + 1
dData(dr, 1) = cString
End If
End If
Next c
If dr = 0 Then
MsgBox "No new values found.", vbExclamation
Exit Sub
End If
' Write the values from the destination array to the destination range.
fCell.Resize(dr).Value = dData
If OverWrite Then ' clear below
fCell.Resize(ws.Rows.Count - fCell.Row - dr + 1).Offset(dr).Clear
End If
' Inform.
MsgBox "Data appended.", vbInformation
End Sub
I initially thought maybe I can do something under the line - If Len(cString) > 0 Then, to add If dict. Exists(cstring) Then, highlight the cell by doing something like interior.color = vbYellow.
However, I realised that in my current code, the products are appended altogether after checking the repeated items, so I am not exactly sure how to highlight the cell of repeated value, since we are not looping over the appended range.
Any help would be greatly appreciated, thanks in advance.
Use the dictionary value to store a reference to the relevant row. The complication is to differentiate between existing keys from the sheet and those added from the array. For the Overwrite mode the values from the sheet become obsolete. I have used a concatenated string of the row offset and either ";sht" or ";arr". It is easy to separate the 2 values with split().
To identify duplicate in the array I have added another dictionary - dupl.
Sub AppendUnique( _
Arr() As Variant, _
ByVal ws As Worksheet, _
ByVal FirstCellAddress As String, _
Optional ByVal OverWrite As Boolean = False)
If ws.FilterMode Then ws.ShowAllData
Dim fCell As Range, lCell As Range, tcell As Range
Dim sData() As Variant, srCount As Long
' Write the data from the source range to the source array ('sData').
' Reference the first destination cell ('fCell').
Set fCell = ws.Range(FirstCellAddress)
If Len(fCell) = 0 Then
srCount = 0
' target cell for appending new items
Set tcell = fCell
fCell.ClearFormats
Else
Set lCell = ws.Cells(ws.Rows.Count, fCell.Column).End(xlUp)
srCount = lCell.Row - fCell.Row + 1
If srCount > 1 Then
sData = fCell.Resize(srCount).Value2
Else
ReDim sData(1 To 1, 1 To 1):
sData(1, 1) = fCell.Value2
End If
' clear any existing coloring
fCell.Resize(srCount).ClearFormats
' target cell for appending new items
Set tcell = lCell.Offset(1)
End If
' Write the unique data from the source array to a dictionary.
Dim dict As Object, sr As Long, r As Long
Set dict = CreateObject("Scripting.Dictionary")
dict.CompareMode = vbTextCompare
r = 0 ' row offset
If srCount > 0 Then
For sr = 1 To UBound(sData)
dict(CStr(sData(sr, 1))) = r & ";sht" ' fcell row offset +1
r = r + 1
Next sr
End If
' reset target cell
If OverWrite Then
Set tcell = fCell
r = 0
End If
' Define the destination array ('dData').
Dim lb As Long, ub As Long
Dim dr As Long, c As Long, cString As String
Dim dData() As Variant:
lb = LBound(Arr)
ub = UBound(Arr)
ReDim dData(1 To ub - lb + 1, 1 To 1)
' Check the values in Arr
' against the values in the dictionary and
' write the non-matches to the destination array.
Dim dupl As Object, k
Set dupl = CreateObject("Scripting.Dictionary")
For c = lb To ub
' dictionary key
k = CStr(Arr(c))
If Len(k) > 0 Then ' is not blank
If Not dict.Exists(k) Then
' is not in the dictionary
' prevent dupes from the given array
dict(k) = r & ";arr ' store fcell offset"
r = r + 1
dr = dr + 1
dData(dr, 1) = k
End If
' check for duplicates in arr
If dupl.Exists(k) Then
dupl(k) = dupl(k) + 1
Else
dupl.Add k, 1
End If
End If
Next c
' clear existing data
If OverWrite And srCount > 0 And dr > 0 Then
fCell.Resize(srCount).Clear
End If
' Write the values from the destination array
' to the destination range.
If dr > 0 Then
tcell.Resize(dr).Value = dData
End If
' highligh if duplicate
Dim ar
For Each k In dupl.keys
If dupl(k) > 1 Then
ar = Split(dict(k), ";")
r = ar(0)
If dr > 0 And OverWrite And ar(1) = "sht" Then
' do nothing as row information is useless
' for existing value with overwrite
Else
fCell.Offset(r).Interior.Color = RGB(255, 255, 0)
End If
End If
Next
If dr = 0 Then
MsgBox "No new values found.", vbExclamation
Else
' Inform.
MsgBox dr & " Data rows appended.", vbInformation
End If
End Sub

Defining the end of the Range with last cell with a value instead of the row number

I use two data dumps which are saved in OPL_Dump and OPL_DUMP_2 sheets.
The code I am trying to improve, finds the data in one of the dumps and copies and pastes as a new parameter as addition to the same corresponding value it sees for the other dump.
The length of both the data dumps varies. I manually amend the length of the range every time.
I am trying to make my code a bit more robust
I tried defining N and L instead of fixed numbers of last rows.
Sub Merging_Both_Dumps_for_Product_Type()
Dim out() As String
'Dim out2() As String
L As Long
L = ThisWorkbook.Sheets("OPL_DUMP_2").Select.Cells(Rows.Count, "B").End(xlUp).Row
ThisWorkbook.Sheets("OPL_DUMP_2").Select
keyarray = Range("F" & 2 & ":F" & L)
valuearray = Range("J" & 2 & ":J" & L)
N As Long
N = ThisWorkbook.Sheets("OPL_DUMP").Select.Cells(Rows.Count, "B").End(xlUp).Row
ReDim out(N, 0)
For j = 2 To N
ind = Index(keyarray, ThisWorkbook.Sheets("OPL_DUMP").Cells(j, 2).Value)
out(j - 2, 0) = valuearray(ind, 1)
Next j
'ReDim out2(1, 0)
'out2(1, 0) = "test"
'ThisWorkbook.Sheets("OPL_DUMP").Range("AD2:AD3") = out2()
ThisWorkbook.Sheets("OPL_DUMP").Range("AC" & 2 & ":AC" & N) = out
End Sub
Try this code, should work fine, fast and always no matter the size of your dumps:
Option Explicit
Sub Merging_Both_Dumps_for_Product_Type()
'You need the reference Microsoft Scripting Runtime
'under tools-references activated for this code to work.
Dim output_values As Dictionary
Set output_values = load_output_values(ThisWorkbook.Sheets("OPL_DUMP_2").UsedRange.Value)
'Store your output worksheet inside an array
Dim arr As Variant: arr = ThisWorkbook.Sheets("OPL_DUMP").UsedRange.Value
'loop through the array
Dim i As Long
For i = 2 To UBound(arr)
'check if the value in col B exists in the dictionary
If output_values.Exists(arr(i, 2)) Then
arr(i, 29) = output_values(arr(i, 2))
End If
Next i
'paste back the array to the worksheet
ThisWorkbook.Sheets("OPL_DUMP").UsedRange.Value = arr
'Note that using worksheet.usedrange.value will store
'everything in the sheet that has been used, even if its blank
'meaning if you do ctrl+end in your keyboard, the array will be
'as big as A1: the cell where ctrl+end sends you.
End Sub
Private Function load_output_values(arr As Variant) As Dictionary
'this function will store in a dictionary each key (col F = index 2)
'with it's item (col J = index 10)
'Since we stored the sheet inside an array we can loop through it
Set load_output_values = New Dictionary ' init the dictionary
Dim i As Long
For i = 2 To UBound(arr)
'first check either column B is empty or already exists
'will take the first ocurrence if col B is duplicated.
If Not arr(i, 2) = vbNullString _
And Not load_output_values.Exists(arr(i, 2)) Then
load_output_values.Add arr(i, 2), arr(i, 10)
End If
Next i
End Function
Lookup Data Using Application.Match
Option Explicit
Sub LookupData()
' 1. Define constants.
' Source
Const sName As String = "OPL_DUMP_2"
Const skCol As String = "F" ' 2. ... lookup the key...
Const svCol As String = "J" ' 3. ... read the associated value...
Const sfRow As Long = 2
' Destination
Const dName As String = "OPL_DUMP"
Const dkCol As String = "B" ' 1. Read the key...
Const dvCol As String = "AC" ' 4. ... write the value.
Const dfRow As Long = 2
' Workbook
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
' 2. Reference the source key (one-column) range ('skrg')
' and write the values from the source value (one-column) range ('svrg')
' to a 2D one-based (one-column) array ('svData').
' We will use 'skrg' because 'Application.Match' is faster on a range.
' We will use 'svData' because reading from an array is faster than
' from a range.
Dim sws As Worksheet: Set sws = wb.Worksheets(sName)
Dim slRow As Long: slRow = sws.Cells(sws.Rows.Count, skCol).End(xlUp).Row
Dim srCount As Long: srCount = slRow - sfRow + 1
If srCount < 1 Then
MsgBox "No data in column range.", vbCritical
Exit Sub
End If
Dim skrg As Range: Set skrg = sws.Cells(sfRow, skCol).Resize(srCount)
' ... which is the same as:
'Set skrg = sws.Range(sws.Cells(sfRow, skCol), sws.Cells(slrow, skCol))
Dim svrg As Range: Set svrg = skrg.EntireRow.Columns(svCol)
Dim svData() As Variant
If srCount = 1 Then ' one cell
ReDim svData(1 To 1, 1 To 1): svData(1, 1) = svrg.Value
Else ' multiple cells
svData = svrg.Value
End If
' 3. Reference the destination key (one-column) range ('skrg')
' and write its values the to a 2D one-based (one-column) array,
' the destination keys array ('dkData').
' We will use 'dkData' because reading from an array is faster than
' from a range.
Dim dws As Worksheet: Set dws = wb.Worksheets(dName)
Dim dlRow As Long: dlRow = dws.Cells(dws.Rows.Count, dkCol).End(xlUp).Row
Dim drCount As Long: drCount = dlRow - dfRow + 1
If drCount < 1 Then
MsgBox "No data in column range.", vbCritical
Exit Sub
End If
Dim dkrg As Range: Set dkrg = dws.Cells(dfRow, dkCol).Resize(drCount)
' ... which is the same as:
'Set dkrg = dws.Range(dws.Cells(dfRow, dkCol), dws.Cells(dlrow, dkCol))
Dim dkData() As Variant
If drCount = 1 Then ' one cell
ReDim dkData(1 To 1, 1 To 1): dkData(1, 1) = dkrg.Value
Else ' multiple cells
dkData = dkrg.Value
End If
' 3. Write the matching values to the destination values array ('dvData'),
' a 2D one-based one-column array, with the same number of rows
' as the number of rows of the destination keys array.
Dim dvData() As Variant: ReDim dvData(1 To drCount, 1 To 1)
Dim sr As Variant
Dim dValue As Variant
Dim dr As Long
For dr = 1 To drCount
dValue = dkData(dr, 1)
sr = Application.Match(dValue, skrg, 0)
If IsNumeric(sr) Then ' is a number (the row index)
dvData(dr, 1) = svData(sr, 1)
'Else ' is an error value (no match); do nothing
End If
Next dr
' 4. Write the values from the destination values array
' to the destination values range ('dvrg').
Dim dvrg As Range: Set dvrg = dkrg.EntireRow.Columns(dvCol)
dvrg.Value = dvData
' Save the workbook.
'wb.Save
' 5. Inform.
MsgBox "Lookup has finished.", vbInformation
End Sub

Fuction has an array inside, gives back error 1004

I have a formula that works fine when hardcoded but gives me error 1004 when I put it in code. I think it's because I am using an array inside the formula. I tried .FormulaArray but it still returns an error.
ws_a.Range("D2:D" & LastRowCriar).Formula = "=IFERROR(IFERROR(IFERROR(INDEX(IBAN!D:D,MATCH(B2&I2,IBAN!F:F&IBAN!E:E,0)),INDEX(IBAN!D:D,MATCH(B2&I2-1,IBAN!F:F&IBAN!E:E,0))),INDEX(IBAN!D:D,MATCH(B2&I2-2,IBAN!F:F&IBAN!E:E,0))),INDEX(IBAN!D:D,MATCH(B2&I2-3,IBAN!F:F&IBAN!E:E,0)))"
Once again, the formula works when hardcoded, I just need some help on how to use it in VBA. Probably, I have to declare those arrays but I am not sure (if I have to or how to do it).
Hardcoded:
=IFERROR(IFERROR(IFERROR(INDEX(IBAN!D:D;MATCH(B2&I2;IBAN!F:F&IBAN!E:E;0));INDEX(IBAN!D:D;MATCH(B2&I2-1;IBAN!F:F&IBAN!E:E;0)));INDEX(IBAN!D:D;MATCH(B2&I2-2;IBAN!F:F&IBAN!E:E;0)));INDEX(IBAN!D:D;MATCH(B2&I2-3;IBAN!F:F&IBAN!E:E;0)))
Thank you!
Replace Slow Formula With VBA
With this setup, the code will write the resulting values to the 4th ("D") column (cSh1C3) in Sheet1 named "Sheet1". The sheet name wasn't mentioned, so change it appropriately, Also change the other constants to fit your needs. Maybe change cSh1C3 to an empty column first, to see if the code does the expected. There will be no formulas just the values since your formula is slowing down the worksheet considerably. If this code is doing the expected, then the formula isn't. In some cases the results differ, but I think the code is correct. When this happens, check the accuracy manually.
Option Explicit
Sub ReplaceSlowFormulaWithVBA()
' Sheet1
Const cSh1 As String = "Sheet1" ' Sheet1 Name
Const cSh1FR As Long = 2 ' Sheet1 First Row Number
Const cSh1C1 As Variant = 2 ' "B" ' Sheet1 First Column Number/Letter
Const cSh1C2 As Variant = 9 ' "I" ' Sheet1 Second Column Number/Letter
Const cSh1C3 As Variant = 4 ' "D" ' Target Column Number/Letter
' (Sheet1 Third Column Number/Letter)
Const cReduce As Long = 3 ' Reduce Number
' Sheet2
Const cSh2 As String = "IBAN" ' Sheet2 Name
Const cSh2FR As Long = 2 ' Sheet2 First Row Number
Const cSh2C1 As Variant = 6 ' "F" ' Sheet2 First Column Number/Letter
Const cSh2C2 As Variant = 5 ' "E" ' Sheet2 Second Column Number/Letter
Const cSh2C3 As Variant = 4 ' "D" ' Source Column Number/Letter
' Sheet2 Third Column Number/Letter
Dim ws1 As Worksheet ' First Worksheet
Dim ws2 As Worksheet ' Second Worksheet
Dim rng As Range ' Various Ranges
Dim vnt1 As Variant ' Sheet1 Array
Dim vnt1C1 As Variant ' Sheet1 First Column Array
Dim vnt1C2 As Variant ' Sheet1 Second Column Array
Dim vntT As Variant ' Target Array (Sheet1 Third Column Array)
Dim vnt2 As Variant ' Sheet2 Array
Dim vnt2C1 As Variant ' Sheet2 First Column Array
Dim vnt2C2 As Variant ' Sheet2 Second Column Array
Dim vntS As Variant ' Source Array (Sheet2 Third Column Array)
Dim LR As Long ' Last Row Compare Number
Dim sh1LR As Long ' Sheet1 (Current) Last Row Number
Dim sh2LR As Long ' Sheet2 (Current) Last Row Number
Dim UB1 As Long ' Sheet1 Arrays Upper Bound
Dim UB2 As Long ' Sheet2 Arrays Upper Bound
Dim i As Long ' Various Counters
Dim j As Long ' Second Array Elements Counter
Dim k As Long ' Reduce Counter
Dim lng1 As Long ' Current Sheet1 Array Value
Dim lng2 As Long ' Current Sheet2 Array Value
' IN RANGES
' Define Worksheets.
Set ws1 = ThisWorkbook.Worksheets(cSh1)
Set ws2 = ThisWorkbook.Worksheets(cSh2)
' Calculate Sheet1 Last Row Number.
Set rng = ws1.Columns(cSh1C1): GoSub LastRow: sh1LR = LR
Set rng = ws1.Columns(cSh1C2): GoSub LastRow
If LR > sh1LR Then sh1LR = LR
' Calculate Sheet2 Last Row Number.
Set rng = ws2.Columns(cSh2C1): GoSub LastRow: sh2LR = LR
Set rng = ws2.Columns(cSh2C2): GoSub LastRow
If LR > sh2LR Then sh2LR = LR
Set rng = ws2.Columns(cSh2C3): GoSub LastRow
If LR > sh2LR Then sh2LR = LR
' Write Column Ranges to Arrays.
vnt1C1 = ws1.Cells(cSh1FR, cSh1C1).Resize(sh1LR - cSh1FR + 1)
vnt1C2 = ws1.Cells(cSh1FR, cSh1C2).Resize(sh1LR - cSh1FR + 1)
vnt2C1 = ws2.Cells(cSh2FR, cSh2C1).Resize(sh2LR - cSh2FR + 1)
vnt2C2 = ws2.Cells(cSh2FR, cSh2C2).Resize(sh2LR - cSh2FR + 1)
vntS = ws2.Cells(cSh2FR, cSh2C3).Resize(sh2LR - cSh2FR + 1)
' Define Target Range.
Set rng = ws1.Cells(cSh1FR, cSh1C3).Resize(sh1LR - cSh1FR + 1)
' Release worksheet object variables.
Set ws2 = Nothing
Set ws1 = Nothing
' IN ARRAYS
' Define and populate Sheet1 Array from the two Sheet1 Column Arrays.
UB1 = UBound(vnt1C1)
ReDim vnt1(1 To UB1) ' 1D 1-based (1-row)
For i = 1 To UB1: vnt1(i) = vnt1C1(i, 1) & vnt1C2(i, 1): Debug.Print vnt1(i): Next i
' Erase the two Sheet1 Column Arrays.
Erase vnt1C1: Erase vnt1C2
' Define and populate Sheet2 Array from the two Sheet2 Column Arrays.
UB2 = UBound(vnt2C1)
ReDim vnt2(1 To UB2) ' 1D 1-based (1-row)
For i = 1 To UB2: vnt2(i) = vnt2C1(i, 1) & vnt2C2(i, 1): Next i
' Erase the two Sheet2 Column Arrays.
Erase vnt2C1: Erase vnt2C2
' Resize Target Array to rows defined by the number of elements
' in Sheet1 Array.
ReDim vntT(1 To UB1, 1 To 1) ' 2D 1-based 1-column
' Loop through elements of Sheet1 Array.
For i = 1 To UB1
If IsNumeric(vnt1(i)) Then
' Loop through Reduce Values.
For k = 0 To cReduce
' Calculate Current Sheet1 Array Value.
lng1 = vnt1(i) - k
' Loop through elements of Sheet2 Array.
For j = 1 To UB2
If IsNumeric(vnt2(j)) Then
' Calculate Current Sheet2 Array Value.
lng2 = vnt2(j)
' Compare current Sheet1 and Sheet2 Array Values.
If lng1 = lng2 Then
' Write value of current element (row) in Source
' Array to current element (row) in Target Array.
vntT(i, 1) = vntS(j, 1)
' Ensure exiting "For k"-loop immediately after
' exiting "For j"-loop.
k = cReduce
' Exit "For j"-loop.
Exit For
End If
End If
Next j
Next k
End If
Next i
' IN RANGES
' Write Target Array to Target Range.
rng = vntT
Exit Sub
LastRow:
LR = 0
Set rng = rng.Find("*", , xlFormulas, , , xlPrevious)
If Not rng Is Nothing Then LR = rng.Row
Return
End Sub

How to match up data from two spreadsheets using specific format

I am wondering if someone can help me figure out how to match up data from two sheets, in a specific format. Here is an example of the data I need matched up, including an example showing expected output.
Note that UniqueToGroup_IDs are unique to only the specific Group_ID listed. As you can see, both of the sample Group_IDs I listed contain a UniqueToGroup_ID value of XSTN, which will return two different result IDs; 2306765 for Group_ID 16453, and 8272773 for Group_ID 8156705.
I can (painfully) do this semi-manually, by a combination of Text To Columns, adding the Group_ID to the UniqueToGroup_ID and NotUniqueToGroup_ID, and VLOOKUP -- but it takes forever and I need to do this often.
I haven't tried to write any VBA yet, because I'm not sure how to approach this problem. I am not terribly experienced with coding.
See example here (Dropbox)
Thank you in advance, for any advice.
Crazy Lookup
Links
Workbook Download how-to-match-up-data-from-two-spreadsheets-using-specific-format_54299649.xls
The Code
Sub CrazyLookup()
Const cSheet1 As String = "Original Data" ' 1st Source Worksheet Name
Const cSheet2 As String = "Data To Match" ' 2nd Source Worksheet Name
Const cSheet3 As String = "Sample Result" ' Target Worksheet Name
Const cFirstR As Long = 2 ' First Row Number
Const cFirstC As Variant = "A" ' First Column Letter/Number
Const cLastC As Variant = "C" ' Source Worksheet's Last Column
Const cNoC As Long = 2 ' Number of Columns of Target Array/Range
Const cDel As String = "|" ' Split/Join Delimiter
Dim vnt1 As Variant ' 1st Source Array
Dim vnt2 As Variant ' 2nd Source Array
Dim vnt3 As Variant ' Target Array
Dim vntU As Variant ' Unique Array
Dim lastR1 As Long ' Last Row Number of 1st Source Range
Dim lastR2 As Long ' Last Row Number of 2nd Source Range
Dim i As Long ' 1st Source Array Row Counter
Dim j As Long ' Unique Array Row Counter
Dim k As Long ' 2nd Source Array Row Counter
Application.ScreenUpdating = False
On Error GoTo ProcedureExit
' Write 1st Source Range to 1st Source Array.
With ThisWorkbook.Worksheets(cSheet1)
lastR1 = .Columns(.Cells(1, cFirstC).Column) _
.Find("*", , -4123, , 2, 2).Row
vnt1 = .Range(.Cells(cFirstR, cFirstC), .Cells(lastR1, cLastC))
End With
' Write 2nd Source Range to 2nd Source Array.
With ThisWorkbook.Worksheets(cSheet2)
lastR2 = .Columns(.Cells(1, cFirstC).Column) _
.Find("*", , -4123, , 2, 2).Row
vnt2 = .Range(.Cells(cFirstR, cFirstC), .Cells(lastR2, cLastC))
End With
' Resize Target Array TO 1st Source Array's rows count and TO
' Number of Columns of Target Array.
ReDim vnt3(1 To UBound(vnt1), 1 To cNoC)
' Write First Source Array's First Column to Target Array's first column.
For i = 1 To UBound(vnt1)
vnt3(i, 1) = vnt1(i, 1)
Next
' Write
For i = 1 To UBound(vnt1) ' Loop through rows of 1st Source Array.
' Split 1st Source Array's row in 3rd column to Unique Array.
vntU = Split(vnt1(i, 3), cDel)
For j = 0 To UBound(vntU) ' Loop through rows of Unique Array.
For k = 1 To UBound(vnt2) ' Loop through rows of 2nd Source Array.
' Match 1st Source Array's row in 2nd column TO 2nd Source
' Array's row in first column AND Unique Array's row TO
' 2nd Source Array's row in 2nd column.
If vnt1(i, 2) = vnt2(k, 1) And vntU(j) = vnt2(k, 2) Then
' Write from 2nd Source Array's row in 3rd column to
' Unique Array's row.
vntU(j) = vnt2(k, 3)
Exit For ' Stop searching.
End If
Next
' Check if match was not found.
If k > UBound(vnt2) Then vntU(j) = "NotFound"
Next
' Join Unique Array's rows to Target Array's row in second column.
vnt3(i, 2) = Join(vntU, cDel)
Next
With ThisWorkbook.Worksheets(cSheet3)
' Clear contents of Target Range columns (excl. Headers).
.Range(.Cells(cFirstR, cFirstC), .Cells(.Rows.Count, _
.Cells(1, cFirstC).Column + cNoC - 1)).ClearContents
' Copy Target Array to Target Range.
.Cells(cFirstR, cFirstC).Resize(UBound(vnt3), UBound(vnt3, 2)) = vnt3
End With
ProcedureExit:
Application.ScreenUpdating = True
End Sub
You can build a two column cross reference with a dictionary.
Option Explicit
Sub ertgyhj()
Dim i As Long, ii As String, gi As Long, ugi As String, nuid As Long, r As String
Dim a As Long, itm As String, tmp As String, arr As Variant, xref As Object, results As Object
Dim ws1 As Worksheet, ws2 As Worksheet, ws3 As Worksheet
Set ws1 = Worksheets("original data")
Set ws2 = Worksheets("data to match")
Set ws3 = Worksheets("sample result")
Set xref = CreateObject("scripting.dictionary")
Set results = CreateObject("scripting.dictionary")
'build two column cross reference dictionary
With ws2
For i = 2 To .Cells(.Rows.Count, "A").End(xlUp).Row
itm = Join(Array(.Cells(i, "A").Value2, .Cells(i, "B").Value2), Chr(124))
xref.Item(itm) = .Cells(i, "C").Value2
Next i
End With
'put column header labels into results
results.Item("image_id") = "result"
'collect results
With ws1
'loop through rows
For i = 2 To .Cells(.Rows.Count, "A").End(xlUp).Row
ii = .Cells(i, "A").Value2
gi = .Cells(i, "B").Value2
ugi = .Cells(i, "C").Value2
tmp = vbNullString
arr = Split(ugi, Chr(124))
'loop through UniqueToGroup_ID and find matches
For a = LBound(arr) To UBound(arr)
itm = Join(Array(gi, arr(a)), Chr(124))
If xref.exists(itm) Then
tmp = IIf(CBool(Len(tmp)), tmp & Chr(124), vbNullString) & xref.Item(itm)
End If
Next a
'store concatenated result with image id
results.Item(ii) = tmp
Next i
End With
'post results
With ws3
.Cells(1, "A").Resize(results.Count, 1) = Application.Transpose(results.keys)
.Cells(1, "B").Resize(results.Count, 1) = Application.Transpose(results.items)
End With
End Sub
I built a workbook that I think can solve your problem. Let me know if this helps!
https://www.dropbox.com/s/3h6mja0xtwucbr5/20180121-Matching.xlsm?dl=0

Rearranging cells Excel VBA

I'm trying to rearrange a large data set and am thinking VBA is the best, most effective method to do this.
I have a data set similar to this structure:
and with this data, I'm trying to get this output:
Has anyone written anything to do this sort of thing? I'd be most grateful for any suggestions or advise on where to go with this.
Many thanks,
Transpose Data (Rearrange)
Adjust the values in the constants section to fit your needs.
Links
Workbook Download (Dropbox)
Images
Source (Sheet1)
Target 1 (Sheet2)
Target 2 (Sheet3)
ID is not gonna happen because, like Ted in the previous version, it is nowhere to be found.
Version 1
Sub TransposeData1()
' Source
Const cSource As String = "Sheet1" ' Worksheet Name
Const cFR As Long = 2 ' First Row Number
Const cFRC As Variant = "A" ' First-Row Column Letter/Number
Const cRep As String = "B" ' Repeat Columns Range Address
Const cUni As String = "C:G" ' Unique Columns Range Address
' Target
Const cTarget As String = "Sheet2" ' Worksheet Name
Const cHeaders As String = "IDDiff,Supervisor,Primary,Secondary"
Const cSupervisor As String = "Ted" ' Supervisor
Const cFCell As String = "A1" ' First Cell Range Address
' Source
Dim rng As Range ' First-Row Column Last Used Cell Range
Dim vntR As Variant ' Repeat Array
Dim vntU As Variant ' Unique Array
Dim NoR As Long ' Number of Records
' Target
Dim vntH As Variant ' Header Array
Dim vntT As Variant ' Target Array
Dim CUR As Long ' Current Column
Dim i As Long ' Target Array Row Counter
Dim j As Long ' Target/Repeat Array Column Counter
Dim k As Long ' Repeat/Unique Array Row Counter
Dim m As Long ' Unique Array Column Counter
' Speed up.
With Application
.ScreenUpdating = False
.Calculation = xlCalculationManual
End With
On Error GoTo ProcedureExit
' In Source Worksheet
With ThisWorkbook.Worksheets(cSource).Columns(cFRC)
' In First-Row Column
With .Columns(cFRC)
' Calculate First-Row Column Last Used Cell Range.
Set rng = .Find("*", , xlFormulas, xlWhole, xlByColumns, xlPrevious)
' Check if no data in First-Row Column.
If rng Is Nothing Then
MsgBox "No data in column '" _
& Split(.Cells(1).Address, "$")(1) & "'."
GoTo ProcedureExit
End If
' Calculate Number of Records needed to calculate Repeat Range
' and Unique Range.
NoR = rng.Row - cFR + 1
End With
' In Repeat Columns
With .Columns(cRep)
' Copy calculated Repeat Range to Repeat Array.
vntR = .Cells(1).Offset(cFR - 1).Resize(NoR, .Columns.Count)
End With
' In Unique Columns
With .Columns(cUni)
' Copy calculated Unique Range to Unique Array.
vntU = .Cells(1).Offset(cFR - 1).Resize(NoR, .Columns.Count)
End With
End With
' In Arrays
' Resize Target Array:
' Rows
' 1 - for Headers.
' NoR * Ubound(vntU, 2) - for data.
' Columns
' 1 - for IDs.
' 1 - for Supervisor.
' UBound(vntR, 2) - for Repeat Array Columns.
' 1 - for unique values.
ReDim vntT(1 To 1 + NoR * UBound(vntU, 2), _
1 To 1 + 1 + UBound(vntR, 2) + 1)
' Headers to Header Array
vntH = Split(cHeaders, ",")
' Header Array to Target Array
For j = 1 To UBound(vntT, 2)
vntT(1, j) = Trim(vntH(j - 1))
Next
' IDs to Target Array
CUR = CUR + 1 ' Calculate Current Column in Target Array.
For i = 2 To UBound(vntT)
vntT(i, CUR) = i - 1
Next
' Supervisor to Target Array
CUR = CUR + 1 ' Calculate Current Column in Target Array.
For i = 2 To UBound(vntT)
vntT(i, CUR) = cSupervisor
Next
' Repeat Array to Target Array
CUR = CUR + 1 ' Calculate Current Column in Target Array.
i = 1 ' First row of Target Array contains Headers.
' Task: Write values of current rows (k) in columns (j) in Repeat Array
' to current rows (i) in columns (j + CUR - 1) of Target Array as many
' times as there are columns (m) in Unique Array.
For k = 1 To UBound(vntR) ' Rows of Repeat Array
For m = 1 To UBound(vntU, 2) ' Columns of Unique Array
i = i + 1 ' Count current row of Target Array.
For j = 1 To UBound(vntR, 2) ' Columns of Repeat Array
' Write value of current record in Repeat Array
' to current record of Target Array.
vntT(i, j + CUR - 1) = vntR(k, j)
Next
Next
Next
' Unique Array to Target Array
CUR = CUR + UBound(vntR, 2) ' Calculate Current Column in Target Array.
i = 1 ' First row of Target Array contains Headers.
' Task: Write values of current row (k) and current column (m) of Unique
' Array each to the next row (i) in current column (CUR) of Target Array.
For k = 1 To UBound(vntU) ' Rows of Unique Array
For m = 1 To UBound(vntU, 2) ' Columns of Unique Array
i = i + 1 ' Count current row of Target Array.
' Write value of current record in Unique Array
' to current record of Target Array.
vntT(i, CUR) = vntU(k, m)
Next
Next
' In Target Worksheet
With ThisWorkbook.Worksheets(cTarget).Range(cFCell)
' Clear contents of Target Range and the range below it.
.Resize(.Parent.Rows.Count - .Parent.Range(cFCell).Row + 1, _
UBound(vntT, 2)).ClearContents
' Copy Target Array to Target Range.
.Resize(UBound(vntT), UBound(vntT, 2)) = vntT
End With
ProcedureExit:
' Speed down.
With Application
.Calculation = xlCalculationAutomatic
.ScreenUpdating = True
End With
End Sub
Version 2
Sub TransposeData2()
' Source
Const cSource As String = "Sheet1" ' Worksheet Name
Const cFR As Long = 2 ' First Row Number
Const cFRC As Variant = "A" ' First-Row Column Letter/Number
Const cRep As String = "A:B" ' Repeat Columns Range Address
Const cUni As String = "C:G" ' Unique Columns Range Address
Const cUH As Long = 1 ' Unique Header Row Number
' Target
Const cTarget As String = "Sheet3" ' Worksheet Name
Const cHeaders As String = "ID,Primary,Secondary,Relationship"
Const cFCell As String = "A1" ' First Cell Range Address
' Source
Dim rng As Range ' First-Row Column Last Used Cell Range
Dim vntR As Variant ' Repeat Array
Dim vntU As Variant ' Unique Array
Dim NoR As Long ' Number of Records
' Target
Dim vntH As Variant ' Header Array
Dim vntT As Variant ' Target Array
Dim vntUH As Variant ' Unique Header Array
Dim CUR As Long ' Current Column
Dim i As Long ' Target Array Row Counter
Dim j As Long ' Target/Repeat Array Column Counter
Dim k As Long ' Repeat/Unique Array Row Counter
Dim m As Long ' Unique/Unique Header Array Column Counter
' Speed up.
With Application
.ScreenUpdating = False
.Calculation = xlCalculationManual
End With
On Error GoTo ProcedureExit
' In Source Worksheet
With ThisWorkbook.Worksheets(cSource).Columns(cFRC)
' In First-Row Column
With .Columns(cFRC)
' Calculate First-Row Column Last Used Cell Range.
Set rng = .Find("*", , xlFormulas, xlWhole, xlByColumns, xlPrevious)
' Check if no data in First-Row Column.
If rng Is Nothing Then
MsgBox "No data in column '" _
& Split(.Cells(1).Address, "$")(1) & "'."
GoTo ProcedureExit
End If
' Calculate Number of Records needed to calculate Repeat Range
' and Unique Range.
NoR = rng.Row - cFR + 1
End With
' In Repeat Columns
With .Columns(cRep)
' Copy calculated Repeat Range to Repeat Array.
vntR = .Cells(1).Offset(cFR - 1).Resize(NoR, .Columns.Count)
End With
' In Unique Columns
With .Columns(cUni)
' Copy calculated Unique Range to Unique Array.
vntU = .Cells(1).Offset(cFR - 1).Resize(NoR, .Columns.Count)
' Copy calculated Unique Header Range to Unique Header Array.
vntUH = .Cells(1).Offset(cUH - 1).Resize(, .Columns.Count)
End With
End With
' In Arrays
' Resize Target Array:
' Rows
' 1 - for Headers.
' NoR * Ubound(vntU, 2) - for data.
' Columns
' UBound(vntR, 2) - for Repeat Array Columns.
' 1 - for unique values.
' 1 - for Unique Header Row.
ReDim vntT(1 To 1 + NoR * UBound(vntU, 2), _
1 To UBound(vntR, 2) + 1 + 1)
' Write Headers to Header Array.
vntH = Split(cHeaders, ",")
' Write Headers to Target Array.
For j = 1 To UBound(vntT, 2)
vntT(1, j) = Trim(vntH(j - 1))
Next
' Repeat Array to Target Array
CUR = CUR + 1 ' Calculate Current Column in Target Array.
i = 1 ' First row of Target Array contains Headers.
' Task: Write values of current rows (k) in columns (j) in Repeat Array
' to current rows (i) in columns (j + CUR - 1) of Target Array as many
' times as there are columns (m) in Unique Array.
For k = 1 To UBound(vntR) ' Rows of Repeat Array
For m = 1 To UBound(vntU, 2) ' Columns of Unique Array
i = i + 1 ' Count current row of Target Array.
For j = 1 To UBound(vntR, 2) ' Columns of Repeat Array
' Write value of current record in Repeat Array
' to current record of Target Array.
vntT(i, j + CUR - 1) = vntR(k, j)
Next
Next
Next
' Unique Array to Target Array
CUR = CUR + UBound(vntR, 2) ' Calculate Current Column in Target Array.
i = 1 ' First row of Target Array contains Headers.
' Task: Write values of current row (k) and current column (m) of Unique
' Array each to the next row (i) in current column (CUR) of Target Array.
For k = 1 To UBound(vntU) ' Rows of Unique Array
For m = 1 To UBound(vntU, 2) ' Columns of Unique Array
i = i + 1 ' Count current row of Target Array.
' Write value of current record in Unique Array
' to current record of Target Array.
vntT(i, CUR) = vntU(k, m)
Next
Next
' Unique Header Array to Target Array
CUR = CUR + 1 ' Calculate Current Column in Target Array.
i = 1 ' First row of Target Array contains Headers.
' Task: Write values of current column (m) of Unique Header Array each
' to the next row (i) in current column (CUR) of Target Array as many
' times as there are rows(k) in Unique Array.
For k = 1 To UBound(vntU) ' Rows of Unique Array
For m = 1 To UBound(vntUH, 2) ' Columns of Unique Header Array
i = i + 1 ' Count current row of Target Array.
' Write value of current record in Unique Array
' to current record of Target Array.
vntT(i, CUR) = vntUH(1, m)
Next
Next
' In Target Worksheet
With ThisWorkbook.Worksheets(cTarget).Range(cFCell)
' Clear contents of Target Range and the range below it.
.Resize(.Parent.Rows.Count - .Parent.Range(cFCell).Row + 1, _
UBound(vntT, 2)).ClearContents
' Copy Target Array to Target Range.
.Resize(UBound(vntT), UBound(vntT, 2)) = vntT
End With
ProcedureExit:
' Speed down.
With Application
.Calculation = xlCalculationAutomatic
.ScreenUpdating = True
End With
End Sub
You could just loop through the names, and output them in a column.
Something like the following maybe:
Option Explicit
Sub sort()
Dim rArea As Range, lRow As Long, oCN As Long, outCol As String, cell As Range
'Set this to the range of names
Set rArea = ActiveSheet.Range("C2:G4")
'Set this to output
outCol = "J"
oCN = Columns(outCol).Column
For Each cell In rArea
lRow = ActiveSheet.Range(outCol & ActiveSheet.Rows.Count).End(xlUp).Row 'Update last row in output column
Cells(lRow + 1, oCN).Value = cell.Value 'Print Name
Cells(lRow + 1, oCN - 1).Value = Cells(cell.Row, 2).Value 'Print Company
Next cell
End Sub
I made some last minute changes for dynamics. But compare with the picture, and you should be able to figure out what I'm doing.
I don't see the point to adding the other rows with a macro, but you can do that as well obviously.

Resources