Looping a single column to find a match in another worksheet - excel

I have two worksheets (Master & Sample).
I want to update the data in Master sheet if I have a value match, unique value in column A of Master sheet with a value in column A in Sample sheet else insert a new row at the end in Master sheet.
What is the logic for this?
I have added sample data for my master and sample in the images.
Example data for Master sheet:
Example data for Sample sheet and comments:

Update Master Worksheet
Adjust the five constants (Const) to fit your needs.
The Code
Sub UpdateMaster()
Const cMaster As String = "Master" ' Master Worksheet Name
Const cSample As String = "Sample" ' Sample Worksheet Name
Const cCols As String = "A:D" ' Data Columns Range Address (or "1:4")
Const cLRC As Variant = "A" ' Last-Row Column Letter/Number (or 1)
Const cFR As Long = 2 ' First Row Number
Dim rng As Range ' Last Used Cell in Last-Row Column of both
' Worksheets, Sample/Master/Unique Range
Dim vntM As Variant ' Master Array
Dim vntS As Variant ' Sample Array
Dim vntR As Variant ' Row Array
Dim vntU As Variant ' Unique Array
Dim MNoR As Long ' Master Number of Rows
Dim SNoR As Long ' Sample Number of Rows
Dim Cols As Long ' Number of Columns in Data Columns Range
Dim i As Long ' Sample/Unique Array Row Counter
Dim j As Long ' Sample/Master/Unique Array Column Counter
Dim k As Long ' Master Array Row Counter
Dim m As Long ' Row Array Row Count(er)
' Speed up.
With Application
.ScreenUpdating = False
.Calculation = xlCalculationManual
End With
' Handle errors.
On Error GoTo ErrorHandler
' In (Last-Row Column of) Sample Worksheet
With ThisWorkbook.Worksheets(cSample).Columns(cLRC)
' Create a reference to Last Used Cell.
Set rng = .Find("*", , xlFormulas, , xlByColumns, xlPrevious)
' Check if no data in column.
If rng Is Nothing Then
MsgBox "No data in column '" & Split(.Cells(1).Address, "$")(1) _
& "'.", vbCritical, "Column Empty"
GoTo ProcedureExit
End If
' Calculate Sample Number of Rows.
SNoR = rng.Row - cFR + 1
' Create a reference to Sample Range.
Set rng = .Parent.Columns(cCols).Rows(cFR).Resize(SNoR)
' Copy Sample Range to Sample Array.
vntS = rng
End With
' In (Last-Row Column of) Master Worksheet
With ThisWorkbook.Worksheets(cMaster).Columns(cLRC)
' Create a reference to Last Used Cell.
Set rng = .Find("*", , xlFormulas, , xlByColumns, xlPrevious)
' Check if no data in column.
If rng Is Nothing Then
MsgBox "No data in column '" & Split(.Cells(1).Address, "$")(1) _
& "'.", vbCritical, "Column Empty"
GoTo ProcedureExit
End If
' Calculate Master Number of Rows.
MNoR = rng.Row - cFR + 1
' In Data Columns Range
With .Parent.Columns(cCols)
' Calculate Number of Columns in Data Columns Range.
Cols = .Columns.Count
' Create a reference to Master Range.
Set rng = .Rows(cFR).Resize(MNoR)
' Copy Master Range to Master Array.
vntM = rng
End With
End With
' Resize Row Array to Sample Number of Rows (as big as it could get).
ReDim vntR(1 To SNoR)
' Loop through rows of Sample Array.
For i = 1 To SNoR
' Loop through rows of Master Array.
For k = 1 To MNoR
' Check if value of element at current row in 1st column of Source
' Array is equal to the value of element at current row in 1st
' column of Master Array.
If vntS(i, 1) = vntM(k, 1) Then ' Match FOUND.
' Loop through the rest of the columns (to update the values).
For j = 2 To Cols
' Write value of element at current row in current column
' of Source Array to element at current row in current
' column of Master Array.
vntM(k, j) = vntS(i, j)
Next
' Stop looping through rows of Master Array (unique values).
Exit For
End If
Next
' Check if no match was found using the 'For Next Trick' i.e. when the
' for next loop finishes uninterupted, the value of the 'counter' is
' by 1 greater than the 'end' (VBA Help: "For counter = start To end").
If k = MNoR + 1 Then
' Count the number of rows in Row Array.
m = m + 1
' Write the current row number of Sample Array to Row Array.
vntR(m) = i
End If
Next
' Check if new values found.
If m > 0 Then
' Resize Row Array to number of new values found.
ReDim Preserve vntR(1 To m)
' Resize Unique Array to number of rows of Row Array and to Cols
' number of columns.
ReDim vntU(1 To m, 1 To Cols)
' Loop through rows of Row/Unique Array
For i = 1 To m
' Loop through columns of Sample/Unique Array.
For j = 1 To Cols
' Write the rows (containded in Row Array) of Sample Array to
' Unique Array.
vntU(i, j) = vntS(vntR(i), j)
Next
Next
End If
' Erase Row & Sample Arrays. All needed data is in Master & Unique Arrays.
Erase vntR
Erase vntS
' Copy Master Array to Master Range.
rng = vntM
' Erase Master Array.
Erase vntM
' Check if new values found.
If m > 0 Then
' Create a reference to Unique Range.
Set rng = rng.Cells(rng.Rows.Count, 1).Offset(1).Resize(m, Cols)
' Copy Unique Array to Unique Range.
rng = vntU
End If
MsgBox "The operation finished successfully.", vbInformation, "Success"
ProcedureExit:
' Speed down.
With Application
.Calculation = xlCalculationAutomatic
.ScreenUpdating = True
End With
Exit Sub
ErrorHandler:
MsgBox "An unexpected error occurred. Error '" & Err.Number & "': " _
& Err.Description, vbCritical, "Error"
GoTo ProcedureExit
End Sub

