Properly looping through non-contiguous ranges? - excel

I have a few non-contiguous ranges that may vary in size each time it is run. I would like to take each of the ranges and copy and paste them onto their own individual worksheets (one range per sheet).
My code currently works for the first range and sheet. After the second sheet is created, the ranges are highlighted, but the first range is again copied and pasted onto the second sheet, instead of the corresponding second range. Then, the third sheet is created, but again, only the first range is copied and pasted onto this sheet. I know something is wrong with my looping, but I can't figure out where.
I have exhausted all of my resources. I just can't figure out why the loop isn't getting to the other 2 ranges.
'Get current sheet name
Dim activeSheetName As String
activeSheetName = ActiveSheet.Name
'Create a new sheet to reformat existing data
Dim newSheetName As String
newSheetName = (activeSheetName + "_Data")
Dim filterRange As range
Dim areasCount As Integer
For Each a In filterRange.Areas
Sheets(newSheetName).Select
filterRange.Select
range(Selection, Selection.End(xlToRight)).Select
areasCount = Selection.Areas.Count
With a
For i = 2 To areasCount + 1
Selection.Copy
With Sheets.Add(After:=Sheets(Sheets.Count))
.Name = a.Cells(1, 1).Value
.range("A1").Value = a.Offset(, 1)
range("A50").Select
Selection.PasteSpecial paste:=xlPasteAll, Operation:=xlNone, _
SkipBlanks:= False, Transpose:=False
Application.CutCopyMode = False
End With
Next i
End With
Next a
I have tried to incorporate the following code I found in a book, but no such luck.
Dim SelAreas() As range
Dim pasteRange As range
Dim upperLeft As range
Dim numAreas As Long, i As Long
Dim topRow As Long, leftCol As Long
Dim rowOffset As Long, colOffset As Long
If TypeName(Selection) <> "Range" Then Exit Function
numAreas = Selection.Areas.Count
ReDim SelAreas(1 To numAreas)
For i = 1 To numAreas
Set SelAreas(i) = Selection.Areas(i)
Next
topRow = ActiveSheet.Rows.Count
leftCol = ActiveSheet.Columns.Count
For i = 1 To numAreas
If SelAreas(i).Row < topRow Then topRow = SelAreas(i).Row
If SelAreas(i).Column < leftCol Then leftCol = SelAreas(i).Column
Next
Set upperLeft = Cells(topRow, leftCol)
On Error Resume Next
Set pasteRange = range("A50")
On Error GoTo 0
If TypeName(pasteRange) <> "Range" Then Exit Function
Set pasteRange = pasteRange.range("A1")
For i = 1 To numAreas
rowOffset = SelAreas(i).Row - topRow
colOffset = SelAreas(i).Column - leftCol
SelAreas(i).Copy
range("A1").Value = pasteRange.Offset(rowOffset, colOffset)
Next i

For Each a In filterRange.Areas
Sheets(newSheetName).Select
range(a, a.End(xlToRight)).Copy
With a
If filterRange Is Nothing Then
MsgBox ("Value not present in this workbook.")
Else
With Sheets.Add(After:=Sheets(Sheets.Count))
.Name = a.Cells(1, 1).Value
.range("A1").Value = a.Offset(, 1)
range("A50").Select
ActiveSheet.paste
End With
range("A10:A49").Select
range(Selection, Selection.End(xlToRight)).Select
Selection.Delete
range("A1").Select
End If
End With
Next a

Related

Transpose VBA Excel Macro based on condition

