how to merge cells with same value in one row - excel

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.

Related

Insert numbered cells + row based on cell value

I have managed to insert rows based on cell value for instance if A1 cell is 20, I run the macro, 20 rows appear under A1, those rows are blank right, I need the 20 new cells below A1 to be number 1 to 20 ( the number in A1) let me know if possible.
Cheers Adrien
Try this:
Sub counter()
Dim i as integer
for i = 2 to cells(1, 1) + 1
cells(i, 1) = i - 1
next i
End Sub
Insert an Integer Sequence Below a Cell
A Basic Example For the Active Sheet
Note that this doesn't insert rows, it just writes the integer sequence to the cells below A1.
Sub IntegersBelow()
With Range("A1")
.Resize(.Value).Offset(1).Value _
= .Worksheet.Evaluate("ROW(1:" & CStr(.Value) & ")")
End With
End Sub
Applied to Your Actual Use Case
Adjust the values in the constants section.
Sub InsertIntegersBelow()
' Use constants to change their values in one place instead
' of searching for them in the code (each may be used multiple times).
Const wsName As String = "Sheet1"
Const fRow As Long = 3
Const Col As String = "E"
' Reference the workbook ('wb').
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing the code
' Reference the worksheet ('ws').
Dim ws As Worksheet: Set ws = wb.Worksheets(wsName)
' Calculate the last row ('lRow'),
' the row of the last non-empty cell in the column.
Dim lRow As Long: lRow = ws.Cells(ws.Rows.Count, Col).End(xlUp).Row
' Validate the last row.
If lRow < fRow Then
MsgBox "No data in column range.", vbInformation
Exit Sub
End If
Dim cCell As Range ' Current Cell
Dim cValue As Variant ' Current Cell Value
Dim r As Long ' Current Row
For r = lRow To fRow Step -1 ' loop backwards
Set cCell = ws.Cells(r, Col) ' reference the current cell...
cValue = cCell.Value ' ... and write its value to a variable
If VarType(cValue) = vbDouble Then ' is a number
cValue = CLng(cValue) ' ensure whole number
If cValue > 0 Then ' greater than 0
' Insert the rows.
cCell.Offset(1).Resize(cValue) _
.EntireRow.Insert xlShiftDown, xlFormatFromLeftOrAbove
With cCell.Offset(1).Resize(cValue)
' Write the values.
.Value = ws.Evaluate("ROW(1:" & cValue & ")")
' Apply formatting.
.ClearFormats
.Font.Bold = True
End With
'Else ' less than or equal to zero; do nothing
End If
'Else ' is not a number
End If
Next r
MsgBox "Rows inserted.", vbInformation
End Sub

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

Speeding Up a Loop in VBA