Related

Excel VBA search id and import data from other sheet

I'm working on a project with lots of data in two different sheets which is want to combine.
For example:
My Sheet1 should contain 4 columns. Columns 1 and 2 are already filled with ID's and a status.
In Sheet2 I have 3 columns. The first contains the ID's again, the second a serial-number and the third a Yes/No.
The two sheets have around 5500 rows in it. The first a little more then the second.
I would like to run a loop which picks the first ID in Sheet1, checks if it exists in Sheet2, and if it does, it should copy the two missing columns (serial-number and Yes/No) into into Sheet1.
Then the to the next Id in Sheet1 and do the same again.
I tried it with the code below, but I'm not getting it to work.
Hope you can help me out!
Dim i As Long
Dim Found As Range
For i = 1 To Rows.Count
Worksheets("Sheet1").Activate
If Cells(i, 1).Value <> "" Then
Set Found = Worksheets("Sheet2").Range("A2", Range("A")).Find(i, 1)
If Not Found Is Nothing Then
Worksheets("Sheet1").Range(i, 3).Value = Cells(Found.Row, 2).Value
Worksheets("Sheet1").Range(i, 4).Value = Cells(Found.Row, 3).Value
End If
End If
Next i
You could try with two nested for each loops.
Sub copySerial()
Dim range1 As Range, range2 As Range
Set range1 = Worksheets("Sheet1").Range("A2:A" & Cells(Rows.Count, 1).End(xlUp).Row)
Set range2 = Worksheets("Sheet2").Range("A2:A" & Cells(Rows.Count, 1).End(xlUp).Row)
For Each c1 In range1
For Each c2 In range2
If c1.Value = c2.Value Then
c1.Offset(0, 2).Value = c2.Offset(0, 1).Value
c1.Offset(0, 3).Value = c2.Offset(0, 2).Value
End If
Next c2
Next c1
End Sub
Arrays Before Ranges
Adjust the values in the constants section to fit your needs. Do it
carefully (slowly) because there are many.
First I created the second code which appeared to be super slow.
After implementing arrays, it got 30 times faster at 5000 records. I guess the extra work pays off.
Option Explicit
Sub UpdateSheetArray() ' Calculates for about 3s at 5000 records - Acceptable!
Const strSrc As String = "Sheet2" ' Source Worksheet Name
Const frSrc As Long = 2 ' Source First Row Number
Const colSrc As Long = 1 ' Source Compare Column Number
Const colSrc1 As Long = 2 ' Source Data Column 1
Const colSrc2 As Long = 3 ' Source Data Column 2
Const strTgt As String = "Sheet1" ' Target Worksheet Name
Const frTgt As Long = 1 ' Target First Row Number
Const colTgt As Long = 1 ' Target Compare Column Number
Const colTgt1 As Long = 3 ' Target Data Column 1
Const colTgt2 As Long = 4 ' Target Data Column 2
Dim wsSrc As Worksheet ' Source Worksheet
Dim wsTgt As Worksheet ' Target Worksheet
Dim vntSrc As Variant ' Source Compare Array
Dim vntSrc1 As Variant ' Source Data Array 1
Dim vntSrc2 As Variant ' Source Data Array 2
Dim vntTgt As Variant ' Target Compare Array
Dim vntTgt1 As Variant ' Target Data Array 1
Dim vntTgt2 As Variant ' Target Data Array 2
Dim rngSrc As Range ' Source Compare Range,
' Source Data Range 1,
' Source Data Range 2
Dim rngTgt As Range ' Target Compare Range,
' Target Data Range 1,
' Target Data Range 2
Dim lrSrc As Long ' Source Last Non-Empty Row Number
Dim lrTgt As Long ' Target Last Non-Empty Row Number
Dim varCur As Variant ' Current Target Cell Value
Dim i As Long ' Source Row Counter
Dim j As Long ' Target Row Counter
' Define Source and Target Worksheets.
Set wsSrc = Worksheets(strSrc)
Set wsTgt = Worksheets(strTgt)
' Calculate Last Non-Empty Row in Source Worksheet.
lrSrc = wsSrc.Columns(colSrc).Find(What:="*", LookIn:=xlFormulas, _
SearchDirection:=xlPrevious).Row
' Calculate Last Non-Empty Row in Target Worksheet.
lrTgt = wsTgt.Columns(colTgt).Find(What:="*", LookIn:=xlFormulas, _
SearchDirection:=xlPrevious).Row
' Define Source Compare Range and write its values to Source Compare Array.
Set rngSrc = wsSrc.Cells(frSrc, colSrc).Resize(lrSrc - frSrc + 1)
vntSrc = rngSrc
' Define Source Data Range 1 and write its values to Source Data Array 1.
Set rngSrc = rngSrc.Offset(, colSrc1 - colSrc): vntSrc1 = rngSrc
' Define Source Data Range 2 and write its values to Source Data Array 2.
Set rngSrc = rngSrc.Offset(, colSrc2 - colSrc1): vntSrc2 = rngSrc
' Define Target Compare Range and write its values to Target Compare Array.
Set rngTgt = wsTgt.Cells(frTgt, colTgt).Resize(lrTgt - frTgt + 1)
vntTgt = rngTgt
' Define Target Data Arrays (same size as Target Compare Array).
ReDim vntTgt1(1 To UBound(vntTgt), 1 To 1)
ReDim vntTgt2(1 To UBound(vntTgt), 1 To 1)
' Note: These last two arrays are going to be written to,
' while the previous four are going to be read from.
' All arrays are 2-dimensional 1-based 1-column arrays.
' Loop through elements of Target Compare Array.
For i = 1 To UBound(vntTgt)
' Write value of current element in Target Array
' to Current Target Cell Value.
varCur = vntTgt(i, 1)
' Check if Current Target Cell Value is not "".
If varCur <> "" Then
' Loop through elements of Source Compare Array.
For j = 1 To UBound(vntSrc)
' Check if value of current element in Source Array is equal
' to Current Target Cell Value.
If vntSrc(j, 1) = varCur Then
' Write current elements in Source Data Arrays
' to Target Data Arrays.
vntTgt1(i, 1) = vntSrc1(j, 1): vntTgt2(i, 1) = vntSrc2(j, 1)
' No need to loop anymore after found.
Exit For
End If
Next
End If
Next
' Define Target Data Range 1.
Set rngTgt = rngTgt.Offset(, colTgt1 - colTgt)
' Write values of Target Data Array 1 to Target Data Range 1.
rngTgt = vntTgt1
' Define Target Data Range 2.
Set rngTgt = rngTgt.Offset(, colTgt2 - colTgt1)
' Write values of Target Data Array 2 to Target Data Range 2.
rngTgt = vntTgt2
End Sub
Sub UpdateSheetRange() ' Calculates for about 90s at 5000 records - too slow!
Const strSrc As String = "Sheet2" ' Source Worksheet Name
Const frSrc As Long = 2 ' Source First Row Number
Const colSrc As Long = 1 ' Source Compare Column Number
Const colSrc1 As Long = 2 ' Source Data Column 1
Const colSrc2 As Long = 3 ' Source Data Column 2
Const strTgt As String = "Sheet1" ' Target Worksheet Name
Const frTgt As Long = 1 ' Target First Row Number
Const colTgt As Long = 1 ' Target Compare Column Number
Const colTgt1 As Long = 3 ' Target Data Column 1
Const colTgt2 As Long = 4 ' Target Data Column 2
Dim wsSrc As Worksheet ' Source Worksheet
Dim wsTgt As Worksheet ' Target Worksheet
Dim lrSrc As Long ' Source Last Non-Empty Row Number
Dim lrTgt As Long ' Target Last Non-Empty Row Number
Dim varCur As Variant ' Current Target Cell Value
Dim i As Long ' Source Row Counter
Dim j As Long ' Target Row Counter
' Define Worksheet.
Set wsSrc = Worksheets(strSrc)
Set wsTgt = Worksheets(strTgt)
' Calculate Last Non-Empty Row in Source Worksheet.
lrSrc = wsSrc.Columns(colSrc).Find(What:="*", LookIn:=xlFormulas, _
SearchDirection:=xlPrevious).Row
' Calculate Last Non-Empty Row in Target Worksheet.
lrTgt = wsTgt.Columns(colTgt).Find(What:="*", LookIn:=xlFormulas, _
SearchDirection:=xlPrevious).Row
With Application
.ScreenUpdating = False
.Calculation = xlCalculationManual
.EnableEvents = False
End With
On Error GoTo ProgramError
For i = frTgt To lrTgt
varCur = wsTgt.Cells(i, colTgt).Value
If varCur <> "" Then
For j = frSrc To lrSrc
If wsSrc.Cells(j, colSrc).Value = varCur Then
wsTgt.Cells(i, colTgt1) = wsSrc.Cells(j, colSrc1).Value
wsTgt.Cells(i, colTgt2) = wsSrc.Cells(j, colSrc2).Value
Exit For
End If
Next
End If
Next
SafeExit:
With Application
.EnableEvents = True
.Calculation = xlCalculationAutomatic
.ScreenUpdating = True
End With
Exit Sub
ProgramError:
MsgBox "An unexpected error occurred."
On Error GoTo 0
GoTo SafeExit
End Sub