I am trying to copy & transpose values from one Sheet to another Sheet based on a condition, only transpose the first 4 lines looping in large range.
From this:
To this :
I've found a transpose macro and adapt it but I couldn't apply the condition.
Sub Test()
Set rng = Range("B5", Range("B5").End(xlDown))
Sheets("Example #2").Range(rng).Value = WorksheetFunction.Transpose()
EndSub
Anyone can guide me? Any help would be greatly appreciated!
Please, test the next code. It uses arrays, works in memory and will be much faster than copying. This can be easier observed on a large range:
Sub CopyTranspose4rows()
Dim sh1 As Worksheet, sh2 As Worksheet, lastR As Long, arr, arrSl, i As Long
Set sh1 = ActiveSheet 'use here the sheet you need to copy from
Set sh2 = sh1.Next 'use here what sheet you need to paste
lastR = sh1.Range("B" & sh1.rows.count).End(xlUp).row 'last row sh1
arr = sh1.Range("B5:B" & lastR).Value 'put the range in an array for fast iteration
For i = 1 To UBound(arr) Step 4 'iterate from four to four
With Application
'create a slice array
arrSl = .Transpose(.Index(arr, Evaluate("row(" & i & ":" & i + 4 & ")"), 1))
End With
'drop the slice array content in the second sheet
sh2.Range("A" & sh2.rows.count).End(xlUp).Offset(1).Resize(1, 4).Value = arrSl
Next i
sh2.Activate 'activate the sheet where pasted
End Sub
#FaneDuru's array solution is more elegant, but here's another alternative. You would need to replace the sheet names and the starting cell numbers.
Sub TestTranspose()
Dim sht1 As Worksheet
Dim sht2 As Worksheet
Dim LR1 As Long
Dim x As Long
Set sht1 = ThisWorkbook.Worksheets("Sheet1")
Set sht2 = ThisWorkbook.Worksheets("Sheet2")
LR1 = sht1.Cells(Rows.Count, 2).End(xlUp).Row
y = 1
For x = 1 To LR Step 4
sht1.Range(sht1.Cells(x, 2), sht1.Cells(x + 3, 2)).Copy
sht2.Cells(y, 1).PasteSpecial Paste:=xlPasteAll, Transpose:=True
y = y + 1
Next x
End Sub
With this code you can have different number of answers per question.
Sub Tranpose_Questions()
Dim fnd As String, FirstFound As String
Dim FoundCell As Range, rng As Range
Dim myRange As Range, LastCell As Range
Dim transRng As Range ' range to transpose
Dim dstRng As Range: Set dstRng = ActiveSheet.Range("C1") ' destination cell
' Value find
fnd = "Question"
Set myRange = ActiveSheet.Range("A1", Range("A1").End(xlDown))
Set LastCell = myRange.Cells(myRange.Cells.Count)
Set FoundCell = myRange.Find(what:=fnd, after:=LastCell)
' Test to see if anything was found
If Not FoundCell Is Nothing Then
FirstFound = FoundCell.Address
Else
GoTo errHandler
End If
Set rng = FoundCell
' Loop
Do Until FoundCell Is Nothing
' Find next cell
Set FoundCell = myRange.FindNext(after:=FoundCell)
Debug.Print rng.Address, FoundCell.Address
' Test to see if cycled through to first found cell
If FoundCell.Address = FirstFound Then
Set transRng = Range(rng, rng.End(xlDown))
If rng.Offset(1, 0) <> "" Then
transRng.Select: transRng.Copy
dstRng.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks:=False, Transpose:=True
Application.CutCopyMode = False
End If
Exit Do
End If
' Transpose
Set transRng = rng.Resize(FoundCell.Row - rng.Row, 1)
transRng.Copy
dstRng.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks:=False, Transpose:=True
Application.CutCopyMode = False
' update rng position
Set rng = FoundCell
' update destination range
Set dstRng = dstRng.Offset(1, 0)
Loop
Exit Sub
' Error Handler
errHandler:
MsgBox "No 'Question' found!"
End Sub

How to store text and post in above rows in specific column if condition is met?

