Reputation: 131
I think i am staring at it too much, but i can't seem to figure out what I did wrong. I have a page with 3 different lists on it like this:
what i am trying to do is make a loop that looks at how much items are on the list, and then copies each line on a separate worksheet. so sheet 2 has the data of B2, C2, D2 & E2, sheet 3 has B3, C3, D3 & E3, etcetra.
here is my code:
Sub testLoopCustom()
Dim i As Long
Dim ii As Long
Dim LastRow As Long
Dim wb As Workbook
Dim wb1 As Worksheet
Dim sht2 As Worksheet
Dim sht3 As Worksheet
Dim sht4 As Worksheet
Dim sht5 As Worksheet
Set wb = ThisWorkbook
Set wb1 = wb.Sheets("source")
Set sht2 = wb.Sheets("sheet2")
Set sht3 = wb.Sheets("Sheet3")
Set sht4 = wb.Sheets("Sheet4")
Set sht5 = wb.Sheets("Sheet5")
'Find the last row (in column A) with data.
LastRow = wb1.Range("B:B").Find("*", searchdirection:=xlPrevious).Row
i = 2
'This is the beginning of the loop
For i = 2 To LastRow
'First sheet
sht2.Range("A2") = wb1.Range("B" & i).Value
sht2.Range("B2") = wb1.Range("C" & i).Value
sht2.Range("C2") = wb1.Range("D" & i).Value
sht2.Range("D2") = wb1.Range("E" & i).Value
i = i + 1
'Second sheet
sht3.Range("A2") = wb1.Range("B" & i).Value
sht3.Range("B2") = wb1.Range("C" & i).Value
sht3.Range("C2") = wb1.Range("D" & i).Value
sht3.Range("D2") = wb1.Range("E" & i).Value
i = i + 1
'Third sheet
sht4.Range("A2") = wb1.Range("B" & i).Value
sht4.Range("B2") = wb1.Range("C" & i).Value
sht4.Range("C2") = wb1.Range("D" & i).Value
sht4.Range("D2") = wb1.Range("E" & i).Value
i = i + 1
'Second sheet
sht5.Range("A2") = wb1.Range("B" & i).Value
sht5.Range("B2") = wb1.Range("C" & i).Value
sht5.Range("C2") = wb1.Range("D" & i).Value
sht5.Range("D2") = wb1.Range("E" & i).Value
i = i + 1
Next i
End Sub
the annoying part is that it worked before i changed "something" and now it doesn't anymore... It now only copies the last line into the first sheet.
Can anyone see my mistake? and bonus question: can the loop be simplyfied so that it automatically goes to the next sheet as well?
Upvotes: 0
Views: 282
Reputation: 20302
You should probably take this approach.
The range for the code example below looks like this
Column A : Header in A1 = Country, A2:A? = Country names
Column B : Header in B1 = Name, B2:B? = Names
Column C : Header in C1 = Gender, C2:C? = F or M
Column D : Header in D1 = Birthday, D2:D? = Dates
1: Set filter range on ActiveSheet: A1 is the top left cell of your filter range and the header of the first column, D is the last column in the filter range. You can also add the sheet name to the code like this : Worksheets("Sheet1").Range("A1:D" & LastRow(Worksheets("Sheet1"))) No need that the sheet is active then when you run the macro when you use this. Set My_Range = Range("A1:D" & LastRow(ActiveSheet))
2: Filter and set the filter field and the filter criteria:This example filter on the first column in the range (change the field if needed). In this case the range starts in A so Field 1 is column A, 2 = column B, ...... Use "<>Netherlands" as criteria if you want the opposite My_Range.AutoFilter Field:=1, Criteria1:="=Netherlands"
3:Important:This macro call a function named LastRow You find this function below the macro, copy this function together with the macro in a standard module
In the code you see four filter examples that you can use, we use example 1 in this macro and I commented the other 3 examples in the code. 1: Criteria in the code (=Netherlands, see the tips below the macro) 2: Filter on ActiveCell value 3: Filter on Range value (D1 in this example) 4: Filter on InputBox value
Sub Copy_With_AutoFilter1()
'Note: This macro use the function LastRow
Dim My_Range As Range
Dim CalcMode As Long
Dim ViewMode As Long
Dim FilterCriteria As String
Dim CCount As Long
Dim WSNew As Worksheet
Dim sheetName As String
Dim rng As Range
'Set filter range on ActiveSheet: A1 is the top left cell of your filter range
'and the header of the first column, D is the last column in the filter range.
'You can also add the sheet name to the code like this :
'Worksheets("Sheet1").Range("A1:D" & LastRow(Worksheets("Sheet1")))
'No need that the sheet is active then when you run the macro when you use this.
Set My_Range = Range("A1:D" & LastRow(ActiveSheet))
My_Range.Parent.Select
If ActiveWorkbook.ProtectStructure = True Or _
My_Range.Parent.ProtectContents = True Then
MsgBox "Sorry, not working when the workbook or worksheet is protected", _
vbOKOnly, "Copy to new worksheet"
Exit Sub
End If
'Change ScreenUpdating, Calculation, EnableEvents, ....
With Application
CalcMode = .Calculation
.Calculation = xlCalculationManual
.ScreenUpdating = False
.EnableEvents = False
End With
ViewMode = ActiveWindow.View
ActiveWindow.View = xlNormalView
ActiveSheet.DisplayPageBreaks = False
'Firstly, remove the AutoFilter
My_Range.Parent.AutoFilterMode = False
'Filter and set the filter field and the filter criteria :
'This example filter on the first column in the range (change the field if needed)
'In this case the range starts in A so Field 1 is column A, 2 = column B, ......
'Use "<>Netherlands" as criteria if you want the opposite
My_Range.AutoFilter Field:=1, Criteria1:="=Netherlands"
'If you want to filter on a cell value you can use this, use "<>" for the opposite
'This example uses the activecell value
'My_Range.AutoFilter Field:=1, Criteria1:="=" & ActiveCell.Value
'This will use the cell value from A2 as criteria
'My_Range.AutoFilter Field:=1, Criteria1:="=" & Range("A2").Value
''If you want to filter on a Inputbox value use this
'FilterCriteria = InputBox("What text do you want to filter on?", _
' "Enter the filter item.")
'My_Range.AutoFilter Field:=1, Criteria1:="=" & FilterCriteria
'Check if there are not more then 8192 areas(limit of areas that Excel can copy)
CCount = 0
On Error Resume Next
CCount = My_Range.Columns(1).SpecialCells(xlCellTypeVisible).Areas(1).Cells.Count
On Error GoTo 0
If CCount = 0 Then
MsgBox "There are more than 8192 areas:" _
& vbNewLine & "It is not possible to copy the visible data." _
& vbNewLine & "Tip: Sort your data before you use this macro.", _
vbOKOnly, "Copy to worksheet"
Else
'Add a new Worksheet
Set WSNew = Worksheets.Add(After:=Sheets(ActiveSheet.Index))
'Ask for the Worksheet name
sheetName = InputBox("What is the name of the new worksheet?", _
"Name the New Sheet")
On Error Resume Next
WSNew.Name = sheetName
If Err.Number > 0 Then
MsgBox "Change the name of sheet : " & WSNew.Name & _
" manually after the macro is ready. The sheet name" & _
" you fill in already exists or you use characters" & _
" that are not allowed in a sheet name."
Err.Clear
End If
On Error GoTo 0
'Copy/paste the visible data to the new worksheet
My_Range.Parent.AutoFilter.Range.Copy
With WSNew.Range("A1")
' Paste:=8 will copy the columnwidth in Excel 2000 and higher
' Remove this line if you use Excel 97
.PasteSpecial Paste:=8
.PasteSpecial xlPasteValues
.PasteSpecial xlPasteFormats
Application.CutCopyMode = False
.Select
End With
' If you want to delete the rows that you copy, also use this
' With My_Range.Parent.AutoFilter.Range
' On Error Resume Next
' Set rng = .Offset(1, 0).Resize(.Rows.Count - 1, .Columns.Count) _
' .SpecialCells(xlCellTypeVisible)
' On Error GoTo 0
' If Not rng Is Nothing Then rng.EntireRow.Delete
' End With
End If
'Close AutoFilter
My_Range.Parent.AutoFilterMode = False
'Restore ScreenUpdating, Calculation, EnableEvents, ....
My_Range.Parent.Select
ActiveWindow.View = ViewMode
If Not WSNew Is Nothing Then WSNew.Select
With Application
.ScreenUpdating = True
.EnableEvents = True
.Calculation = CalcMode
End With
End Sub
Function LastRow(sh As Worksheet)
On Error Resume Next
LastRow = sh.Cells.Find(What:="*", _
After:=sh.Range("A1"), _
Lookat:=xlPart, _
LookIn:=xlValues, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Row
On Error GoTo 0
End Function
Upvotes: 0
Reputation: 681
It looks to me as though when 'i' finally increments to equal 'LastRow', it will write the First Sheet with LastRow's data, increment past the value of 'LastRow' (i = i + 1) and attempt to write the remaining sheets with the blank cells that exist beyond the LastRow. Then the loop is exited because i > LastRow by 4.
Looks like you are trying to flatten the source worksheets data into separate sheets, one line each. Using a loop:
Dim workSht As Worksheet
For i = 2 To LastRow
Set workSht = wb.Sheets("Sheet" & i)
workSht.Range("A2") = wb1.Range("B" & i).Value
workSht.Range("B2") = wb1.Range("C" & i).Value
workSht.Range("C2") = wb1.Range("D" & i).Value
workSht.Range("D2") = wb1.Range("E" & i).Value
Next i
Upvotes: 1
Reputation: 26640
If all you are trying to do is copy each row to a new sheet, then this will work for you:
Sub tgr()
Dim wb As Workbook
Dim SourceWS As Worksheet
Dim Headers As Range
Dim SourceData As Range
Dim DataRow As Range
Set wb = ActiveWorkbook
Set SourceWS = wb.Sheets("Source")
Set Headers = SourceWS.Range("B1").CurrentRegion.Resize(1)
Set SourceData = SourceWS.Range("B2", SourceWS.Cells(SourceWS.Rows.Count, "B").End(xlUp))
If SourceData.Row < 2 Then Exit Sub 'No data
For Each DataRow In SourceData.Cells
With wb.Sheets.Add(After:=wb.Sheets(wb.Sheets.Count))
Headers.Copy
.Range("A1").PasteSpecial xlPasteAll
.Range("A1").PasteSpecial xlPasteColumnWidths
DataRow.Resize(, Headers.Columns.Count).Copy .Range("A2")
End With
Next DataRow
Application.CutCopyMode = False
End Sub
Upvotes: 0
Reputation: 43585
Try to do your code like this:
sht2.Range("A" & i) = wb1.Range("A" & i).Value
sht2.Range("B" & i) = wb1.Range("B" & i).Value
sht2.Range("C" & i) = wb1.Range("C" & i).Value
sht2.Range("D" & i) = wb1.Range("D" & i).Value
Thus on every sheet you would get a copy from wb1
. Another option is the usage of Offset()
like this:
sht2.Range("A2").Offset(i - 2, 0) = wb1.Range("A" & i).Value
sht2.Range("B2").Offset(i - 2, 0) = wb1.Range("B" & i).Value
sht2.Range("C2").Offset(i - 2, 0) = wb1.Range("C" & i).Value
depending on what exactly do you need and how do you feel more comfortable.
Upvotes: 0
Reputation: 27249
Try this:
For i = 2 to LastRow
Worksheets("Sheet" & i).Range("A2").Value = wb1.Range("B" & i).value
Worksheets("Sheet" & i).Range("B2").Value = wb1.Range("C" & i).value
Worksheets("Sheet" & i).Range("C2").Value = wb1.Range("D" & i).value
Worksheets("Sheet" & i).Range("D2").Value = wb1.Range("E" & i).value
Next
As you loop through the rows it will place each row onto the sheet with the corresponding row number in the name.
Upvotes: 0