Search first sheet coumn name in another sheet coumn and insert that column data

I have 2 workbook i.e workbook A and Workbook B
A workbook having column in order A,B,C,D and B workbook having Column D,C,B,A.
I have to insert A workbook data into B workbook in proper column that is the columns inserted into proper column A in A ,B in B, C in C, D in D
I tried below code
Sub DEMO()
For i = 1 To 4
For j = 2 To 4
For k = 2 To 4
If Sheets(1).Cells(i, j).Value = Sheets(2).Cells(i, j).Value Then
Sheets(2).Cells(k, j).Value = Sheets(1).Cells(j, i).Value
End If
Next k
'MsgBox Sheets(1).Cells(2, 1).Value
'MsgBox Sheets(2).Cells(2, 1).Value
Next j
Next i
End Sub
Please help on this
To match the column names of …
Worksheet A
with the column names in …
Worksheet B
Use a loop and the WorksheetFunction.Match method
Option Explicit
Sub MatchColumns()
Dim wsA As Worksheet 'define worksheet A
Set wsA = ThisWorkbook.Worksheets("A")
Dim ColsRangeA As Range 'get column names in A
Set ColsRangeA = wsA.Range("A1", wsA.Cells(1, wsA.Columns.Count).End(xlToLeft))
Dim wsB As Worksheet 'define worksheet B
Set wsB = ThisWorkbook.Worksheets("B")
Dim ColsRangeB As Range 'get column names in B
Set ColsRangeB = wsB.Range("A1", wsB.Cells(1, wsB.Columns.Count).End(xlToLeft))
Dim MatchedColNo As Long
Dim Col As Range
For Each Col In ColsRangeA 'loop throug column names in A
MatchedColNo = 0 'initialize
On Error Resume Next 'test if column name can be found in worksheet B column names
MatchedColNo = Application.WorksheetFunction.Match(Col.Value, ColsRangeB, False)
On Error GoTo 0
If MatchedColNo <> 0 Then 'if name was found
wsB.Cells(2, MatchedColNo).Value = "Matches wsA col " & Col.Column
Else 'if name didn't match
MsgBox "no maching column found for " & Col.Value
End If
Next Col
End Sub
Copy Below Headers
The Code
'*******************************************************************************
'Purpose: Copies the values below headers from one worksheet
' to another containing the same headers.
'*******************************************************************************
Sub CopyBelowHeaders()
' !!! Header List !!! Change this to any comma separated string containing
' the values of the headers e.g. "ID, Product,Count, Price,Stock ".
Const cHeaders As String = "A,B,C,D"
Const cSource As String = "Sheet1" ' Source Worksheet Name
Const cTarget As String = "Sheet2" ' Target Worksheet Name
Const cFirstR As Long = 2 ' First Row Number
Dim rngS As Range ' Current Source Header Cell Range,
' Current Source Column Last Used Cell Range,
' Current Source Column Range
Dim rngT As Range ' Current Target Header Cell Range,
' Current Target Column Range
Dim vntH As Variant ' Header Array
Dim vntS As Variant ' Source Header Column Array
Dim vntT As Variant ' Target Header Column Array
Dim i As Long ' Header Arrays Element Counter
vntH = Split(cHeaders, ",") ' Write Header List to Header Array.
ReDim vntS(UBound(vntH)) As Long ' Resize Source Header Column Array.
ReDim vntT(UBound(vntH)) As Long ' Resize Target Header Column Array.
' Column Numbers to Column Arrays
' In Source Worksheet
With ThisWorkbook.Worksheets(cSource)
' Loop through elements of Header Array.
For i = 0 To UBound(vntH)
' In Source Row Range (Header Row, 1st Row)
With .Rows(1)
' Find current element (string) of Header Array
' in Source Row Range.
Set rngS = .Find(Trim(vntH(i)), .Cells(.Cells.Count), _
xlValues, xlWhole, xlByRows, xlNext)
' When current element was found, write column number to
' Source Header Columns Array.
If Not rngS Is Nothing Then vntS(i) = rngS.Column
End With
Next
End With
' In Target Worksheet
With ThisWorkbook.Worksheets(cTarget)
' Loop through elements of Header Array.
For i = 0 To UBound(vntH)
' In Target Row Range (Header Row, 1st Row)
With .Rows(1)
' Find current element (string) of Header Array
' in Target Row Range.
Set rngT = .Find(Trim(vntH(i)), .Cells(.Cells.Count), _
xlValues, xlWhole, xlByRows, xlNext)
' When current element was found, write column number to
' Source Header Columns Array.
If Not rngS Is Nothing Then vntT(i) = rngT.Column
End With
Next
End With
' Source Worksheet to Target Worksheet
' In Source Worksheet
With ThisWorkbook.Worksheets(cSource)
' Loop through elements of Source Array.
For i = 0 To UBound(vntS)
' When current element of Source Header Column Array and current
' element of Target Header Column Array are different than "".
If vntS(i) > 0 And vntT(i) > 0 Then
' Find Last Used Cell Range in current Source Column Range.
Set rngS = .Columns(vntS(i)).Find("*", , xlFormulas, _
xlWhole, xlByColumns, xlPrevious)
' When current Source Column is not empty.
If Not rngS Is Nothing Then
' When current Source Column contains data in at least
' one more row than the Source Header row.
If rngS.Row > 1 Then
' Calculate Source Column Range.
Set rngS = .Range(.Cells(cFirstR, vntS(i)), rngS)
' In First Cell of Target Column Range
With ThisWorkbook.Worksheets(cTarget) _
.Cells(cFirstR, vntT(i))
' Clear contents in Target Column Range from
' First Cell to bottom cell.
.Resize(Rows.Count - cFirstR + 1).ClearContents
' Resize Current Target Column Range to the size
' of Current Source Column Range.
Set rngT = .Resize(rngS.Rows.Count)
End With
' Copy values from Current Source Column Range to
' Current Target Column Range.
rngT = rngS.Value
End If
End If
End If
Next
End With
End Sub