I am trying to speed up a loop in VBA with over 25,000 line items
I have code that is stepping down through a spread sheet with over 25,000 lines in it. Right now the code loops thought each cell to see if the Previous cell values match the current cell values. If they do not match it inserts a new blank line. Right now the code take over 5 hours to complete on a pretty fast computer. Is there any way I can speed this up?
With ActiveSheet
BottomRow4 = .Cells(.Rows.Count, "E").End(xlUp).Row
End With
Do
Cells(ActiveCell.Row, 5).Select
Do
ActiveCell.Offset(1, 0).Select
'Determines if previous cells is the same as current cells
Loop Until (ActiveCell.Offset(0, -1) & ActiveCell <>
ActiveCell.Offset(1, -1) & ActiveCell.Offset(1, 0))
'Insert Blank Row if previous cells do not match current cells...
Rows(ActiveCell.Offset(1, 0).Row & ":" & ActiveCell.Offset(1,
0).Row).Select
Selection.Insert Shift:=xlDown, CopyOrigin:=xlFormatFromLeftOrAbove
BottomRow4 = BottomRow4 + 1
Loop Until ActiveCell.Row >= BottomRow4
Similarly to when deleting rows, you can save your inserts until you're done looping.
Run after selecting a cell at the top of the column you want to insert on (but not on row 1):
Sub Tester()
Dim c As Range, rngIns As Range, sht As Worksheet
Dim offSet As Long, cInsert As Range
Set sht = ActiveSheet
For Each c In sht.Range(Selection, _
sht.Cells(sht.Rows.Count, Selection.Column).End(xlUp)).Cells
offSet = IIf(offSet = 0, 1, 0) '<< toggle offset
If c.offSet(-1, 0).Value <> c.Value Then
'This is a workaround to prevent two adjacent cells from merging in
' the rngInsert range being built up...
Set cInsert = c.offSet(0, offSet)
If rngIns Is Nothing Then
Set rngIns = cInsert
Else
Set rngIns = Application.Union(cInsert, rngIns)
End If
End If
Next c
If Not rngIns Is Nothing Then
rngIns.EntireRow.Insert Shift:=xlDown, CopyOrigin:=xlFormatFromLeftOrAbove
End If
End Sub
Edit: runs in 3 secs on 25k rows populated using ="Val_" & ROUND(RAND()*1000), converted to values, then sorted.
Insert If Not Equal
Sub InsertIfNotEqual()
Const cSheet As Variant = 1 ' Worksheet Name/Index
Const cFirstR As Long = 5 ' First Row
Const cCol As Variant = "E" ' Last-Row-Column Letter/Number
Dim rng As Range ' Last Cell Range, Union Range
Dim vntS As Variant ' Source Array
Dim vntT As Variant ' Target Array
Dim i As Long ' Source Array Row Counter
Dim j As Long ' Target Array Row Counter
With Application
.ScreenUpdating = False
.Calculation = xlCalculationManual
End With
On Error GoTo ProcedureExit
' In Worksheet
With ThisWorkbook.Worksheets(cSheet)
' Determine the last used cell in Last-Row-Column.
Set rng = .Columns(cCol).Find("*", , xlFormulas, , , xlPrevious)
' Copy Column Range to Source Array.
vntS = .Cells(cFirstR, cCol).Resize(rng.Row - cFirstR + 1)
End With
' In Arrays
' Resize 1D Target Array to the first dimension of 2D Source Array.
ReDim vntT(1 To UBound(vntS)) As Long
' Loop through rows of Source Array.
For i = 2 To UBound(vntS)
' Check if current value is equal to previous value.
If vntS(i, 1) <> vntS(i - 1, 1) Then
' Increase row of Target Array.
j = j + 1
' Write Source Range Next Row Number to Target Array.
vntT(j) = i + cFirstR
End If
Next
' If no non-equal data was found.
If j = 0 Then Exit Sub
' Resize Target Array to found "non-equal data count".
ReDim Preserve vntT(1 To j) As Long
' In Worksheet
With ThisWorkbook.Worksheets(cSheet)
' Set Union range to first cell of row in Target Array.
Set rng = .Cells(vntT(1), 2)
' Check if there are more rows in Target Array.
If UBound(vntT) > 1 Then
' Loop through the rest of the rows (other than 1) in Target Array.
For i = 2 To UBound(vntT)
' Add corresponding cells to Union Range. To prevent the
' creation of "consecutive" ranges by Union, the resulting
' cells to be added are alternating between column A and B
' (1 and 2) using the Mod operator against the Target Array
' Row Counter divided by 2.
Set rng = Union(rng, .Cells(vntT(i), 1 + i Mod 2))
Next
End If
' Insert blank rows in one go.
rng.EntireRow.Insert
End With
ProcedureExit:
With Application
.Calculation = xlCalculationAutomatic
.ScreenUpdating = True
End With
End Sub
Edited: Added two options: didn't test for speed. I thought test2() would have been faster but I'm not certain depending on number of rows.
Untested, but just something I thought of quickly. If I'll remember I'll come back to this later because I think there are faster ways
Sub Test1()
Dim wsSheet As Worksheet
Dim arrSheet() As Variant
Dim collectRows As New Collection
Dim rowNext As Long
Application.ScreenUpdating = False
Application.EnableEvents = False
Const ColCheck As Integer = 6
Set wsSheet = ActiveSheet
arrSheet = wsSheet.Range("A1").CurrentRegion
For rowNext = UBound(arrSheet, 1) To LBound(arrSheet, 1) + 1 Step -1
If arrSheet(rowNext, ColCheck) <> arrSheet(rowNext - 1, ColCheck) Then collectRows.Add rowNext
Next rowNext
For rowNext = 1 To collectRows.Count
wsSheet.Cells(collectRows(rowNext), 1).EntireRow.Insert
Next rowNext
Application.ScreenUpdating = True
Application.EnableEvents = True
End Sub
Second Option inserting all at once:
I used a string here because union would change rows next to each other into one larger range. Instead of Range("1:1", "2:2") it would create ("1:2") and that won't insert the way you need. I don't know of a cleaner way, but there probably is.
Sub Test2()
Dim wsSheet As Worksheet
Dim arrSheet() As Variant
Dim collectRows As New Collection
Dim rowNext As Long
Dim strRange As String
Dim cntRanges As Integer
Dim rngAdd As Range
Application.ScreenUpdating = False
Application.EnableEvents = False
Const ColCheck As Integer = 6
Set wsSheet = ActiveSheet
arrSheet = wsSheet.Range("A1").CurrentRegion
For rowNext = UBound(arrSheet, 1) To LBound(arrSheet, 1) + 1 Step -1
If arrSheet(rowNext, ColCheck) <> arrSheet(rowNext - 1, ColCheck) Then
strRange = wsSheet.Cells(rowNext, 1).EntireRow.Address & "," & strRange
cntRanges = cntRanges + 1
If cntRanges > 10 Then
collectRows.Add Left(strRange, Len(strRange) - 1)
strRange = vbNullString
cntRanges = 0
End If
End If
Next rowNext
If collectRows.Count > 0 Then
Dim i As Long
For i = 1 To collectRows.Count
Set rngAdd = Range(collectRows(i))
rngAdd.Insert
Next i
End If
Application.ScreenUpdating = True
Application.EnableEvents = True
End Sub

