I have a column of IDs in an Excel worksheet called Sheet1. I have data that corresponds to the IDs in columns to the right of Column A. The amount of cells in a row varies. For example:
A, B, C, D, E, F, ...
John, 5, 10, 15, 20
Jacob, 2, 3
Jingleheimmer, 5, 10, 11
I'm trying to copy that data into a new worksheet, Sheet5, in the following format:
A, B, C, D, E, F, ...
John, 5
John, 10
John, 15
John, 20
Jacob, 2
Jacob, 3
Jingleheimmer, 5
Jingleheimmer, 10
Jingleheimmer, 11
I wrote the following code that copies over the first two IDs. I could continue to copy paste the second half of the code and just change the cells, however, I have 100s of IDs. This would take too long. I think whenever a process is repeated I should be using a loop. Can you help me turn this repetitive code into a loop?
Sub Macro5()
Dim LastRowA As Integer
Dim LastRowB As Integer
''' Process of copying over first ID '''
'grab all data cells in B2 to the right
With Sheets("Sheet1").Select
Range("B2", Range("B2").End(xlToRight)).Select
Selection.Copy
End With
'paste that data into the first empty cell of Column B in Sheet5
With Sheets("Sheet5").Select
Range("B1").Select
Selection.PasteSpecial Paste:=xlPasteAll, Operation:=xlNone, SkipBlanks:= _
False, Transpose:=True
End With
'grab the corresponding ID in cell A2
With Sheets("Sheet1").Select
Range("A2").Select
Application.CutCopyMode = False
Selection.Copy
End With
'paste the corresponding ID into the first empty cell of Column A in Sheet5
With Sheets("Sheet5").Select
LastRowB = Cells(Rows.Count, "B").End(xlUp).Row
Range("A1").Select
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
Application.CutCopyMode = False
Selection.AutoFill Destination:=Range("A1:A" & LastRowB)
End With
''' Repeat that process for each row in Sheet1 '''
'grab all data cells in B3 to the right
With Sheets("Sheet1").Select
Range("B3", Range("B3").End(xlToRight)).Select
Selection.Copy
End With
'paste that data into the first empty cell of Column B in Sheet5
With Sheets("Sheet5").Select
LastRowB = Cells(Rows.Count, "B").End(xlUp).Row
Range("B" & LastRowB + 1).Select
Selection.PasteSpecial Paste:=xlPasteAll, Operation:=xlNone, SkipBlanks:= _
False, Transpose:=True
End With
'grab the corresponding ID in cell A3
With Sheets("Sheet1").Select
Range("A3").Select
Application.CutCopyMode = False
Selection.Copy
End With
'paste the corresponding ID into the first empty cell of column A in Sheet5
'and autofill down to the last populated cell in column B
With Sheets("Sheet5").Select
LastRowA = Cells(Rows.Count, "A").End(xlUp).Row + 1
Range("A" & LastRowB + 1).Select
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
Application.CutCopyMode = False
LastRowB = Cells(Rows.Count, "B").End(xlUp).Row
Selection.AutoFill Destination:=Range("A" & LastRowA & ":A" & LastRowB)
End With
End Sub
Try this:
Sub test()
Dim i As Integer
Dim j As Integer
Dim ws1 As Worksheet
Dim ws2 As Worksheet
Dim nRow As Integer
Dim lRow As Integer
Dim lCol As Integer
Set ws1 = Sheets("Sheet1")
Set ws2 = Sheets("Sheet5")
nRow = 1
With ws1
lRow = .Cells(.Rows.Count, 1).End(xlUp).Row
For i = 1 To lRow
lCol = .Cells(i, .Columns.Count).End(xlToLeft).Column
For j = 2 To lCol
ws2.Cells(nRow, 1).Value = .Cells(i, 1).Value
ws2.Cells(nRow, 2).Value = .Cells(i, j).Value
nRow = nRow + 1
Next j
Next i
End With
End Sub
It runs through each row in the sheet one at a time, copying over the names and associated numbers up through the last column with values in that row. Should work very quickly and doesn't require constant copy & pasting.
This should do what you're looking for.
Sub test()
Dim lastrow As Long, lastcol As Long
Dim i As Integer, j as Integer, x as Integer
Dim ws1 As Worksheet
Dim ws2 As Worksheet
Set ws1 = Sheets("Sheet1")
Set ws2 = Sheets("Sheet5")
lastrow = ws1.Cells(Rows.Count, "A").End(xlUp).Row
x = 1
With ws1
For i = 1 To lastrow
lastcol = .Cells(i, .Columns.Count).End(xlToLeft).Column
For j = 2 To lastcol
ws2.Cells(x, 1).Value = .Cells(i, 1).Value
ws2.Cells(x, 2).Value = .Cells(i, j).Value
x = x + 1
Next j
Next i
End With
End Sub
Related
I am trying to transfer the data from sheet one to sheet two and combined the information on the second sheet. The code I have listed below works, but it seems very inefficient. I am trying to improve by VBA abilities and would love to here ways to shrink my code down, make it more efficient, and still achieve the same goal. Thanks for any help you can provide.
Sheet 1
Sheet 2
Sub batchorder()
Dim Pname As String
Dim Lplace As String
Dim numsld As Long
Dim rating As Integer
Dim lastrow As Long
Dim i As Long
Dim openc As Long
lastrow = Range("A" & Rows.Count).End(xlUp).Row
Range("A1").Select
For i = 1 To lastrow
If Cells(i, 1).Value <> "" Then
'Copy name to sheet 2
Cells(i, 1).Select
ActiveCell.Offset(0, 1).Select
Selection.Copy
Sheets("Sheet2").Select
Range("A1").Select
'Find the next open cell to paste to
Selection.End(xlDown).Select
Selection.End(xlDown).Select
Selection.End(xlUp).Select
ActiveCell.Offset(1, 0).Select
ActiveSheet.Paste
Sheets("Sheet1").Select
'Copy place to sheet 2
ActiveCell.Offset(1, 0).Select
Selection.Copy
Sheets("Sheet2").Select
Range("B1").Select
'Find the next open cell to paste to
Selection.End(xlDown).Select
Selection.End(xlDown).Select
Selection.End(xlUp).Select
ActiveCell.Offset(1, 0).Select
ActiveSheet.Paste
Sheets("Sheet1").Select
'Copy sold to sheet 2
ActiveCell.Offset(1, 0).Select
Selection.Copy
Sheets("Sheet2").Select
Range("C1").Select
'Find the next open cell to paste to
Selection.End(xlDown).Select
Selection.End(xlDown).Select
Selection.End(xlUp).Select
ActiveCell.Offset(1, 0).Select
ActiveSheet.Paste
Sheets("Sheet1").Select
'Copy rating to sheet 2
ActiveCell.Offset(1, 0).Select
Selection.Copy
Sheets("Sheet2").Select
Range("D1").Select
'Find the next open cell to paste to
Selection.End(xlDown).Select
Selection.End(xlDown).Select
Selection.End(xlUp).Select
ActiveCell.Offset(1, 0).Select
ActiveSheet.Paste
Sheets("Sheet1").Select
Sheets("Sheet1").Select
i = i + 3
Else
End If
Next i
End Sub
Sub batchorder()
Dim Row As Long
Dim i As Long
' These two lines speed up evrything ENORMOUSLY.
' But you need the lines at the end too
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Row = Sheet2.UsedRange.Rows.Count ' Row is nr of last row in sheet
While Application.CountA(Sheet2.Rows(Row)) = 0 And Row > 1
Row = Row - 1 ' skip empty rows at the end if present
Wend
For i = 1 To Sheet1.UsedRange.Rows.Count
If Sheet1.Cells(i, 1).Value <> "" Then
Sheet2.Cells(Row, 1).FormulaLocal = Sheet1.Cells(i, 2).FormulaLocal
Sheet2.Cells(Row, 2).FormulaLocal = Sheet1.Cells(i + 1, 2).FormulaLocal
Sheet2.Cells(Row, 3).FormulaLocal = Sheet1.Cells(i + 2, 2).FormulaLocal
Sheet2.Cells(Row, 4).FormulaLocal = Sheet1.Cells(i + 3, 2).FormulaLocal
i = i + 3
Row = Row + 1
End If
Next
' Restore Excel to human state.
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
End Sub
You should basically never use the select statement, it gets everything really messy quickly. Here's a basic combiner of mine. Just added the If statement to check whether the cell and in this case row is empty.
This should work but more importantly try to understand what it does to learn. I gave it some comments.
Sub batchorder()
Dim ws1 As Worksheet
Dim ws2 As Worksheet
' Just habits, but doing this here means that I won't have to write anything else than ws1 and ws2 in the future
Set ws1 = Worksheets("Sheet1")
Set ws2 = Worksheets("Sheet2")
Dim lastrowWs1 As Long
Dim j As Long
' first row after ws2 headers
j = 2
' With statement to make the code nicer also ".something" now means ws1.something
With ws1
' Bob Ulmas method -- just a personal preference to find the last row.
lastrowWs1 = .Cells.Find("*", searchorder:=xlByRows, searchdirection:=xlPrevious).Row
For i = 1 To lastrowWs1
' Check if the cell is not empty
If Not .Cells(i, 1) = vbNullString Then
'Basically range.value = other_range.value
ws2.Range(ws2.Cells(j, 1), ws2.Cells(j, 4)).Value = WorksheetFunction.Transpose(.Range(.Cells(i, 2), .Cells(i + 3, 2)).Value)
' step 3 forward as the amount of rows per record was 4
i = i + 3
' go to next row for worksheet 2
j = j + 1
End If
Next i
End With
End Sub
Straight to the point:
I am trying to match A2 on sheet "PRD" to A2 on sheet "CRD", if this is a match I want to compare B2 on sheet PRD to B2 on sheet CRD and then A3 same thing on and on to the end of the range. If there is no match between cells in column A I am trying to copy the whole row to a third sheet, if there is a match between cells in A but there is not a match between cells in B I am trying to copy the row to a third sheet.
I am stuck, I think after hours of looking at the code and Googling, not being able to check column B... I seem to be able to check, copy and paste cells that do not match contents in column A fine.
I hope I am asking the right questions and am clear, thanks for any help!!
Dim r1 As Range
Dim r2 As Range
Dim r3 As Range
Dim r4 As Range
Dim cell As Range
Dim cell2 As Range
Dim lastrow As Long
'CRD date
With ThisWorkbook.Worksheets("CRD")
lastrow = .Cells(.Rows.Count, "A").End(xlUp).Row
Set r1 = .Range("A2:A" & lastrow)
End With
'CRD quantity
With ThisWorkbook.Worksheets("CRD")
lastrow = .Cells(.Rows.Count, "B").End(xlUp).Row
Set r3 = .Range("B2:B" & lastrow)
End With
'PRD date
With ThisWorkbook.Worksheets("PRD")
lastrow = .Cells(.Rows.Count, "A").End(xlUp).Row
Set r2 = .Range("A2:A" & lastrow)
End With
'PRD quantity
With ThisWorkbook.Worksheets("PRD")
lastrow = .Cells(.Rows.Count, "B").End(xlUp).Row
Set r4 = .Range("B2:B" & lastrow)
End With
'match PRD date to CRD date: output "Found" for record, or copy/paste onto report page
Range("A2").Select
For Each cell In r1
If IsError(Application.Match(cell, r2, 0)) Then
'select active cell's row and copy, pasting in report page
Rows(ActiveCell.Row).Select
Selection.Copy
Sheets("Sheet1").Select
Range("A1").Select
Selection.End(xlDown).Select
ActiveCell.Offset(1, 0).Select
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
Sheets("CRD").Select
Application.CutCopyMode = False
'if no error check quantity(B) of same cell, if match continue, if no match copy
ElseIf IsError(Application.Match(r3, r4, 0)) Then
For Each cell2 In r3
Rows(ActiveCell.Row).Select
Selection.Copy
Sheets("Sheet1").Select
Range("A1").Select
Selection.End(xlDown).Select
ActiveCell.Offset(1, 0).Select
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
Sheets("CRD").Select
ActiveCell.Offset(rowOffset:=0, columnOffset:=1).Activate
Application.CutCopyMode = False
Next
Else
End If
ActiveCell.Offset(rowOffset:=1, columnOffset:=0).Activate
Next
End Sub
Your code relies too much on Select, ActiveCell, Selection and Activate, you should avoid all these Selecting and use fully qualified objects instead.
See the code below, and explanations inside the code's comments.
Modified Code
Option Explicit
Sub Match2Columns()
Dim r1 As Range
Dim r2 As Range
Dim r3 As Range
Dim r4 As Range
Dim cell As Range
Dim cell2 As Range
Dim lastrow As Long
'CRD date
With ThisWorkbook.Worksheets("CRD")
lastrow = .Cells(.Rows.Count, "A").End(xlUp).Row
Set r1 = .Range("A2:A" & lastrow)
End With
'CRD quantity
With ThisWorkbook.Worksheets("CRD")
lastrow = .Cells(.Rows.Count, "B").End(xlUp).Row
Set r3 = .Range("B2:B" & lastrow)
End With
'PRD date
With ThisWorkbook.Worksheets("PRD")
lastrow = .Cells(.Rows.Count, "A").End(xlUp).Row
Set r2 = .Range("A2:A" & lastrow)
End With
'PRD quantity
With ThisWorkbook.Worksheets("PRD")
lastrow = .Cells(.Rows.Count, "B").End(xlUp).Row
Set r4 = .Range("B2:B" & lastrow)
End With
Dim PasteRow As Long ' row to paste at "sheet1"
'match PRD date to CRD date: output "Found" for record, or copy/paste onto report page
With ThisWorkbook.Worksheets("CRD") ' <-- make sure you are looping and copying from "CRD" sheet
For Each cell In r1
If IsError(Application.Match(cell, r2, 0)) Then
' select active cell's row and copy, pasting in report page
.Rows(cell.Row).Copy
' get last empty row and add 1 row where to paste
PasteRow = Sheets("Sheet1").Range("A1").End(xlDown).Row + 1
' paste action
Sheets("Sheet1").Range("A" & PasteRow).PasteSpecial Paste:=xlPasteValues
Application.CutCopyMode = False
'if no error check quantity(B) of same cell, if match continue, if no match copy
ElseIf IsError(Application.Match(r3, r4, 0)) Then
For Each cell2 In r3
' select active cell's row and copy, pasting in report page
.Rows(cell2.Row).Copy
' get last empty row and add 1 row where to paste
PasteRow = Sheets("Sheet1").Range("A1").End(xlDown).Row + 1
' paste action
Sheets("Sheet1").Range("A" & PasteRow).PasteSpecial Paste:=xlPasteValues
Application.CutCopyMode = False
Next cell2
Else
' you are doing nothing here, not sure why you need it ???
End If
Next cell
End With
End Sub
I want to loop through 8 columns(A-H) in sheet 1 to make one new column in sheet 2. Then loop through 8 columns again(I-P) and make column B in sheet 2. I have do this for a lot of data and think this would be the best way to do it
here is my code
Range("E5").Select
Range(Selection, Selection.End(xlDown)).Select
Range("E5").Select
Range(Selection, Selection.End(xlDown)).Select
ActiveWindow.SmallScroll Down:=-96
Range("E5:E110").Select
Selection.Copy
Sheets("56 J").Select
Range("A2").Select
ActiveSheet.Paste
Sheets("56 g").Select
Range("F5").Select
Range(Selection, Selection.End(xlDown)).Select
Range("F5").Select
Range(Selection, Selection.End(xlDown)).Select
ActiveWindow.SmallScroll Down:=-96
Range("F5:F110").Select
Selection.Copy
Sheets("56 J").Select
Range("A110").Select
ActiveSheet.Paste
Sheets("56 g").Select
any idea how I can put this in something that loops through the columns?
This is an example of what I am trying to do.I would also like to have the time and letter stay with the corresponding data when it loops. But my main focus right now is just getting the data into a single column.
You should be able to loop through the source and destination columns with a little maths.
Dim c As Long, n As Long, tws As Worksheet
Set tws = Worksheets("56 j")
With Worksheets("56 g")
For n = 1 To 2
For c = 1 To 8
With .Range(.Cells(5, c + (n - 1) * 8), .Cells(.Rows.Count, c + (n - 1) * 8).End(xlUp))
tws.Cells(tws.Rows.Count, n).End(xlUp).Offset(1, 0).Resize(.Rows.Count, .Columns.Count) = .Value
End With
Next c
Next n
End With
Option Explicit
Sub copydata()
Dim WS1, WS2 As Worksheet
Dim lastrow As Long
Dim ws1Row, ws2Row As Long
Dim mycol As Integer
Set WS1 = Worksheets("sheet1")
Set WS2 = Worksheets("sheet2")
lastrow = WS1.Cells(WS1.Rows.Count, "A").End(xlUp).Row
ws2Row = 2
For ws1Row = 5 To lastrow
For mycol = 1 To 8
WS2.Cells(ws2Row, 1) = WS1.Cells(ws1Row, mycol)
WS2.Cells(ws2Row, 2) = WS1.Cells(ws1Row, mycol + 8)
ws2Row = ws2Row + 1
Next mycol
Next ws1Row
End Sub
I want to build a table on one Excel Sheet "Ship" by pulling data from another excel sheet "Efficiency." The row data on the "Efficiency" sheet is categorized by "Shipped", "Leave", "Import" and "Export".
Each category (shipped, leave, import, export) has several items and they're in no specific order. The table on the "Efficiency" sheet occupies columns A:H, and starts at row 2; the length can vary.
I want to be able to search the rows for "Shipped" and copy columns A, D:F and H of the matching rows and paste them beginning at cell B4 of the "Ship" sheet. Can anyone help me please?
Sub Ship()
ActiveSheet.Range("$A$1:$H$201").AutoFilter Field:=4, Criteria1:="Shipped"
' this is looking in a specific range, I want to make it more dynamic
Range("A4:A109").Select
'This is the range selected to copy, again I want to make this part more dynamic
Application.CutCopyMode = False
Selection.Copy
Range("A4:A109,D4:F109,H4:H109").Select
Range("G4").Activate
Application.CutCopyMode = False
Selection.Copy
Sheets("Ship").Select
Range("B4").Select
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
End Sub
This code has been tested based on your the information as given in your question:
Sub Ship()
Dim wsEff As Worksheet
Dim wsShip As Worksheet
Set wsEff = Worksheets("Efficiency")
Set wsShip = Worksheets("Shipped")
With wsEff
Dim lRow As Long
'make it dynamic by always finding last row with data
lRow = .Range("A" & .Rows.Count).End(xlUp).Row
'changed field to 2 based on your above comment that Shipped is in column B (the code you posted has 4).
.Range("A1:H" & lRow).AutoFilter Field:=2, Criteria1:="Shipped"
Dim rngCopy As Range
'only columns A, D:F, H
Set rngCopy = Union(.Columns("A"), .Columns("D:F"), .Columns("H"))
'filtered rows, not including header row - assumes row 1 is headers
Set rngCopy = Intersect(rngCopy, .Range("A1:H" & lRow), .Range("A1:H" & lRow).Offset(1)).SpecialCells(xlCellTypeVisible)
rngCopy.Copy
End With
wsShip.Range("B4").PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
End Sub
try the below code
Sub runthiscode()
Worksheets("Efficiency").Select
lastrow = Range("A" & Rows.Count).End(xlUp).Row
startingrow = 4
For i = 2 To lastrow
If Cells(i, 2) = "Shipped" Then
cella = Cells(i, 1)
celld = Cells(i, 4)
celle = Cells(i, 5)
cellf = Cells(i, 6)
cellh = Cells(i, 8)
Worksheets("Ship").Cells(startingrow, 2) = cella
Worksheets("Ship").Cells(startingrow, 5) = celld
Worksheets("Ship").Cells(startingrow, 6) = celle
Worksheets("Ship").Cells(startingrow, 7) = cellf
Worksheets("Ship").Cells(startingrow, 9) = cellh
startingrow = startingrow + 1
End If
Next i
End Sub
Thanks for reading my questions.
I have a table [ws1(A4:Q500)] contains data, while there are formula after column Q. Therefore I cannot copy the whole row but only certain range in text.
Column Q is the formula to define whether the data falls into period, i.e. 16/11-30/11 data. The flag is as follows:
0 < 16/11
1 = 16/11 - 30/11
2 > 30/11
Here the goal is to copy ws1 data with flag "1" to [ws2(A2:P200)]
And then delete ws1 data with flag "1" and "2"
Believe that the rules for copying and deleting is quite similar, I tried to do the copy parts first
Sub PlotGraph()
Dim i As Integer
Dim j As Integer
Dim lastrow As Integer
Dim ws1 As Worksheet: Set ws1 = ThisWorkbook.Sheets("Data")
Dim ws2 As Worksheet: Set ws2 = ThisWorkbook.Sheets("Analysis")
j = 2
lastrow = ws1.Cells(Rows.Count, 1).End(xlUp).Row
For i = 4 To lastrow
If ws1.Cells(i, 17) = 1 Then
ws1.Range(Cells(i, 1), Cells(i, 16)).Copy
ws2.Range(Cells(j, 1), Cells(j, 16)).PasteSpecial Paste:=xlPasteValues, _
Operation:=xlNone, _
SkipBlanks:=True, _
Transpose:=False
j = j + 1
End If
Next i
End Sub
The debug functions said its wrong in
ws1.Range(Cells(i, 1), Cells(i, 16)).Copy
I tried hard to do modifications but it stills not work, please help me a bit :( Thanks so much.
The ws2.Range(Cells(j, 1), Cells(j, 16)).PasteSpecial does not adequately reference the range as belonging to ws2. The Cells(...) within the range could belong to any worksheet; they have to specifically belong to ws2. The same goes for ws1.
ws1.Range(ws1.Cells(i, 1), ws1.Cells(i, 16)).Copy
ws2.Range(ws2.Cells(j, 1), ws2.Cells(j, 16)).PasteSpecial Paste:=xlPasteValues, _
Operation:=xlNone, _
SkipBlanks:=True, _
Transpose:=False
An AutoFilter Method may save you some time with a bulk value transfer.
Sub PlotGraph()
Dim i As Long, j As Long, lr As Long
Dim ws1 As Worksheet
Dim ws2 As Worksheet
Set ws1 = ThisWorkbook.Sheets("Data")
Set ws2 = ThisWorkbook.Sheets("Analysis")
j = 2
With ws1
lr = .Cells(Rows.Count, 1).End(xlUp).Row
With .Range(.Cells(3, 1), .Cells(lr, 17)) 'Range(A3:Q & lr) need header row for autofilter
.AutoFilter field:=17, Criteria1:=1
With .Resize(.Rows.Count - 1, 16).Offset(1, 0)
If CBool(Application.Subtotal(103, .Cells)) Then
.Cells.Copy _
Destination:=ws2.Cells(j, 1)
'optional Copy/PasteSpecial xlPasteValues method
'.Cells.Copy
'ws2.Cells(j, 1).PasteSpecial Paste:=xlPasteValues
'▲ might want to locate row j properly instead of just calling it 2
End If
End With
End With
End With
End Sub
I noticed you are using a Range.PasteSpecial method with xlPasteValues. If you require value-only transfer, then that can be accommodated.