how to merge cells with same value in one row

How do I merge cells with the same value and color in a row?
and the result should be :
I think you could try this:
Option Explicit
Sub test()
Dim LastRow As Long, i As Long, Value As Long
Dim Color As Double
With ThisWorkbook.Worksheets("Sheet1")
LastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
For i = LastRow To 2 Step -1
Value = .Range("A" & i).Value
Color = .Range("A" & i).Interior.Color
If .Range("A" & i - 1).Value = Value And .Range("A" & i - 1).Interior.Color = Color Then
.Rows(i).EntireRow.Delete
End If
Next i
End With
End Sub
Copy Consecutive to One
Adjust the values in the constants section to fit your needs.
The image looks like you want all this to happen in the same column
of the same worksheet, which is adjusted in the constants section.
Before writing to Target Column (cTgtCol), the code will clear its
contents. Be careful not to lose data.
Colors are applied using a loop, which will slow down the fast array approach of copying the data.
The Code
Sub CopyConsecutiveToOne()
' Source
Const cSource As Variant = "Sheet1" ' Worksheet Name/Index
Const cSrcCol As Variant = "A" ' Column Letter/Number
Const cSrcFR As Long = 1 ' Column First Row Number
' Target
Const cTarget As Variant = "Sheet1" ' Worksheet Name/Index
Const cTgtCol As Variant = "A" ' Column Letter/Number
Const cTgtFR As Long = 1 ' Column First Row Number
Dim rng As Range ' Source Column Last Used Cell Range,
' Source Column Range, Target Column Range
Dim vntS As Variant ' Source Array
Dim vntT As Variant ' Target Array
Dim vntC As Variant ' Color Array
Dim i As Long ' Source Range/Array Row/Element Counter
Dim k As Long ' Target/Color Array Element Counter
With Application
.ScreenUpdating = False
.Calculation = xlCalculationManual
End With
'On Error GoTo ProcedureExit
' In Source Worksheet
With ThisWorkbook.Worksheets(cSource).Columns(cSrcCol)
' Calculate Source Column Last Used Cell Range.
Set rng = .Find("*", , xlFormulas, xlWhole, xlByColumns, xlPrevious)
' Check if data in Source Column.
If Not rng Is Nothing Then ' Data found.
' Calculate Source Range.
Set rng = .Range(.Cells(cSrcFR), .Cells(rng.Row))
' Copy values from Source Range to Source Array.
vntS = rng
Else ' Data Not Found.
With .Cells(1)
MsgBox "No Data in column '" & .Split(.Address, "$")(1) & "'."
GoTo ProcedureExit
End With
End If
End With
' In Arrays
' Count the number of elements in Target/Color Array.
k = 1 ' The first element will be included before the loop.
' Loop through elements of Source Array.
For i = 2 To UBound(vntS)
' Check if current value is different then the previous one.
If vntS(i, 1) <> vntS(i - 1, 1) Then
' Count element of Target/Color Array.
k = k + 1
End If
Next
' Write to Target/Color Arrays
' Resize Target/Color Arrays.
ReDim vntT(1 To k, 1 To 1)
ReDim vntC(1 To k, 1 To 1)
' Reset Counter
k = 1 ' The first element will be included before the loop.
' Write first value from Source Array to Target Array.
vntT(1, 1) = vntS(1, 1)
' Write first color value to Target Color Array.
vntC(1, 1) = rng.Cells(1, 1).Interior.Color
' Loop through elements of Source Array.
For i = 2 To UBound(vntS)
' Check if current value is different then the previous one.
If vntS(i, 1) <> vntS(i - 1, 1) Then
' Count element of Target/Color Array.
k = k + 1
' Write from Source Array to Target Array.
vntT(k, 1) = vntS(i, 1)
' Write color values from Source Range to Color Array.
vntC(k, 1) = rng.Cells(i, 1).Interior.Color
End If
Next
' All necessary data is in Target/Color Arrays.
Erase vntS
Set rng = Nothing
' In Target Worksheet
With ThisWorkbook.Worksheets(cTarget).Cells(cTgtFR, cTgtCol)
' Clear contents of range from Target First Cell to Target Bottom Cell.
.Resize(Rows.Count - .Row + 1).ClearContents
' Calculate Target Column Range.
Set rng = .Resize(k)
' Copy Target Array to Target Range.
rng = vntT
' Apply colors to Target Range.
With rng
' Loop through cells of Target Column Range.
For i = 1 To k
' Apply color to current cell of Target Range using the values
' from Color Array.
.Cells(i, 1).Interior.Color = vntC(i, 1)
Next
End With
End With
ProcedureExit:
With Application
.Calculation = xlCalculationAutomatic
.ScreenUpdating = True
End With
End Sub
Create a custom function in Visual Basic Editor that will return to the color index of the cell:
Function COLOR(Target As Range)
COLOR = Target.Interior.ColorIndex
End Function
Then in the right column use a formula similar to this:
=IF(OR(A2<>A3,COLOR(A2)<>COLOR(A3)),1,0)
Then filter to show only 1's.