Find Cell containing text in column and does NOT contain certain word in first 6 characters of string

I am searching a column for cell that contains text and does not contain the word "cat" in the first 6 characters (needs to be case insensitive). This will then cut that entire row to another sheet. Cannot get the code to run without compile errors. the below code is before i try to change it. I do not know how to code it to look at the first 6 characters.
tried instr & iserror but i think my existing code just needs a small alteration which escapes me.
Sub CATDEFECTS()
UsdRws = Range("C" & Rows.Count).End(xlUp).Row
For i = UsdRws To 2 Step -1
If Range("C" & i).Value Like "<>""" And Range("c" & i).Value Like "CAT" Then
Rows(i).Cut Sheets("AWP DEFECTS").Range("A" & rows.Count).End(xlUp).Offset(1)
Rows(i).Delete
End If
Next i
End Sub
Regardless of how you decide to implement the macro, your test to see if a cell is blank is entirely redundant. You can just test if the cell meets your CAT criteria. If it does, it is definitely not blank so no need to test it.
Method 1
You can look at the first 6 characters with LEFT(Range, 6)
If Left(Range("C" & i), 6) Like "*CAT*" Then
This needs Option Compare to work (Thanks #Comintern)
Method 2
I would prefer this method. Its explicit and does not delete or shift anything inside the loop so your action statements are greatly minimized.
Sub Cat()
Dim ws As Worksheet: Set ws = ThisWorkbook.Sheets("Sheet1") '<--UPDATE
Dim ps As Worksheet: Set ps = ThisWorkbook.Sheets("AWP DEFECTS")
Dim LR As Long, DeleteMe As Range, i As Long
LR = ws.Range("C" & ws.Rows.Count).End(xlUp).Row
For i = 2 To LR
If InStr(Left(ws.Range("C" & i), 6), "CAT") Then
If Not DeleteMe Is Nothing Then
Set DeleteMe = Union(DeleteMe, ws.Range("C" & i))
Else
Set DeleteMe = ws.Range("C" & i)
End If
End If
Next i
Application.ScreenUpdating = False
If Not DeleteMe Is Nothing Then
LR = ps.Range("A" & ps.Rows.Count).End(xlUp).Row
DeleteMe.EntireRow.Copy ps.Range("A" & LR)
DeleteMe.EntireRow.Delete
End If
Application.ScreenUpdating = True
End Sub
If cat is within the first 6 characters then InStr will report its position being less than 5.
Sub CATDEFECTS()
dim UsdRws as long, pos as long
UsdRws = Range("C" & Rows.Count).End(xlUp).Row
For i = UsdRws To 2 Step -1
pos =instr(1, cells(i, "C").value2, "cat", vbtextcompare)
If pos > 0 and pos < 5 Then
Rows(i).Cut Sheets("AWP DEFECTS").Range("A" & rows.Count).End(xlUp).Offset(1)
Rows(i).Delete
End If
Next i
End Sub
Criteria Backup (Hide/Delete)
To enable the deletion of the rows in the Source Worksheet you have to set cDEL to True in the constants section. Adjust the other constants to fit you needs.
The Code
Option Explicit
'Option Compare Text
Sub CATDEFECTS()
With Application
.ScreenUpdating = False
.Calculation = xlCalculationManual
End With
On Error GoTo ProcedureExit
' Source Constants
Const cSource As Variant = "Sheet1" ' Worksheet Name/Index
Const cCol As Variant = "C" ' Search Column Letter/Number
Const cFirstR As Long = 2 ' First Row Number
Const cChars As Long = 6 ' Number of Chars
Const cSearch As String = "CAT" ' Search String
' Target Constants
Const cTarget As Variant = "AWP DEFECTS" ' Worksheet Name/Index
Const cColTgt As Variant = "A" ' Column Letter/Number
Const cFirstRTgt As Long = 2 ' First Row Number
Const cDEL As Boolean = False ' Enable Delete (True)
' Variables
Dim rngH As Range ' Help Range
Dim rngU As Range ' Union Range
Dim vntS As Variant ' Source Array
Dim i As Long ' Source Range Row Counter
' The Criteria
' When the first "cChars" characters do not contain the case-INsensitive
' string "cSearch", the criteria is met.
' Source Worksheet
With ThisWorkbook.Worksheets(cSource)
' Calculate Last Cell in Search Column using the Find method and
' assign it to Help (Cell) Range.
Set rngH = .Columns(cCol).Find("*", , xlFormulas, _
xlWhole, xlByColumns, xlPrevious)
' Calculate Source Column Range from Help (Cell) Range.
If Not rngH Is Nothing Then ' Last Cell was found.
' Calculate Source Column Range and assign it to
' Help (Column) Range using the Resize method.
Set rngH = .Cells(cFirstR, cCol).Resize(rngH.Row - cFirstR + 1)
' Copy Help (Column) Range into 2D 1-based 1-column Source Array.
vntS = rngH
' Show hidden rows to prevent the resulting rows (the rows to be
' hidden or deleted) to appear hidden in Target Worksheet.
rngH.EntireRow.Hidden = False
Else ' Last Cell was NOT found (unlikely).
MsgBox "Empty Column '" & cCol & "'."
GoTo ProcedureExit
End If
' Loop through rows of Source Array.
For i = 1 To UBound(vntS)
' Check if current Source Array value doesn't meet Criteria.
If InStr(1, Left(vntS(i, 1), cChars), cSearch, vbTextCompare) = 0 _
Then ' "vbUseCompareOption" if "Option Compare Text"
' Note: To use the Like operator instead of the InStr function
' you have to use (uncomment) "Option Compare Text" at the beginning
' of the module for a case-INsensitive search and then outcomment
' the previous and uncomment the following line.
' If Not Left(vntS(i, 1), cChars) Like "*" & cSearch & "*" Then
Set rngH = .Cells(i + cFirstR - 1, cCol)
If Not rngU Is Nothing Then
' Union Range contains at least one range.
Set rngU = Union(rngU, rngH)
Else
' Union Range does NOT contain a range (only first time).
Set rngU = rngH
End If
End If
Next
End With
' Target Worksheet
If Not rngU Is Nothing Then ' Union Range contains at least one range.
With ThisWorkbook.Worksheets(cTarget)
' Calculate Last Cell in Search Column using the Find method and
' assign it to Help Range.
Set rngH = .Columns(cColTgt).Find("*", , xlFormulas, _
xlWhole, xlByColumns, xlPrevious)
' Calculate Last Cell from Help Range, but in column 1 ("A").
If Not rngH Is Nothing Then ' Last Cell was found.
Set rngH = .Cells(rngH.Row + 1, 1)
Else ' Last Cell was NOT found.
Set rngH = .Cells(cFirstRTgt - 1, 1)
End If
' Copy the entire Union Range to Target Worksheet starting from
' Help Range Row + 1 i.e. the first empty row (in one go).
' Note that you cannot Cut/Paste on multiple selections.
rngU.EntireRow.Copy rngH
End With
' Hide or delete the transferred rows (in one go).
If cDEL Then ' Set the constant cDEL to True to enable Delete.
rngU.EntireRow.Delete
Else ' While testing the code it is better to use Hidden.
rngU.EntireRow.Hidden = True
End If
End If
ProcedureExit:
With Application
.Calculation = xlCalculationAutomatic
.ScreenUpdating = True
End With
End Sub
Remarks
The use of the array did not speed up considerably.
The InStr function was a few milliseconds faster than the Like operator in my data set.
Calculating the Real Used Range and copying it into a Source Array
and then writing the data that meets the criteria from Source Array
to a Target Array and copying the Target Array to the Target
Worksheet, might be faster, and/but would additionally copy the data without formulas or formatting.

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
'*******************************************************************************

Resources