I'm writing a code to look for a specific keyword ("Team") and when found I want to paste the team name in a specific column ("D") for all rows above. If the keyword is not found I want to copy the entire row. This all pasted into a new sheet.
What I have:
x-------------x------------x
x-------------x------------x
Team A----x------------x
x-------------x-------------x
x-------------x-------------x
Team B----x-------------x
What I want:
x----x----x----A
x----x----x----A
x----x----x----B
x----x----x----B
Here's what I have so far:
Sub fun()
Dim j as Integer
Dim lastrow as Integer
Dim team as String
Dim sh As Worksheet
sh = Sheets("Sheet 1")
lastrow = ActiveSheet.Cells(Rows.Count, 1).End(xlup).Row
Range("A" & lastrow).Select
for j = 1 to lastrow
If Instr(Cells(j,1).Value, "Team") Then
Cells(j,1).Value = Replace(Cells(j,1).Value, "Team ", "")
Cells(j,1).Value = team
Else
Range(Cells(j,1), Cells(j,3). Select
Selection.Copy
Windows("sheet.xlsm").Activate
ActiveSheet.Cells(1,1).Select
Selection.PasteSpecial Paste:=xlPasteValues, Transpose:=False
End If
next j
End Sub
I'm able to meet the second condition and paste entire rows but I'm unsure how to copy the team names and post them in column D in the new sheet.
Something like this:
Sub fun()
Dim j As Long, destRow As Long
Dim team As String, v, rngTeam As Range
Dim sh As Worksheet, shDest As Worksheet
Set sh = Sheets("Sheet1")
Set shDest = Sheets("Sheet2") 'for example
destRow = shDest.Cells(Rows.Count, 1).End(xlUp).Row + 1
For j = 1 To sh.Cells(Rows.Count, 1).End(xlUp).Row
v = sh.Cells(j, 1).Value
If InStr(v, "Team") > 0 Then
If Not rngTeam Is Nothing Then rngTeam.Value = Replace(v, "Team ", "") '<< set for already-copied rows
Set rngTeam = Nothing 'reset the range
Else
shDest.Cells(destRow, 1).Resize(1, 3).Value = sh.Cells(j, 1).Resize(1, 3).Value
'add to the range to populate next time we hit a "Team"
If rngTeam Is Nothing Then
Set rngTeam = shDest.Cells(destRow, 4)
Else
Set rngTeam = Application.Union(shDest.Cells(destRow, 4), rngTeam)
End If
destRow = destRow + 1
End If
Next j
End Sub

How to make a "copy-paste" macro run faster?

I have written a macro in Excel VBA that basically copy-pastes 53 rows 1440 times, one under another, in order to populate two columns in a ~70000 row table. The macro works, but it takes about five minutes to run completely. This would be fine if I didn't have to run this on ~1000 other files. I am looking for any way to speed up this process so that it doesn't take 5 days to run.
I tried using the range copy method:
Set range1 = {the table I'm copying}
Set range2 = {the cells I want to paste into}
range1.Copy range2
but it took just as long, if not longer.
Here is my current code:
Windows("as_built_comp.xlsm").Activate
Sheets(siteName).Activate
j = Cells(Rows.Count, 1).End(xlUp).Row
Range("C2").Select
Range(Selection, Selection.End(xlToRight)).Select
Range(Selection, Selection.End(xlDown)).Select
Selection.Copy
wb.Activate
Range("I12").Select
For i = 1 To 1440
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone,
SkipBlanks _
:=False, Transpose:=False
ActiveWindow.SmallScroll Down:=56
ActiveCell.Offset(j - 1, 0).Select
Next i
I'm thinking the solution might have something to do with using sql in VBA, but I have yet to learn that syntax. Either way, any advice is greatly appreciated. Thank you for reading!
Load it all into an array and then output the entire array at the end. Code refactored to avoid the use of activate/select
Sub tgr()
Dim wbDest As Workbook
Dim wbData As Workbook
Dim wsDest As Worksheet
Dim wsData As Worksheet
Dim aTemp() As Variant
Dim aData() As Variant
Dim SiteName As String
Dim RepeatData As Long
Dim ixTemp As Long
Dim ixData As Long
Dim ixCol As Long
SiteName = "SiteName1"
RepeatData = 1440
Set wbDest = ThisWorkbook
Set wbData = Workbooks("as_built_comp.xlsm")
Set wsDest = wbDest.Worksheets(1)
Set wsData = wbData.Worksheets(SiteName)
With wsData.Range("C2:D" & wsData.Cells(wsData.Rows.Count, "C").End(xlUp).Row)
If .Row < 2 Then Exit Sub 'No data
aTemp = .Value
ReDim aData(1 To .Rows.Count * RepeatData, 1 To .Columns.Count)
End With
For ixData = 1 To UBound(aData, 1)
ixTemp = ((ixData - 1) Mod UBound(aTemp, 1)) + 1
For ixCol = 1 To UBound(aTemp, 2)
aData(ixData, ixCol) = aTemp(ixTemp, ixCol)
Next ixCol
Next ixData
wsDest.Range("I12").Resize(UBound(aData, 1), UBound(aData, 2)).Value = aData
End Sub

Compile error: invalid qualifier when looping through worksheets

I am trying to copy cells from several worksheets to a summary worksheet if their date (held in col. G) falls within a given range. I want the macro to loop through column g in each sheet and pull in the information where there is a match before moving on to the next worksheet to do the same. Currently my code presents a compile error: Invalid qualifier for the x value within rng...I an new to VBA and can't see what I have done wrong.
Sub Copy_ProjectSummaryData()
Dim i As Integer
Dim ws_num As Integer
Dim rng As Range, destRow As Long
Dim starting_ws As Worksheet
Dim shtDest As Worksheet
Dim c As Range
Dim startdate As Date
Dim enddate As Date
Set starting_ws = ThisWorkbook.Worksheets(1) 'remember which worksheet is
active in the beginning
ws_num = ThisWorkbook.Worksheets.Count
Set shtDest = Sheets("Summary")
destRow = 4 'start copying to this row
destRow2 = 4 'start copying to this row
destRow3 = 4 'start copying to this row
destRow4 = 4 'start copying to this row
startdate = CDate(InputBox("Begining Date"))
enddate = CDate(InputBox("End Date"))
'Clear contents from sheet before running new report
Range("A4").Select
Range(Selection, ActiveCell.SpecialCells(xlLastCell)).Select
Selection.ClearContents
'Find and pull in Escalated Risks within the date range for the report
For i = 1 To ws_num
ThisWorkbook.Worksheets(i).Activate
Set rng =
Application.Intersect(ThisWorkbook.Worksheets(i).Range("G16:G20"),
ThisWorkbook.Worksheets(i).UsedRange)
For Each c In rng.Cells
If c.Value >= startdate And c.Value <= enddate Then
'Starting 6 cells to the left of c (col A),
' copy an 8-cell wide block to the other sheet,
' pasting it in Col B on row destRow
c.Offset(0, -6).Resize(1, 8).Copy _
shtDest.Cells(destRow, 2)
destRow = destRow + 1
End If
Next
Next
'Find and paste Risk Project Name
For i = 1 To ws_num
ThisWorkbook.Worksheets(i).Activate
Set rng = Application.Intersect(ThisWorkbook.Worksheets(i).Range("G16:G20"),
ThisWorkbook.Worksheets(i).UsedRange)
For Each c In rng.Cells
If c.Value >= startdate And c.Value <= enddate Then
' copy C3 to the other sheet,
' pasting it in Col A on row destRow
Range("C3").Copy _
shtDest.Cells(destRow2, 1)
destRow2 = destRow2 + 1
End If
Next
Next
'Find and pull in New Issues within the date range for the report
For i = 1 To ws_num
ThisWorkbook.Worksheets(i).Activate
Set rng =
Application.Intersect(ThisWorkbook.Worksheets(i).Range("G22:G26"),
ThisWorkbook.Worksheets(i).UsedRange)
For Each c In rng.Cells
If c.Value >= startdate And c.Value <= enddate Then
'Starting 6 cells to the left of c (col A),
' copy an 8-cell wide block to the other sheet,
' pasting it in Col B on row destRow
c.Offset(0, -6).Resize(1, 8).Copy _
shtDest.Cells(destRow3, 11)
destRow3 = destRow3 + 1
End If
Next
Next
'Find and paste Issues Project Name
For i = 1 To ws_num
ThisWorkbook.Worksheets(i).Activate
Set rng =
Application.Intersect(ThisWorkbook.Worksheets(i).Range("G22:G26"),
ThisWorkbook.Worksheets(i).UsedRange)
For Each c In rng.Cells
If c.Value >= startdate And c.Value <= enddate Then
' copy C3 to the other sheet,
' pasting it in Col A on row destRow
Range("C3").Copy _
shtDest.Cells(destRow4, 10)
destRow4 = destRow4 + 1
End If
Next
Next
starting_ws.Activate 'activate the worksheet that was originally active
Range("B4").Select
Selection.Copy
Range("A4").Select
Range(Selection, Selection.End(xlDown)).Select
Selection.PasteSpecial Paste:=xlPasteFormats, Operation:=xlNone, _
SkipBlanks:=False, Transpose:=False
Range("K4").Select
Selection.Copy
Range("J4").Select
Range(Selection, Selection.End(xlDown)).Select
Selection.PasteSpecial Paste:=xlPasteFormats, Operation:=xlNone, _
SkipBlanks:=False, Transpose:=False
End Sub
You have declared X as a Long. A Long does not have ranges. You should be using Sheets(X) rather than just X:
Set rng = Application.Intersect(Sheets(x).Range("G:G"), Sheets(x).UsedRange)

VBA to select specific number of rows on filtered range

I have a macro that filters a range, and I have a range of values which I want to represent the number of rows being selected after the filter is applied.
I have most of the code sorted, im just getting stuck on selecting the visible rows only.
EG. Sheet 1 contains variable numbers (1, 2, 3 ,4 etc) which I have labelled as NOC1.
Now once the filter is applied it selects the correct number of rows, but also selects hidden cells. I just want it to select the visible cells only.
Here is the code:
Set TopVisibleCell = Rstatus.Offset(1).Rows.SpecialCells(xlCellTypeVisible).Rows(1)
TopVisibleCell.Select
Selection.Resize(Selection.Rows.Count + NOC1 - 1, _
Selection.Columns.Count).Copy
Any help would be greatly appreciated.
Thanks!
Edit:
Please excuse my poor description, it seems I didnt express myself clearly.
Please find link to Sample.xlsm which will hopefully shed some light on my problem.
Link : Sample Workbook
Thanks for your help
you can loop with a counter:
Sub FilterCDA()
Dim sh1 As Worksheet
Dim N As Long
Dim TopVisibleCell As Range
Dim sh2 As Worksheet
Dim HeaderRow As Long
Dim LastFilterRow As Long
Dim st As String
Dim rng1 As Range
Dim rng2 As Range
Dim rng3 As Range
Dim VTR As String
Dim W As Integer
Dim R As Integer
Dim NOC As Range
Dim NOC1 As Integer
Dim rSelect As Range
Dim rCell As Range
Set sh1 = Sheets("Request")
Set sh2 = Sheets("Request")
C = 2
Set NOC = sh2.Range("D2")
NOC1 = NOC.Value
LR = Worksheets("ORT").Range("A" & Rows.Count).End(xlUp).Row
Set Rstatus1 = Worksheets("ORT").Range("G2:G" & LR)
Set Rstatus = Worksheets("ORT").Range("A1:G" & LR)
N = sh1.Cells(Rows.Count, "C").End(xlUp).Row
Sheets("CSV").Cells.NumberFormat = "#"
For i = 2 To N
v = sh1.Cells(i, 3).Value
If v <> "" Then
st = st & v & ","
End If
Next i
st = Mid(st, 1, Len(st) - 1)
Arr1 = Split(st, ",")
Sheets("ORT").Activate
For i = LBound(Arr1) To UBound(Arr1)
Sheets("ORT").AutoFilterMode = False
With Sheets("ORT").Range("A:G")
.AutoFilter Field:=3, Criteria1:=Arr1(i), Operator:=xlFilterValues
End With
Fr = Worksheets("ORT").Range("C" & Rows.Count).End(xlUp).Row - 1
' No rows filtered then Fr = 0
If Fr > 0 Then
With Rstatus
Set rVis = .Resize(.Rows.Count - 1, 1).Offset(1).SpecialCells(xlCellTypeVisible)
End With
For Each rCell In rVis.Cells
If rSelect Is Nothing Then
Set rSelect = rCell.Resize(, Rstatus.Columns.Count)
Else
Set rSelect = Union(rSelect, rCell.Resize(, Rstatus.Columns.Count))
End If
lCounter = lCounter + 1
If lCounter >= NOC1 Then Exit For
Next rCell
rSelect.Copy
Sheets("CSV").Cells(Rows.Count, "A").End(xlUp).Offset(1).PasteSpecial xlPasteValues
ElseIf Fr = 0 Then
End If
Set NOC = NOC.Offset(1)
NOC1 = NOC.Value
Next i
Sheets("ORT").AutoFilterMode = False
Sheets("Request").Select
Range("E2").Select
ActiveCell.FormulaR1C1 = "=COUNTIF('CSV'!C[-2],'Request'!RC[-2])"
On Error Resume Next
Selection.AutoFill Destination:=Range("E2:E" & Range("C" & Rows.Count).End(xlUp).Row), Type:=xlFillCopy
Columns("E:E").Select
Selection.Copy
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
Range("A1").Select
Sheets("Control").Select
Range("A1").Select
End Sub
If row #1 is the header row and you want to select the visible range of the AutoFilter and there is no "junk" below the filter in column A then:
Sub SelectVisibleA()
Dim NLastVisible As Long, r As Range
NLastVisible = Cells(Rows.Count, "A").End(xlUp).Row
Set r = Range("A2:A" & NLastVisible).Cells.SpecialCells(xlCellTypeVisible)
r.Select
End Sub
will select the visible material in column A...........you need to RESIZE to get additional columns.

Resources