Using loops to copy and paste

I have a large set of duplicate data, I want to be able to copy each unique value and paste it twice into a new worksheet so A1 and A2 will be the same for the first value. Then for the next unique value I want A3 and A4 to be the same and so on until the end of the column. How do I do this? I'm assuming it will be some sort of for or do loop.
So assume Column C is on a different sheet, but I want the data to be simplified like this
You can use a collection, then input to other sheet.
Sheet 2 column C has the original data.
Sub UsingCollection()
Dim cUnique As Collection
Dim Rng As Range, r As Long
Dim Cell As Range
Dim sh As Worksheet
Dim vNum As Variant
Set sh = ThisWorkbook.Sheets("Sheet1")
Set ws = Sheets("Sheet2")
Set Rng = ws.Range("C2:C" & ws.Cells(ws.Rows.Count, "C").End(xlUp).Row)
Set cUnique = New Collection
On Error Resume Next
For Each Cell In Rng.Cells
cUnique.Add Cell.Value, CStr(Cell.Value)
Next Cell
On Error GoTo 0
For Each vNum In cUnique
r = Cells(Rows.Count, "A").End(xlUp).Row + 1
Range(Cells(r, 1), Cells(r + 1, 1)).Value = vNum
Next vNum
End Sub
Multi Unique Values
Carefully adjust the variables in the constants section. The first 7 variables should be self-explanatory.
cBlnTargetFirstRow set to True enables the calculation of the first row on the Target Worksheet e.g. if you want to append the data to the data already in that column.
cBlnTargetNewWorksheet set to True enables the output of the result in a new worksheet, which is added to the end.
cIntBuffer is an increment of the size of the Unique Array i.e. each time the array is full, that amount is added to its size.
'*******************************************************************************
' Purpose: In a column, copies unique values, from each cell a specific
' number of times, to another column.
'*******************************************************************************
Sub MultiUniqueValues()
With Application
.ScreenUpdating = False
.Calculation = xlCalculationManual
.EnableEvents = False
End With
On Error GoTo UnexpectedErr
Const cVntSource As Variant = "Sheet1" ' Source Worksheet Name/Index
Const cVntTarget As Variant = "Sheet1" ' Target Worksheet Name/Index
Const cLngSourceFR As Long = 1 ' Source First Row
Const cLngTargetFR As Long = 1 ' Target First Row
Const cVntSourceC As Variant = "C" ' Source Column Letter/Number
Const cVntTargetC As Variant = "A" ' Target Column Letter/Number
Const cIntRepeat As Integer = 2 ' Unique Values Repeat Count
Const cBlnTargetFirstRow As Boolean = False ' Target First Row Calculation
Const cBlnTargetNewWorksheet As Boolean = False ' Target Worksheet Creation
Const intBuffer As Long = 10 ' Unique Array Resize Buffer
Dim vntSource As Variant ' Source Array
Dim vntUni As Variant ' Unique Array
Dim vntTarget As Variant ' Target Array
Dim lng1 As Long ' Source Array Counter
Dim lng2 As Long ' Unique Array Counter, Repeat Counter
Dim lng3 As Long ' Unique Values Count(er), Target Array Counter
' Paste column range into one-based 2-dimensional (1B2D) Source Array.
With ThisWorkbook.Worksheets(cVntSource)
vntSource = .Range(.Cells(cLngSourceFR, cVntSourceC), _
.Cells(.Rows.Count, cVntSourceC).End(xlUp))
End With
' Try to write first non-empty row from 1B2D Source to 1B1D Unique Array.
For lng1 = 1 To UBound(vntSource)
If Not IsEmpty(vntSource(lng1, 1)) Then
ReDim vntUni(1 To intBuffer)
vntUni(1) = vntSource(lng1, 1)
lng3 = 1
Exit For
End If
Next
If lng1 = UBound(vntSource) + 1 Then GoTo SourceArrayErr ' No non-empty.
' Write the rest of the non-empty rows from 1B2D Source to 1B1D Unique Array.
For lng1 = lng1 + 1 To UBound(vntSource)
For lng2 = 1 To lng3
' Check if current row of Source Array is empty and check it against
' all values in current Unique Array.
If IsEmpty(vntSource(lng1, 1)) Or _
vntUni(lng2) = vntSource(lng1, 1) Then Exit For ' Match found.
Next ' Match not found i.e. "'counter' = 'end' + 1".
If lng2 = lng3 + 1 Then
lng3 = lng2 ' (lng3 + 1)
' Resize 1B1D Unique Array if full.
If (lng3 - 1) Mod intBuffer = 0 Then
ReDim Preserve vntUni(1 To UBound(vntUni) + intBuffer)
End If
vntUni(lng3) = vntSource(lng1, 1) ' Write row to Unique Array.
Else
End If
Next
Erase vntSource
' Resize 1B1D Unique Array i.e. truncate last empty rows.
ReDim Preserve vntUni(1 To lng3)
' Copy 1B1D Unique Array to 1B2D Target Array.
ReDim vntTarget(1 To lng3 * cIntRepeat, 1 To 1)
lng3 = 0
For lng1 = 1 To UBound(vntUni)
For lng2 = 1 To cIntRepeat
lng3 = lng3 + 1
vntTarget(lng3, 1) = vntUni(lng1)
Next
Next
Erase vntUni
' Note: To shorten the following code, an Object reference could have
' been implemented. Didn't wanna do that.
' Paste 1B2D Target Array into Target Range.
If cBlnTargetNewWorksheet Then ' Paste into range of new worksheet.
With ThisWorkbook.Worksheets(cVntTarget)
.Parent.Sheets.Add After:=.Parent.Sheets(Sheets.Count)
With .Parent.Worksheets(Sheets.Count) ' It is the ActiveSheet, now.
If cBlnTargetFirstRow Then ' Target first row calculation enabled.
If .Cells(.Rows.Count, cVntTargetC).End(xlUp).Row = 1 And _
IsEmpty(.Cells(.Cells(.Rows.Count, _
cVntTargetC).End(xlUp).Row, cVntTargetC)) Then
.Cells(.Cells(.Rows.Count, cVntTargetC).End(xlUp).Row, _
cVntTargetC).Resize(UBound(vntTarget)) = vntTarget
Else
.Cells(.Cells(.Rows.Count, cVntTargetC).End(xlUp).Row + 1, _
cVntTargetC).Resize(UBound(vntTarget)) = vntTarget
End If
Else ' Target first row calculation disabled.
.Cells(cLngTargetFR, cVntTargetC).Resize(UBound(vntTarget)) _
= vntTarget
End If
End With
End With
Else ' Paste into range of specified worksheet.
With ThisWorkbook.Worksheets(cVntTarget)
If cBlnTargetFirstRow Then ' Target first row calculation enabled.
If .Cells(.Rows.Count, cVntTargetC).End(xlUp).Row = 1 And _
IsEmpty(.Cells(.Cells(.Rows.Count, cVntTargetC).End(xlUp).Row, _
cVntTargetC)) Then
.Cells(.Cells(.Rows.Count, cVntTargetC).End(xlUp).Row, _
cVntTargetC).Resize(UBound(vntTarget)) = vntTarget
Else
.Cells(.Cells(.Rows.Count, cVntTargetC).End(xlUp).Row + 1, _
cVntTargetC).Resize(UBound(vntTarget)) = vntTarget
End If
Else ' Target first row calculation disabled.
.Cells(cLngTargetFR, cVntTargetC).Resize(UBound(vntTarget)) _
= vntTarget
End If
End With
End If
Erase vntTarget
ProcedureExit:
With Application
.EnableEvents = True
.Calculation = xlCalculationAutomatic
.ScreenUpdating = True
End With
Exit Sub
SourceArrayErr:
MsgBox "No data in Source Array."
GoTo ProcedureExit
UnexpectedErr:
MsgBox "An unexpected error occurred. Error: '" & Err.Number & "', " _
& Err.Description
GoTo ProcedureExit
End Sub
'*******************************************************************************

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