I want to refer to a range of cells across columns: B:C then E:M (skipping D). I want to copy the cells and paste them to another worksheet.
I have a For Next loop with the row number variable iT. How do I select them using the variable?
This selects the whole range including D.
Sheet4.Range("B" & iT & ":C" & iT, "E" & iT & ":M" & iT).Select
I tried Cells().
Try this
Sht.range("A:M").copy AnotherWorkbook.sheets("YourSheet").range("A1")
AnotherWorkbook.sheets("YourSheet").range("D:D").delete
A Brief Study
Copy Values, Formats, Formulas
Sub NonContiguousRow()
Dim iT As Long
iT = 1
Dim cols As Range
Set cols = Sheet1.Range("B:C,E:M")
' Optionally:
'Set cols = Union(Sheet1.Columns("B:C"), Sheet1.Columns("E:M"))
Dim rRng As Range
Set rRng = Intersect(Sheet1.Rows(iT), cols)
rRng.Copy Sheet2.Cells(1, "A")
' This will also work:
'Dim ColumnsCount As Long
'ColumnsCount = getColumnsCount(cols)
'rRng.Copy Sheet2.Cells(1, "A").Resize(, ColumnsCount)
' This will NOT work:
'Sheet2.Cells(1, "A").Resize(, ColumnsCount).Value = rRng.Value
End Sub
Function getColumnsCount( _
aRange As Range) _
As Long
If Not aRange Is Nothing Then
Dim rng As Range
For Each rng In aRange.Areas
getColumnsCount = getColumnsCount + rng.Columns.Count
Next rng
End If
End Function
Copy Values
Sub TESTgetRow()
Dim iT As Long
iT = 1
Dim cols As Range
Set cols = Sheet1.Range("B:C,E:M")
Dim Data As Variant
Data = getRow(cols, iT)
Sheet2.Cells(1, "A").Resize(, UBound(Data) - LBound(Data) + 1).Value = Data
End Sub
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Purpose: Returns the values at the intersection of a range
' and one of its worsheet's rows, in an array.
' Remarks: Supports non-contiguous ranges.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function getRow( _
aRange As Range, _
Optional ByVal aRow As Long = 1) _
As Variant
If Not aRange Is Nothing Then
Dim rRng As Range
Set rRng = Intersect(aRange, aRange.Worksheet.Rows(aRow))
If Not rRng Is Nothing Then
With CreateObject("Scripting.Dictionary")
Dim rng As Range
Dim cel As Range
Dim n As Long
For Each rng In rRng.Areas
For Each cel In rng.Cells
n = n + 1
.Item(n) = cel.Value
Next cel
Next rng
getRow = .Items
End With
Else
' Row range is empty ('Nothing').
End If
Else
' Range is empty ('Nothing').
End If
End Function
If you want to use Cells Method.
Sub CopyUsingCellsMethod()
Dim ColumnNumber As Long
Dim RowNumber As Long
RowNumber = 1 'Enter Your Required Row Number Here
With ThisWorkbook.Worksheets("Sheet4")
For ColumnNumber = 2 To 5 Step 3 'This would Copy Range(B1:C1) into Range(I1:J1) and Then Range(E1:F1) into Range(L1:M1)
.Range(Cells(RowNumber, ColumnNumber), Cells(RowNumber, ColumnNumber + 1)).Copy Worksheets("Sheet4").Range(.Cells(RowNumber, ColumnNumber + 7), .Cells(RowNumber, ColumnNumber + 8))
Next ColumnNumber
End With
End Sub
Related
I have a workbook includes 2 sheets.
In each sheet, it has couple columns like Name(column A), State(column B) and ID (column C). But the rows' sort sequence of two sheets are both random.
According to IDs, I need to use VBA to compare the value of Name and State.
If they don't match, then highlight both of 2 cells in 2 sheets.
The result should be like this:
But my code below can only run for Column A if IDs have the same order sequence.
I understand that it can be much easier if I use conditional formatting to create a new rule or use vlookup or index and match function to compare. But I am asked to use VBA
Thank you!
Sub Test_Sheet()
Dim sheetOne As Worksheet
Dim sheetTwo As Worksheet
Dim lastRow As Long
Dim lastRow2 As Long
Dim thisRow As Long
Dim thisRow2 As Long
Dim lastCol As Long
Dim lastCol2 As Long
Dim thisCol As Long
Dim thisCol2 As Long
Dim foundRow As Range
Dim foundRow2 As Range
Dim lastFoundRow As Long
Dim lastFoundRow2 As Long
Dim searchRange As Range
Dim searchRange2 As Range
Dim isMatch As Boolean
Dim isMatch2 As Boolean
' Set up the sheets
Set sheetOne = Sheets("Sheet1")
Set sheetTwo = Sheets("Sheet2")
' Find the last row of the active sheet
lastRow = sheetOne.Cells(sheetOne.Rows.Count, "A").End(xlUp).Row
lastRow2 = sheetOne.Cells(sheetOne.Rows.Count, "B").End(xlUp).Row
' Set the search range on the other sheet
Set searchRange = sheetTwo.Range("A2:A" & sheetTwo.Cells(sheetTwo.Rows.Count, "A").End(xlUp).Row)
Set searchRange2 = sheetTwo.Range("B2:B" & sheetTwo.Cells(sheetTwo.Rows.Count, "B").End(xlUp).Row)
' Look at all rows
For thisRow = 1 To lastRow
' Find the last column on this row
lastCol = sheetOne.Cells(thisRow, sheetOne.Columns.Count).End(xlToLeft).Column
' Find the first match
Set foundRow = searchRange.Find(sheetOne.Cells(thisRow, "A").Value, searchRange(searchRange.Count), xlValues, xlWhole)
' Must find something to continue
Do While Not foundRow Is Nothing
' Remember the row we found it on
lastFoundRow = foundRow.Row
' Check the found row has the same number of columns
If sheetTwo.Cells(lastFoundRow, sheetTwo.Columns.Count).End(xlToLeft).Column = lastCol Then
' Assume it's a match
isMatch = True
' Look at all the column values
For thisCol = 1 To lastCol
' Compare the column values
If sheetTwo.Cells(lastFoundRow, thisCol).Value <> sheetOne.Cells(thisRow, thisCol).Value Then
' No match
isMatch = False
Exit For
End If
Next thisCol
' If it's still a match then highlight the row
If isMatch Then sheetOne.Range(sheetOne.Cells(thisRow, "A"), sheetOne.Cells(thisRow, lastCol)).Interior.ColorIndex = 3
End If
' Find the next match
Set foundRow = searchRange.Find(sheetOne.Cells(thisRow, "A").Value, foundRow, xlValues, xlWhole)
' Quit out when we wrap around
If foundRow.Row <= lastFoundRow Then Exit Do
Loop
Next thisRow
For thisRow2 = 1 To lastRow2
lastCol2 = sheetOne.Cells(thisRow2, sheetOne.Columns.Count).End(xlToLeft).Column
Set foundRow2 = searchRange2.Find(sheetOne.Cells(thisRow2, "B").Value, searchRange2(searchRange2.Count), xlValues, xlWhole)
Do While Not foundRow2 Is Nothing
lastFoundRow2 = foundRow2.Row
If sheetTwo.Cells(lastFoundRow2, sheetTwo.Columns.Count).End(xlToLeft).Column = lastCol2 Then
isMatch2 = True
For thisCol2 = 1 To lastCol2
If sheetTwo.Cells(lastFoundRow2, thisCol2).Value <> sheetOne.Cells(thisRow2, thisCol2).Value Then
isMatch2 = False
Exit For
End If
Next thisCol2
If isMatch2 Then sheetOne.Range(sheetOne.Cells(thisRow2, "B"), sheetOne.Cells(thisRow2, lastCol2)).Interior.ColorIndex = 5
End If
Set foundRow2 = searchRange2.Find(sheetOne.Cells(thisRow2, "B").Value, foundRow2, xlValues, xlWhole)
If foundRow2.Row <= lastFoundRow2 Then Exit Do
Loop
Next thisRow2
End Sub
Please, try the next code. It uses arrays, for faster iteration, processing the matching in memory and Union ranges, coloring the cells interior at once, at the end. Modifying the interior of each cell consumes Excel resources and takes time:
Sub testCompareIDs()
Dim sheetOne As Worksheet, sheetTwo As Worksheet, lastRow1 As Long, lastRow2 As Long, i As Long, j As Long
Dim rng1 As Range, rng2 As Range, arr1, arr2, rngColA1 As Range, rngColA2 As Range, rngColB1 As Range, rngColB2 As Range
Set sheetOne = Sheets("Sheet1")
Set sheetTwo = Sheets("Sheet2")
lastRow1 = sheetOne.cells(sheetOne.rows.count, "C").End(xlUp).row
lastRow2 = sheetTwo.cells(sheetOne.rows.count, "C").End(xlUp).row
Set rng1 = sheetOne.Range("A2:C" & lastRow1)
Set rng2 = sheetTwo.Range("A2:C" & lastRow2)
arr1 = rng1.value: arr2 = rng2.value 'place ranges to be processed in arrays, for faster iteration
For i = 1 To UBound(arr1)
For j = 1 To UBound(arr2)
If arr1(i, 3) = arr2(j, 3) Then
If arr1(i, 1) <> arr2(j, 1) Then
If rngColA1 Is Nothing Then
Set rngColA1 = rng1.cells(i, 1)
Set rngColA2 = rng2.cells(j, 1)
Else
Set rngColA1 = Union(rngColA1, rng1.cells(i, 1))
Set rngColA2 = Union(rngColA2, rng2.cells(j, 1))
End If
End If
If arr1(i, 2) <> arr2(j, 2) Then
If rngColB1 Is Nothing Then
Set rngColB1 = rng1.cells(i, 2)
Set rngColB2 = rng2.cells(j, 2)
Else
Set rngColB1 = Union(rngColB1, rng1.cells(i, 2))
Set rngColB2 = Union(rngColB2, rng2.cells(j, 2))
End If
End If
Exit For 'exit iteration since the ID has been found
End If
Next j
Next i
If Not rngColA1 Is Nothing Then
rngColA1.Interior.ColorIndex = 3
rngColA2.Interior.ColorIndex = 3
End If
If Not rngColB1 Is Nothing Then
rngColB1.Interior.ColorIndex = 3
rngColB2.Interior.ColorIndex = 3
End If
End Sub
The strings compare is case sensitive. The code can be adapted to not be case sensitive (using Ucase for each compare line)
Please, send some feedback after testing it.
I have a loop that goes through two-dimensional range
set rng = Range("A1:C3")
For each cell in rng
Debug.Print cell.value
next cell
It prints like:
A1
B1
C1
A2
B2...
I would prefer:
A1
A2
A3
B1
B2...
I know there is a way to do that looping with indexes but for each is much more elegant and on the top of that my range can be non-contiguous like:
Range("A1:C3,G9:H16,B14:D18")
Is there a way how to change default (horizontal) excel behavior?
Loop Through the Cells of a Non-Contiguous Range (Multi-Range)
By Areas
The numbers in the yellow cells in the image are illustrating the looping order by areas and by rows. The results of the procedures in the J and K columns are copied from the Immediate window and are showing both of the looping orders.
Option Explicit
Sub WriteCellsByAreaRows()
Const sAddress As String = "A1:C3,G9:H16,B14:D18"
Dim ws As Worksheet: Set ws = ActiveSheet
Dim mrg As Range: Set mrg = ws.Range(sAddress)
Dim arg As Range
Dim aCell As Range
For Each arg In mrg.Areas
For Each aCell In arg.Cells
Debug.Print aCell.Address(0, 0) & " = " & aCell.Value
Next aCell
Next arg
End Sub
Sub WriteCellsByAreaColumns()
Const sAddress As String = "A1:C3,G9:H16,B14:D18"
Dim ws As Worksheet: Set ws = ActiveSheet
Dim mrg As Range: Set mrg = ws.Range(sAddress)
Dim arg As Range
Dim acrg As Range
Dim aCell As Range
For Each arg In mrg.Areas
For Each acrg In arg.Columns
For Each aCell In acrg.Cells
Debug.Print aCell.Address(0, 0) & " = " & aCell.Value
Next aCell
Next acrg
Next arg
End Sub
EDIT
Area Independent (The Challenge)
The numbers in the yellow cells in the image are illustrating the looping order by rows. The results of the procedures in the J and K columns are copied from the Immediate window and are showing both of the looping orders.
Short Function Description (By Columns)
It will loop through the columns of each area and write the column number to an Array List which will finally be sorted. When looping through the elements of the Array List (each column number), a reference to the intersection of the worksheet column and the initial multi-range will be created and each single range reference will be written to an array. The array will be (bubble) sorted by the row numbers of every single column range and then will be added as an element of the resulting array of arrays.
Sub WriteCellsByColumn()
Const sAddress As String = "A1:C3,G9:H16,B14:D18"
Dim ws As Worksheet: Set ws = ActiveSheet
On Error Resume Next
Dim mrg As Range: Set mrg = ws.Range(sAddress)
On Error GoTo 0
If mrg Is Nothing Then Exit Sub
Dim ColumnRanges As Variant: ColumnRanges = GetSortedColumnRanges(mrg)
If IsEmpty(ColumnRanges) Then Exit Sub ' e.g. 'ArrayList' is not working
Dim cCell As Range
Dim a As Long
Dim b As Long
For a = 0 To UBound(ColumnRanges)
For b = 0 To UBound(ColumnRanges(a))
For Each cCell In ColumnRanges(a)(b).Cells
Debug.Print cCell.Address(0, 0) & " = " & cCell.Value
Next cCell
Next b
Next a
End Sub
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Purpose: Returns the sorted column range references of a range ('mrg')
' in an array of arrays.
' Remarks: Supports non-contiguous ranges (multi-ranges).
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function GetSortedColumnRanges( _
ByVal mrg As Range) _
As Variant
If mrg Is Nothing Then Exit Function
On Error Resume Next ' The ArrayList needs 'Microsoft .NET Framework 3.5'.
Dim arl As Object: Set arl = CreateObject("System.Collections.ArrayList")
On Error GoTo 0
If arl Is Nothing Then Exit Function
Dim arg As Range ' Area (Single) Range
Dim crg As Range ' Column Range (in Area Range)
Dim cColumn As Long ' Column Number (of Column Range)
For Each arg In mrg.Areas
For Each crg In arg.Columns
cColumn = crg.Column
If Not arl.Contains(cColumn) Then
arl.Add cColumn
End If
Next crg
Next arg
arl.Sort
' Create a reference to the worksheet ('ws').
Dim ws As Worksheet: Set ws = mrg.Worksheet
' Define the Multi Range Array ('mrgArr').
Dim mrgArr As Variant: ReDim mrgArr(0 To arl.Count - 1)
Dim icrg As Range ' Intersect Column Range (in Worksheet Column)
Dim trg As Range ' Temp Range
Dim trgArr() As Range ' Temp Range Array
Dim Key As Variant '
Dim aUpper As Long
Dim a As Long
Dim b As Long
Dim n As Long
For Each Key In arl
Set icrg = Intersect(mrg, ws.Columns(Key))
aUpper = icrg.Areas.Count - 1
If aUpper = 0 Then
' Write current Intersect Row Range reference to Temp Range Array.
ReDim trgArr(0 To 0): Set trgArr(0) = icrg
Else
' Write current Intersect Row Range references to Temp Range Array.
ReDim trgArr(0 To aUpper)
For a = 0 To aUpper
Set trgArr(a) = icrg.Areas(a + 1)
Next a
' (Bubble) Sort the references by rows.
For a = 0 To aUpper - 1
For b = a To aUpper
If trgArr(a).Row > trgArr(b).Row Then
Set trg = trgArr(a)
Set trgArr(a) = trgArr(b)
Set trgArr(b) = trg
End If
Next b
Next a
End If
' Write the current Temp Range Array to the current element
' of the Multi Range Array.
mrgArr(n) = trgArr
n = n + 1
Next Key
GetSortedColumnRanges = mrgArr
End Function
Sub WriteCellsByRow()
Const sAddress As String = "A1:C3,G9:H16,B14:D18"
Dim ws As Worksheet: Set ws = ActiveSheet
On Error Resume Next
Dim mrg As Range: Set mrg = ws.Range(sAddress)
On Error GoTo 0
If mrg Is Nothing Then Exit Sub
Dim RowRanges As Variant: RowRanges = GetSortedRowRanges(mrg)
If IsEmpty(RowRanges) Then Exit Sub ' e.g. 'ArrayList' is not working
Dim rCell As Range
Dim a As Long
Dim b As Long
For a = 0 To UBound(RowRanges)
For b = 0 To UBound(RowRanges(a))
For Each rCell In RowRanges(a)(b).Cells
Debug.Print rCell.Address(0, 0) & " = " & rCell.Value
Next rCell
Next b
Next a
End Sub
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Purpose: Returns the sorted row range references of a range ('mrg')
' in an array of arrays.
' Remarks: Supports non-contiguous ranges (multi-ranges).
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function GetSortedRowRanges( _
ByVal mrg As Range) _
As Variant
If mrg Is Nothing Then Exit Function
On Error Resume Next ' The ArrayList needs 'Microsoft .NET Framework 3.5'.
Dim arl As Object: Set arl = CreateObject("System.Collections.ArrayList")
On Error GoTo 0
If arl Is Nothing Then Exit Function
Dim arg As Range ' Area (Single) Range
Dim rrg As Range ' Row Range (in Area Range)
Dim cRow As Long ' Row Number (of Row Range)
For Each arg In mrg.Areas
For Each rrg In arg.Rows
cRow = rrg.Row
If Not arl.Contains(cRow) Then
arl.Add cRow
End If
Next rrg
Next arg
arl.Sort
' Create a reference to the worksheet ('ws').
Dim ws As Worksheet: Set ws = mrg.Worksheet
' Define the Multi Range Array ('mrgArr').
Dim mrgArr As Variant: ReDim mrgArr(0 To arl.Count - 1)
Dim irrg As Range ' Intersect Row Range (in Worksheet Row)
Dim trg As Range ' Temp Range
Dim trgArr() As Range ' Temp Range Array
Dim Key As Variant '
Dim aUpper As Long
Dim a As Long
Dim b As Long
Dim n As Long
For Each Key In arl
Set irrg = Intersect(mrg, ws.Rows(Key))
aUpper = irrg.Areas.Count - 1
If aUpper = 0 Then
' Write current Intersect Row Range reference to Temp Range Array.
ReDim trgArr(0 To 0): Set trgArr(0) = irrg
Else
' Write current Intersect Row Range references to Temp Range Array.
ReDim trgArr(0 To aUpper)
For a = 0 To aUpper
Set trgArr(a) = irrg.Areas(a + 1)
Next a
' (Bubble) Sort the references by rows.
For a = 0 To aUpper - 1
For b = a To aUpper
If trgArr(a).Column > trgArr(b).Column Then
Set trg = trgArr(a)
Set trgArr(a) = trgArr(b)
Set trgArr(b) = trg
End If
Next b
Next a
End If
' Write the current Temp Range Array to the current element
' of the Multi Range Array.
mrgArr(n) = trgArr
n = n + 1
Next Key
GetSortedRowRanges = mrgArr
End Function
Maybe
Sub Test()
Dim rng As Range, rRow As Range, rCol As Range
Set rng = Range("A1:C3")
For Each rCol In rng.Columns
For Each rRow In rCol.Cells
Debug.Print rRow.Value
Next rRow
Next rCol
End Sub
As a proof-of-concept with non-contiguous ranges that may overlap. Determine the lowest row and right most column of the areas, create a array that encloses all those areas, and fill it with the cell addresses of the cells in each area. Then traverse the array horizontally or vertically skipping over the blank entries.
Option Explicit
Sub demo_by_column()
Dim ar, a, cell As Range, rng As Range, i As Long
Dim rmax As Long, cmax As Long, rmin As Long, cmin As Long
Dim r As Long, c As Long
rmin = Rows.Count
cmin = Columns.Count
' non-contiguous range
Set rng = Sheet1.Range("A1:C3,D9:H16,B14:D18,A21:J21")
' dimension an array that encloses all the areas
For Each a In rng.Areas
r = a.Row + a.Rows.Count - 1
If r > rmax Then rmax = r
If a.Row < rmin Then rmin = a.Row
c = a.Column + a.Columns.Count - 1
If c > cmax Then cmax = c
If a.Column < cmin Then cmin = a.Column
Next
ReDim ar(1 To rmax, 1 To cmax)
' fill array with cell addresses
For Each a In rng.Areas
For Each cell In a
ar(cell.Row, cell.Column) = cell.Address
Next
Next
' loop the array by column, result to sheet2
i = 1
For c = cmin To cmax
For r = rmin To rmax
If Len(ar(r, c)) > 0 Then
Sheet2.Cells(i, 1) = Sheet1.Range(ar(r, c))
i = i + 1
End If
Next
Next
' show array on sheet3
Sheet3.Range("A1").Resize(rmax, cmax) = ar
End Sub
I have master Data in Sheet 2 (Column B) and search criteria in Sheet 1 (Column A), i want VBA to find all the data from Sheet 1 (Column A) in Sheet 2 (Column B) if found cut the entire row and past it into Sheet 3 next available row.
Sub remDup()
Dim LR As Long, LRSheet2 As Long, i As Long, a As Long
Dim vAllSheet2Values() As Variant
LRSheet2 = Worksheets("Sheet2").Cells(Rows.Count, 1).End(xlUp).Row
LR = Worksheets("Sheet1").Cells(Rows.Count, 1).End(xlUp).Row
a = 1
For i = 1 To LRSheet2 'Load all values in ColumnA of Sheet2 into an array
ReDim Preserve vAllSheet2Values(i)
vAllSheet2Values(i) = Worksheets("Sheet2").Cells(i, 2).Value
Next i
For i = LR To 1 Step -1
If IsInArray(Worksheets("Sheet1").Cells(i, 1).Value, vAllSheet2Values) Then
Worksheets("Sheet1").Rows(i).Copy Worksheets("Sheet3").Rows(a)
Worksheets("Sheet1").Rows(i).Delete
a = a + 1
End If
Next i
End Sub
Function IsInArray(stringToBeFound As String, arr As Variant) As Boolean
IsInArray = (UBound(Filter(arr, stringToBeFound)) > -1)
End Function
in above code data is getting deleted from sheet 1 :( and not sheet 2
Backup Matching Rows
In the current setup, the code will search for all values in column A of Sheet1 in column B of Sheet2. The cells of each found value will be combined into a Total Range whose entire rows will be copied to Sheet3 (in one go) and then removed (deleted) from Sheet1 (in another go).
The Code
Option Explicit
Sub remDup()
' Constants
Const sName As String = "Sheet1"
Const sFirst As String = "A1"
Const lName As String = "Sheet2"
Const lFirst As String = "B1"
Const dName As String = "Sheet3"
Const dFirst As String = "A1"
' Workbook
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
' Source
Dim sws As Worksheet: Set sws = wb.Worksheets(sName)
Dim srg As Range: Set srg = refColumn(sws.Range(sFirst))
If srg Is Nothing Then Exit Sub
Dim sData As Variant: sData = getColumn(srg)
' Lookup
Dim lws As Worksheet: Set lws = wb.Worksheets(lName)
Dim lrg As Range: Set lrg = refColumn(lws.Range(lFirst))
If lrg Is Nothing Then Exit Sub
Dim lData As Variant: lData = getColumn(lrg)
' Match
Dim trg As Range
Dim i As Long
For i = 1 To UBound(sData)
If foundMatchInVector(sData(i, 1), lData) Then
Set trg = getCombinedRange(trg, srg.Cells(i))
End If
Next i
' Destination
If Not trg Is Nothing Then
Dim dws As Worksheet: Set dws = wb.Worksheets(dName)
' This is a kind of a ridiculous use of "refColumn".
Dim drg As Range: Set drg = refColumn(dws.Range(dFirst))
If drg Is Nothing Then
Set drg = dws.Range(dFirst).EntireRow
Else
Set drg = drg.Cells(drg.Cells.Count).Offset(1).EntireRow
End If
trg.EntireRow.Copy drg
trg.EntireRow.Delete
End If
End Sub
' Assumptions: 'FirstCellRange' is a one-cell range e.g. 'Range("A1")'.
' Returns: Either the range from 'FirstCellRange' to the bottom-most
' non-empty cell in the column, or 'Nothing' if all cells
' below 'FirstCellRange' (incl.) are empty.
Function refColumn( _
ByVal FirstCellRange As Range) _
As Range
With FirstCellRange
Dim cel As Range
Set cel = .Resize(.Worksheet.Rows.Count - .Row + 1) _
.Find("*", , xlFormulas, , , xlPrevious)
If Not cel Is Nothing Then
Set refColumn = .Resize(cel.Row - .Row + 1)
End If
End With
End Function
' Assumptions: 'rg' is a one-column range e.g. 'Range("A1")', 'Range("A1:A2")'.
' Returns: A 2D one-based one-column array.
Function getColumn( _
rg As Range) _
As Variant
If rg.Rows.Count > 1 Then
getColumn = rg.Value
Else
Dim OneElement As Variant: ReDim OneElement(1 To 1, 1 To 1)
OneElement(1, 1) = rg.Value
getColumn = OneElement
End If
End Function
' Assumptions: 'MatchValue' is a simple data type (not an object or an array).
' 'Vector' is a structure that 'Application.Match' can handle,
' e.g. a 1D array, a one-column or one-row range or 2D array.
' Returns: 'True' or 'False' (boolean).
' Remarks: Error values and blanks are ignored ('False').
Function foundMatchInVector( _
ByVal MatchValue As Variant, _
ByVal Vector As Variant) _
As Boolean
If Not IsError(MatchValue) Then
If Len(MatchValue) > 0 Then
foundMatchInVector _
= IsNumeric(Application.Match(MatchValue, Vector, 0))
End If
End If
End Function
' Assumptions: 'AddRange' is not 'Nothing' and it is in the same worksheet
' as 'BuiltRange'.
' Returns: A range (object).
Function getCombinedRange( _
ByVal BuiltRange As Range, _
ByVal AddRange As Range)
If BuiltRange Is Nothing Then
Set getCombinedRange = AddRange
Else
Set getCombinedRange = Union(BuiltRange, AddRange)
End If
End Function
If I'm understanding correctly this should do it. I put comments on the changed lines
Sub remDup()
Dim LR As Long, LRSheet2 As Long, i As Long, a As Long
Dim vAllSheet1Values() As Variant 'This should be referencing sheet 1 not 2
LRSheet2 = Worksheets("Sheet2").Cells(Rows.Count, 1).End(xlUp).Row
LR = Worksheets("Sheet1").Cells(Rows.Count, 1).End(xlUp).Row
a = 1
ReDim Preserve vAllSheet1Values(LR) 'No need for this to be in a loop
For i = 1 To LR 'Load all values in ColumnA of Sheet1 into an array
vAllSheet1Values(i) = Worksheets("Sheet1").Cells(i, 1).Value 'This should be sheet1
Next i
For i = LRSheet2 To 1 Step -1 'This and all sheet1 references after should be sheet 2
If IsInArray(Worksheets("Sheet2").Cells(i, 1).Value, vAllSheet1Values) Then
Worksheets("Sheet2").Rows(i).Copy Worksheets("Sheet3").Rows(a)
Worksheets("Sheet2").Rows(i).Delete
a = a + 1
End If
Next i
End Sub
Function IsInArray(stringToBeFound As String, arr As Variant) As Boolean
IsInArray = (UBound(Filter(arr, stringToBeFound)) > -1)
End Function
Please, try the next code:
Sub remDup()
Dim LR As Long, LRSheet2 As Long, arr, i As Long, rngCopy As Range, rngDel As Range
Dim sh1 As Worksheet, sh2 As Worksheet, sh3 As Worksheet, a As Long
Set sh1 = Worksheets("Sheet1")
Set sh2 = Worksheets("Sheet2")
Set sh3 = Worksheets("Sheet3")
LRSheet2 = sh2.cells(Rows.count, 2).End(xlUp).row
LR = sh1.cells(Rows.count, 1).End(xlUp).row
a = 1 'The Sheet3 row where the rows to be copied
arr = sh2.Range("B1:B" & LRSheet2).Value 'put the range in a 2D array
arr = Application.Transpose(Application.Index(arr, 0, 1)) 'obtain 1D array
For i = 1 To LR
If IsInArray(sh1.cells(i, 1).Value, arr) Then
If rngCopy Is Nothing Then
Set rngCopy = sh1.cells(i, 1) 'create a range to be copied/deleted
Else
Set rngCopy = Union(rngCopy, sh1.cells(i, 1))
End If
End If
Next i
rngCopy.EntireRow.Copy sh3.Range("A" & a) 'copy the range entirerow at once
rngCopy.EntireRow.Delete 'delete the range entirerow
End Sub
Function IsInArray(stringToBeFound As String, arr As Variant) As Boolean
IsInArray = UBound(Filter(arr, stringToBeFound)) > -1
End Function
I have created a function that retrieves a Range based on column name. Here is my code:
Sub sep_Filter()
Dim zip_rng As String
With Sheet2
zip_rng = getColRangeFunction("postalcode")
If Len(Range(zip_rng)) > 5 Then
Range(zip_rng).Interior.Color = RGB(255, 0, 0)
Range(zip_rng).Select
Else
Range(zip_rng).Interior.Color = xlNone
End If
End With
End Sub
Sheet2 Input Column D
Sheet2 Output Column D
Sheet3 Output Column D
088762598
088762598
06610-5000
06610-5000
330161898
330161898
970152880
970152880
112202570
112202570
127420800
127420800
062262040
062262040
07631
07631
10029
10029
11803
11803
99336
99336
EDIT I misunderstood what you were asking, I updated my answer to be tied to your question.
Here's a basic approach that will do what you're asking. It skips row one.
Sub onlyfirst5()
Const pRange As String = "D1"
Dim ws As Worksheet
Set ws = ActiveSheet
Dim crng As Range, cValues()
Set crng = Intersect(ws.UsedRange.Offset(1, 0), ws.UsedRange, ws.Range("D:D"))
cValues = crng.Value
Dim i As Long, j As Long
For i = LBound(cValues) To UBound(cValues)
For j = LBound(cValues, 2) To UBound(cValues, 2)
cValues(i, j) = Left(cValues(i, j), 5)
Next j
Next i
'for same sheet different column
ws.Range("F2").Resize(UBound(cValues), UBound(cValues, 2)) = Application.Transpose(cValues)
'different sheet
Sheets("Sheet2").Range("F2").Resize(UBound(cValues), UBound(cValues, 2)) = Application.Transpose(cValues)
'different file
Workbooks("Zip Code Question.xlsb").Sheets("Sheet3").Range("F2").Resize(UBound(cValues), UBound(cValues, 2)) = Application.Transpose(cValues)
End Sub
Copy Entire Rows If Criteria Met
Option Explicit
Sub Postal5()
' Define constants.
Const srcName As String = "Sheet2"
Const srcFirst As String = "D2"
Const dstName As String = "Sheet3"
Const dstFirst As String = "A2" ' do not change the 'A' (entire row).
Const pLen As Long = 5
' Define workbook.
Dim wb As Workbook: Set wb = ThisWorkbook ' Workbook containing this code.
' Define Source Range.
Dim LastRow As Long
Dim srg As Range
With wb.Worksheets(srcName).Range(srcFirst)
LastRow = .Offset(.Worksheet.Rows.Count - .Row).End(xlUp).Row
Set srg = .Resize(LastRow - .Row + 1)
End With
' 'Combine' critical cells into a range.
Dim brg As Range ' Built Range
Dim cel As Range ' Current Cell Range
For Each cel In srg.Cells
If Len(cel.Value) > pLen Then
If brg Is Nothing Then
Set brg = cel
Else
Set brg = Union(brg, cel)
End If
End If
Next cel
If brg Is Nothing Then Exit Sub
Application.ScreenUpdating = False
' Copy and delete critical rows of Source Range.
With wb.Worksheets(dstName).Range(dstFirst)
.Resize(.Worksheet.Rows.Count - .Row + 1, _
.Worksheet.Columns.Count).Clear
Set brg = brg.EntireRow ' 'Convert' cells into rows.
brg.Copy .Offset ' Copy. 'Offset' because range is in 'With'.
brg.Delete ' Delete.
End With
Application.ScreenUpdating = False
End Sub
Text the next code, please. It uses arrays and it should be very fast for a big range:
Sub testSplitZiPCodeStrings()
Dim sh2 As Worksheet, sh3 As Worksheet, lastR As Long
Dim i As Long, arr, arrZip, arrNoZip, kZ As Long, kN As Long
Set sh2 = ActiveSheet ' Worksheets("Sheet2")
Set sh3 = sh2.Next ' Worksheets("Sheet3")
lastR = sh2.Range("D" & sh2.Rows.count).End(xlUp).row 'last row
arr = sh2.Range("D2:D" & lastR).Value 'put the range in an array
ReDim arrZip(UBound(arr) - 1) 'redim the array to surely have place for all elements
ReDim arrNoZip(UBound(arr) - 1) 'redim the array to surely have place for all elements
For i = 1 To UBound(arr) ' iterate between the array elements
If Len(arr(i, 1)) = 5 Then
arrZip(kZ) = arr(i, 1): kZ = kZ + 1
Else
arrNoZip(kN) = arr(i, 1): kN = kN + 1
End If
Next i
ReDim Preserve arrZip(kZ - 1) 'keep only the array elements having values
ReDim Preserve arrZip(kN - 1) 'keep only the array elements having values
sh2.Range("D2:D" & lastR).Clear 'Clear the initial range
'Drop the Zip array content at once:
sh2.Range("D2").Resize(UBound(arrZip), 1).Value = Application.Transpose(arrZip)
'Drop the NoZip array content at once:
sh3.Range("D2").Resize(UBound(arrNoZip), 1).Value = Application.Transpose(arrNoZip)
End Sub
Here's 2 samples. The first one is more intuitive and uses ranges. The second one is less intuitive but faster by using arrays.
Simple but Slower:
'The easy way, but can be slow if you have lots of zip codes
Sub TrimRange()
Dim InputWorksheet As Worksheet, OutputWorksheet As Worksheet
Dim RangeInput As Range, RangeOutput As Range, Column As Range
Dim HeaderRow As Integer, ColumnNumber As Integer, LastRow As Integer, i As Integer
Dim OutputColumn As Range
Dim ColumnFound As Boolean
Dim fullzipcode As String
Set InputWorksheet = Worksheets("Sheet2")
Set OutputWorksheet = Worksheets("Sheet3")
HeaderRow = 1
'Get Input and Output Range
ColumnNumber = 0
ColumnFound = False
For Each Column In InputWorksheet.Columns
ColumnNumber = ColumnNumber + 1
If Column.Cells(HeaderRow, 1) = "postalcode" Then
LastRow = Column.End(xlDown).Row
'I assume the Output column will be in the same position as the input column
Set OutputColumn = OutputWorksheet.Columns(ColumnNumber)
'If OutputColumn is always in Column 'D' then replace previous line with:
'Set OutputColumn = OutputWorksheet.Columns(4)
Set RangeInput = InputWorksheet.Range(Column.Cells(HeaderRow + 1, 1), Column.Cells(LastRow, 1))
Set RangeOutput = OutputWorksheet.Range(OutputColumn.Cells(HeaderRow + 1, 1), OutputColumn.Cells(LastRow, 1))
ColumnFound = True
Exit For
End If
Next
If ColumnFound Then
'Initialize Interior color to nothing
'and remove values from output column
RangeInput.Interior.ColorIndex = 0
RangeOutput.ClearContents
'Change values and formatting
For i = 1 To RangeInput.Rows.Count
fullzipcode = RangeInput.Cells(i, 1).Value
If Len(fullzipcode) > 5 Then
RangeInput.Cells(i, 1).Interior.Color = RGB(255, 0, 0)
RangeInput.Cells(i, 1).Value = Left(fullzipcode, 5)
End If
RangeOutput.Cells(i, 1).Value = fullzipcode
Next
End If
End Sub
Faster but Less Intuitive
'The harder way, but faster
Sub TrimRange2()
Dim InputWorksheet As Worksheet, OutputWorksheet As Worksheet
Dim RangeInput As Range, RangeOutput As Range, Column As Range
Dim HeaderRow As Integer, ColumnNumber As Integer, LastRow As Integer, i As Integer
Dim InputValues() As Variant, OutputValues() As Variant
Dim OutputColumn As Range
Dim ColumnFound As Boolean
Dim fullzipcode As String
Set InputWorksheet = Worksheets("Sheet2")
Set OutputWorksheet = Worksheets("Sheet3")
HeaderRow = 1
'Get Input and Output Range
ColumnNumber = 0
ColumnFound = False
For Each Column In InputWorksheet.Columns
ColumnNumber = ColumnNumber + 1
If Column.Cells(HeaderRow, 1) = "postalcode" Then
LastRow = Column.End(xlDown).Row
'I assume the Output column will be in the same position as the input column
Set OutputColumn = OutputWorksheet.Columns(ColumnNumber)
'If OutputColumn is always in Column 'D' then replace previous line with:
'Set OutputColumn = OutputWorksheet.Columns(4)
Set RangeInput = InputWorksheet.Range(Column.Cells(HeaderRow + 1, 1), Column.Cells(LastRow, 1))
Set RangeOutput = OutputWorksheet.Range(OutputColumn.Cells(HeaderRow + 1, 1), OutputColumn.Cells(LastRow, 1))
ColumnFound = True
Exit For
End If
Next
If ColumnFound Then
'Initialize Interior color to nothing
'and remove values from output column
RangeInput.Interior.ColorIndex = 0
RangeOutput.ClearContents
'Initialize Arrays (much faster than working with ranges)
InputValues = RangeInput.Value2
OutputValues = RangeOutput.Value2
'Change values and formatting
For i = 1 To RangeInput.Rows.Count
fullzipcode = InputValues(i, 1)
If Len(fullzipcode) > 5 Then
RangeInput.Cells(i, 1).Interior.Color = RGB(255, 0, 0)
InputValues(i, 1) = Left(fullzipcode, 5)
End If
OutputValues(i, 1) = fullzipcode
Next
'Save arrays to ranges
RangeInput.Value2 = InputValues
RangeOutput.Value2 = OutputValues
End If
End Sub
What change do i need to do in the below code so that the entire row is copied into a defined number of rows and not just the first column?
Sub InsertSessions()
Dim Rng As Long
Dim k As Long
Dim rRange As Range
Set rRange = Selection
ActiveCell.EntireRow.Select
Rng = InputBox("Enter number of sessions:.")
For k = 1 To Rng
Rows(rRange.Row).Insert Shift:=xlDown, _
CopyOrigin:=xlFormatFromLeftOrAbove
Call rRange.Copy(Range(Cells(rRange.Row - 1, rRange.Column), Cells(rRange.Row - 1, rRange.Column)))
Next k
End Sub
this should work with no loop needed:
Option Explicit
Sub InsertSessions()
Dim rRange As Range
Set rRange = Selection.EntireRow
Dim Rng As Long
Rng = InputBox("Enter number of sessions:.") * rRange.Rows.Count
With ActiveSheet
Dim StartRng As Range
Set StartRng = .Cells(rRange.Cells(rRange.Rows.Count, 1).Offset(1), 1)
StartRng.Resize(Rng).Insert xlDown
rRange.Copy .Range(.Cells(StartRng, 1), .Cells(StartRng.Offset(Rng - 1), 1))
End With
End Sub