VBA: multiple copy and paste from one sheet to another - vba

I'm new with vba and have created a macro that copy columns from all worksheets (sourceSheet) in the workbook over to Sheet1 (destSheet) and also creating an header in Sheet1 (destSheet).
It's working fine when I'm running it with columns in order, like A to D (A:D) but I want to copy the columns
A from sourceSheet to B in destSheet
B from sourceSheet to C in destSheet
C from sourceSheet to D in destSheet
D from sourceSheet to E in destSheet
E from sourceSheet to G in destSheet
J from sourceSheet to H in destSheet
K from sourceSheet to I in destSheet
O from sourceSheet to J in destSheet
I also want to insert a blank row in F in destSheet.
Someone that can help me with this ?
Sub Test()
Dim sourceSheet As Worksheet 'Define Source Sheet
Dim sourceRows As Integer 'Define Source Row
Dim destSheet As Worksheet 'Define Destination Sheet
Dim lastRow As Integer 'Define Last Row
Dim sourceMaxRows As Integer 'Define Source Max Rows
Dim totalRows As Integer 'Define Total Rows
Dim destRange As String 'Define Destination Range
Dim sourceRange As String 'Define Source Range
lastRow = 1
Worksheets.Add().Name = "Sheet1"
For Each sourceSheet In Worksheets
If sourceSheet.Name <> "Sheet1" Then
sourceSheet.Activate
sourceMaxRows = sourceSheet.Cells(Rows.Count, "A").End(xlUp).Row
totalRows = lastRow + sourceMaxRows - 4
Let sourceRange = "A5:D" & sourceMaxRows
Range(sourceRange).Select
Selection.Copy
sourceSheet.Select
Set destSheet = Worksheets("Sheet1")
destSheet.Activate
Let destRange = "B" & lastRow & ":E" & totalRows
Range(destRange).Select
destSheet.Paste
destSheet.Select
lastRow = lastRow + sourceMaxRows - 4
End If
Next
Rows("1:1").Select
Selection.Insert Shift:=xlDown, CopyOrigin:=xlFormatFromLeftOrAbove
Range("A1").Select
ActiveCell.FormulaR1C1 = "Product_Id"
Range("B1").Select
ActiveCell.FormulaR1C1 = "Category"
Range("C1").Select
ActiveCell.FormulaR1C1 = "Brand"
Range("D1").Select
ActiveCell.FormulaR1C1 = "Model"
Range("E1").Select
ActiveCell.FormulaR1C1 = "EAN"
Range("F1").Select
ActiveCell.FormulaR1C1 = "UPC"
Range("G1").Select
ActiveCell.FormulaR1C1 = "SKU"
Range("H1").Select
ActiveCell.FormulaR1C1 = "Supplier_Shop_Price"
Range("I1").Select
ActiveCell.FormulaR1C1 = "Invoice_Price"
Range("J1").Select
ActiveCell.FormulaR1C1 = "In_Stock"
Range("A1").Select
MsgBox "Updated"
End Sub

You need to build-up an algorithm on the following logic: for each sheet you run, the source column is "1" and the destination column is "2": at the end of each loop, you need to increment both variables by 2. Here is how your code algorithm should look like (I let you do the job of rearranging your specific code to the algorithm):
sourceColumnIndex = 1
destColumnIndex = 2
sourceColumn = Split(Cells(1, sourceColumnIndex).Address, "$")(1)
destColumn = Split(Cells(1, destColumnIndex).Address, "$")(1)
For Each sourceSheet In Worksheets
'do your job here: for example:
'...
sourceMaxRows = sourceSheet.Cells(Rows.Count, sourceColumn).End(xlUp).Row
'...
destRange = destColumn & lastRow & ":E" & totalRows
'...
'before to go to the next, readapt the indexes:
sourceColumnIndex = sourceColumnIndex + 2
destColumnIndex = destColumnIndex + 2
sourceColumn = Split(Cells(1, sourceColumnIndex).Address, "$")(1)
destColumn = Split(Cells(1, destColumnIndex).Address, "$")(1)
'we can go to the next with "C" and "D", then with "E" and "F" etc.
Next sourceSheet
NOTE 1
Such a function:
sourceColumn = Split(Cells(1, sourceColumnIndex).Address, "$")(1)
is just converting the column number to the associated letter by using the address and splitting it by "$".
NOTE 2
A passage like this is useless as well as slower:
Range("A1").Select
ActiveCell.FormulaR1C1 = "Product_Id"
Rather, try to re-factor your code like this:
Range("A1").FormulaR1C1 = "Product_Id"
without need of selecting the cell, but directly writing on its property (in this case I would rather use .Value, but you might want to use .FormulaR1C1, you know better than me.
AFTER THAT CHRISTMAS007 HAS CLEANED YOUR QUESTION
Well, clearly the key of all this is to use a variable letter. I might suggest you to embed the split into a function that converts numbers in letters:
Function myColumn(ByVal num As Integer) As String
myColumn = Split(Cells(1, num).Address, "$")(1)
End Function
and every time working with numbers. You can call the above function like this:
num = 1
Range(myColumn(num) & 1).Select
the above will select for you the range "A1", because you passed the number "1" into the function.
Of course, being your request a bit more detailed, this is something you should study yourself. But the idea is anyway that one: define the indexes at the beginning, such as indSource and indDest, then...
decrease them at your pleasure with indSource = indSource - 1 (or -2, or -3)
increase them at your pleasure with indDest = indDest + 1 (or +2, or +3)
and work within the loop to get your desired results.

I did it easier, by deleting the rows and added one new blank column in the macro and it's working as I want, I just added the code to the macro:
For Each sourceSheet In Worksheets
sourceSheet.Activate
Columns("F:I").Delete
Columns("H:J").Delete
Columns("I:N").Delete
Columns("E:E").Insert
Next

Related

Remove row if cell value not in list

I have 2 sheets : in the first i have date and in sheet2 i have a list of names in column A . I want to delete all the rows that don't have the names from sheet2 in the column O from the first sheet. The code just deletes everything from the first sheet. Any help is welcomed.
Sub Demo()
Dim Rng As Range, List As Object, Rw As Long
Dim x As Date
x = Now()
Set List = CreateObject("Scripting.Dictionary")
With Sheets("Sheet2")
For Each Rng In .Range("A1", .Range("A" & Rows.Count).End(xlUp))
If Not List.Exists(Rng.Value) Then
List.Add Rng.Value, Nothing
End If
Next
End With
With Sheets("query " & Format(x, "dd.mm.yyyy"))
For Rw = .Range("O" & Rows.Count).End(xlUp).Row To 1 Step -1
If Not List.Exists(.Cells(Rw, "O").Value) Then
.Rows(Rw).Delete
End If
Next
End With
Set List = Nothing
End Sub
I'm not sure if this does exactly what you wants, but it does something very similar. To be clear:
Marks the cell adjacent to the list of names in Sheet1, if the name is found, then subsequently deletes the entire row if the the cell in said adjacent column is empty.
Sub Macro()
Dim r As Long
Dim r2 As Long
Dim counter As Long
Dim counter2 As Long
Range("O1").Select
Selection.End(xlDown).Select
r = ActiveCell.Row
Sheets(ActiveSheet.Index + 1).Select
Range("A1").Select
Selection.End(xlDown).Select
r2 = ActiveCell.Row
Range("A1").Select
For counter = 1 To r2
needle = ActiveCell.Value
Sheets(ActiveSheet.Index - 1).Select
On Error GoTo NotFound
Range(Cells(1, 15), Cells(r, 15)).Find(needle).Select
Selection.Offset(0, 1).Value = "found"
NotFound:
Sheets(ActiveSheet.Index + 1).Select
Selection.Offset(1, 0).Select
Next
Sheets(ActiveSheet.Index - 1).Select
Range("P1").Select
For counter2 = 1 To r
If ActiveCell.Value = "" Then Selection.EntireRow.Delete
Selection.Offset(1, 0).Select
Next
Cleanup:
Range("P1:P10000").Value = ""
End Sub
It is however, rather ugly and inefficient code. Lmk if there's something that needs changing!
i would do it like this:
Dim i as integer
dim x as integer
Dim rngSearch as Range
Dim strName as String
Dim ws1 as Worksheet
dim ws2 as Worksheet
Set ws1 = Thisworkbook.worksheets(1)
Set ws2 = Thisworkbook.worksheets(2)
x = ws1.cells(ws1.rows.count,1).end(xlup).row
for i = 2 to x
strName = ws1.cells(i, 1)
set rngSearch = ws2.columns(15).find(strName)
if rngSeach is nothing then
ws1.rows(i).entirerow.delete
i = i-1
end if
next i
It's not tested but it should work like this.
Edit: I think you have to put the worksheets in right order. I think i mixed them up here.

Excel fill rows with data from columns in separate sheets

I have an excel file with lots of sheets named "xxA" and "xxB" with xx being consecutive numbers.
Each sheet has the following format:
header1 header2 header3 header 4 header5
ingredient1 description xx 20 g
ingredient2 description xx 34 ml
ingredient3 description xx 56 g
and some other rows at the end.
Basically I want to create a new sheet in which rows 2-27 from column D are copied to a column named "value" and create two new columns with the number in the sheet name and another one with the letter like so:
subject condition ingredient value
21 A ingredient1 20
21 A ingredient2 34
21 A ingredient3 56
21 B ingredient1 34
21 B ingredient2 23
21 B ingredient3 47
...
I tried messing with pivot tables but that doesn't really work. I don't know how to create a VBA, so any direction on that would be great if that is the only way to go.
I think this is what you are looking for. It copies data from worksheets and gets the sheet names split as asked. I have it hard coded to only work for double digit numbers and single letters. Do you have sheets that do not fit that form? If so, I can rework my code!
ORIGINAL:
Sub SheetSummary()
'Make new worksheet with required headers
ActiveWorkbook.Sheets.Add
ActiveSheet.name = "Summary"
range("A1").Value = "subject"
range("B1").Value = "condition"
range("C1").Value = "ingredient"
range("D1").Value = "value"
Dim ws As Worksheet
Dim wsNum As String
Dim wsLetter As String
Dim wsLastRow As Long
Dim sumLastRow As Long
Dim myCell As range
Dim nextOpenRow As Long
'If a worksheet is not the summary, then get the data
For Each ws In Worksheets
If ws.name <> "Summary" Then
wsNum = Left(ws.name, 2)
wsLetter = Right(ws.name, 1)
wsLastRow = ws.Cells(Rows.count, "A").End(xlUp).Row
nextOpenRow = Cells(Rows.count, "A").End(xlUp).Row + 1
ws.range("A2", ws.Cells(wsLastRow, "A")).Copy
range("C" & nextOpenRow).PasteSpecial xlPasteAll
lastRow = Cells(Rows.count, "C").End(xlUp).Row
ws.range("C2", ws.Cells(wsLastRow, "C")).Copy
range("D" & nextOpenRow).PasteSpecial xlPasteAll
Application.CutCopyMode = False
For Each myCell In range("A2", Cells(lastRow, "A"))
If myCell.Value = "" Then
myCell.Value = wsNum
End If
Next myCell
For Each myCell In range("B2", Cells(lastRow, "B"))
If myCell.Value = "" Then
myCell.Value = wsLetter
End If
Next myCell
End If
Next ws
End Sub
EDIT:
Sub SheetSummary()
'Make new worksheet with required headers
ActiveWorkbook.Sheets.Add
ActiveSheet.name = "Summary"
range("A1").Value = "subject"
range("B1").Value = "condition"
range("C1").Value = "ingredient"
range("D1").Value = "value"
Dim ws As Worksheet
Dim wsNum As String
Dim wsLetter As String
Dim wsLastRow As Long
Dim sumLastRow As Long
Dim myCell As range
Dim nextOpenRow As Long
'If a worksheet is not the summary, then get the data
For Each ws In Worksheets
If ws.name <> "Summary" Then
wsNum = Left(ws.name, 2)
wsLetter = Right(ws.name, 1)
wsLastRow = ws.Cells(Rows.count, "A").End(xlUp).Row
nextOpenRow = Cells(Rows.count, "A").End(xlUp).Row + 1
ws.range("A2:A27").Copy
range("C" & nextOpenRow).PasteSpecial xlPasteAll
lastRow = Cells(Rows.count, "C").End(xlUp).Row
ws.range("D2:D27").Copy
range("D" & nextOpenRow).PasteSpecial xlPasteAll
Application.CutCopyMode = False
For Each myCell In range("A2", Cells(lastRow, "A"))
If myCell.Value = "" Then
myCell.Value = wsNum
End If
Next myCell
For Each myCell In range("B2", Cells(lastRow, "B"))
If myCell.Value = "" Then
myCell.Value = wsLetter
End If
Next myCell
End If
Next ws
End Sub
Since you don't know VBA I wouldn't recommend taking that route. You can acheive everything you want using Excel formulas.
To get the name of a sheet use:
=MID(CELL("filename",A1),FIND("]",CELL("filename",A1))+1,255)
Replace "A1" with a reference to the cell on the worksheet you want the name of.
Then use the Left() function to split out the "xx" from the name and then use the Right() function to split out "A"
Hope this helps.

Code Won't Copy and Paste Specific Cells Into New Sheet VBA

I am using this code to check each row in worksheet "Report2" for the phrase "Chicago" and copy and paste any rows with "Chicago" in them into a new sheet. However, it is not working. Any help on why would be appreciated.
Code:
Sub BranchCount()
Dim s As Worksheet
Dim LastRow As Long
Set s = Worksheets("Report 1")
LastRow = s.UsedRange.SpecialCells(xlCellTypeLastCell).Row
Worksheets("Report 1").Select
Range("A1:J" & LastRow).Select
Selection.Copy
Sheets.Add.Name = "Report2"
Selection.PasteSpecial xlPasteValues
Range("A1").EntireRow.Delete
Range("B1").EntireRow.Delete
Range("C1").EntireRow.Delete
Dim Z As Range
Dim Y As String
Y = W
W = "Chicago"
Sheets("Report2").Range("A1").Select
For Each Z In Range("J1:J" & LastRow)
If Y = Z.Value Then
Z.EntireRow.Copy
Sheets("Clean").Select
Range("A700").End(xlUp).Offset(1, 0).Select
Selection.PasteSpecial xlPasteValues
Sheets("Report2").Select
End If
Next
End Sub
Let me know if you can help. Thanks!
no need for any helper ("Report2") sheet
you could filter the relevant part of data cells and copy selected cells directly to "Clean" sheet as follows
Option Explicit
Sub BranchCount()
Dim s1 As Worksheet, sC As Worksheet
Dim LastRow As Long
Set s1 = Worksheets("Report 1")
Set sC = Sheets("Clean")
With s1
LastRow = .Cells(.Rows.Count, "J").End(xlUp).Row
With .Range("A1:J" & LastRow)
.AutoFilter field:=10, Criteria1:="Chicago"
With .Offset(1).Resize(.Rows.Count - 1)
If Application.WorksheetFunction.Subtotal(103, .Columns("J")) > 1 Then .SpecialCells(xlCellTypeVisible).Copy Destination:=sC.Range("A700").End(xlUp).Offset(1, 0)
End With
.AutoFilter
End With
End With
End Sub

VBA - copy the range in some cells and appear them into other sheet

I want a code that doing the following:
if the last 5 characters of the text value in the cell in column E is “(UK)” then the macro copies the range consisting of 4 cells in columns B,C,D,E in the same row and pastes below the last non-empty row in the worksheet “Sheet 1” in the same columns (so all ranges B-E with “(UK)” must be transferred to the sheet “Sheet1”);
I am just posting my code. Hope #Jonathan will learn it.
Sub CopyC()
Dim wb As Workbook
Dim ws As Worksheet
Dim sheet1lastrow As Long
Dim lastrow As Long
Set wb = ThisWorkbook
Set ws = wb.Worksheets("sheet1")
lastrow = ActiveSheet.Range("E" & Rows.Count).End(xlUp).Row
sheet1lastrow = ws.Range("E" & Rows.Count).End(xlUp).Row
For i = 1 To lastrow
If Right(ActiveSheet.Cells(i, 5).Value, 5) = "(UK)" Then
ActiveSheet.Range(Cells(i, 2), Cells(i, 5)).Copy
ws.Cells(sheet1lastrow + 1, 2).PasteSpecial xlValues
Application.CutCopyMode = False
Application.CutCopyMode = True
End If
Next
End Sub

How to use column headers to select different ranges of cells to populate data from a filename

This is a separate question stemming from this post: How to use the filename of an excel file to change a column of cells?
I noticed that in the last post's code it was referencing specific cells (J2,K2). However when using the code, I came into an error when the columns changed. So now I am seeking a way to modify the below code to use the names of the header columns to populate the 2nd column instead of referencing specific cells. I think the only line that really needs adjusting is the myRng line, but I will provide all the code I am trying for reference.
In case you don't read the other post, I will describe the issue. I am trying to fill in the 2nd column (name+type) based on the "name" column and the filename. When I was referencing the K or J row in the code, everything was working fine, but when I load a different file and the columns positions have changed, everything gets messed up.
I need to populate the 2nd column (name+type) to be the exactly the same number or rows as the 1st column (name) which is why I am using the Range ("K2:K" & lastCell) formula.
Is there a way to do this?
Current Attempted VBA code:
' Insert Column after name and then rename it name+type
Rows(1).Find("name").Offset(0, 1).EntireColumn.Insert
Rows(1).Find("name").Offset(0, 1).FormulaR1C1 = "name+type"
Dim myRng As Range
Dim lastCell As Long
Dim myOtherRange As Range
Dim column2Range As Range
myOtherRange = Rows(1).Find("name")
column2Range = Rows(1).Find("name+type")
lastCell = Range(myOtherRange).End(xlDown).Row
Set myRng = Range("K2:K" & lastCell)
myOtherRange.FormulaR2C1 = "=LEFT(MID(CELL(""filename""),SEARCH(""["",CELL(""filename""))+1, SEARCH(""]"",CELL(""filename""))-SEARCH(""["",CELL(""filename""))-1),5)&RC[-1]"
myOtherRange.FormulaR2C1.Select
Selection.Copy
myRng.Select
ActiveSheet.Paste
First Draft VBA code:
' Insert Column after name and then rename it name+type
Rows(1).Find("name").Offset(0, 1).EntireColumn.Insert
Rows(1).Find("name").Offset(0, 1).FormulaR1C1 = "name+type"
'Add the contents to the name+type column
Range("K2").Select
ActiveCell.FormulaR1C1 = "=LEFT(MID(CELL(""filename"",RC[-1]),SEARCH(""["",CELL(""filename"",RC[-1]))+1,SEARCH(""]"",CELL(""filename"",RC[-1]))-SEARCH(""["",CELL(""filename"",RC[-1]))-1),5)&RC[-1]"
Range("K2").Select
Selection.Copy
Range("K2:K8294").Select
ActiveSheet.Paste
#Scott or Siddharth Rout probably =) – Jonny 11 hours ago
I would never recommend this :) SO is full of experts who can assist you. Why do you want to limit the help that you can get? ;)
Is this what you are trying?
Option Explicit
Sub Sample()
Dim ws As Worksheet
Dim lRow As Long, aCol As Long
Dim aCell As Range
Set ws = Sheets("Sheet1") '<~~ Change this to the relevant sheet name
With ws
Set aCell = .Rows(1).Find("Name")
'~~> Check if the column with "name" is found
If Not aCell Is Nothing Then
aCol = aCell.Column
.Columns(aCol + 1).EntireColumn.Insert
.Cells(1, aCol + 1).Value = "Name+Type"
.Activate
.Rows(1).Select
With ActiveWindow
.SplitColumn = 0
.SplitRow = 1
.FreezePanes = True
End With
'~~> Get lastrow of Col which has "name"
lRow = .Range(Split(.Cells(, aCol).Address, "$")(1) & .Rows.Count).End(xlUp).Row
ThisWorkbook.Save
'~~> Add the formula to all the cells in 1 go.
.Range(Split(.Cells(, aCol + 1).Address, "$")(1) & "2:" & _
Split(.Cells(, aCol + 1).Address, "$")(1) & lRow).Formula = _
"=LEFT(MID(CELL(""filename"",RC[-1]),SEARCH(""["",CELL(""filename"",RC[-1]))+1," & _
"SEARCH(""]"",CELL(""filename"",RC[-1]))-SEARCH(""["",CELL(""filename"",RC[-1]))-1),5)&RC[-1]"
.Columns("A:AK").Columns.AutoFit
Else
MsgBox "Name Column Not Found"
End If
End With
End Sub
After modifying the code provided by Siddharth, this is the final code that worked for me. The save feature needed to also remove a format and the Formula to search and add the filename to the cells did not work without this edit. I also had to change the sheet to the activeSheet, because it was constantly changing. Here is the code:
Sub Naming()
Dim LR As Long, i As Long, lngCol As Long
lngCol = Rows(1).Find("NAME", lookat:=xlWhole).Column 'assumes there will always be a column with "NAME" in row 1
Application.ScreenUpdating = False
LR = Cells(Rows.Count, lngCol).End(xlUp).Row
For i = LR To 1 Step -1
If Len(Cells(i, lngCol).Value) < 4 Then Rows(i).Delete
Next i
Application.ScreenUpdating = True
' Insert Column after NAME and then rename it NAME+TYPE
Dim ws As Worksheet
Dim lRow As Long, aCol As Long
Dim aCell As Range
Set ws = ActiveSheet 'Need to change to the Active sheet
With ws
Set aCell = .Rows(1).Find("NAME")
' Check if the column with "NAME" is found, it is assumed earlier
If Not aCell Is Nothing Then
aCol = aCell.Column
.Columns(aCol + 1).EntireColumn.Insert
.Cells(1, aCol + 1).Value = "NAME+TYPE"
.Activate
' Freeze the Top Row
Rows("1:1").Select
With ActiveWindow
.SplitColumn = 0
.SplitRow = 1
End With
ActiveWindow.FreezePanes = True
' Get lastrow of Col which has "NAME"
lRow = .Range(Split(.Cells(, aCol).Address, "$")(1) & .Rows.Count).End(xlUp).Row
'Save the file and format the filetype
Dim wkb As Workbook
Set wkb = ActiveWorkbook 'change to your workbook reference
wkb.SaveAs Replace(wkb.Name, "#csv.gz", ""), 52 'change "csv.gz" to ".xlsm" if need be
' Add the formula to all the cells in 1 go.
.Range(Split(.Cells(, aCol + 1).Address, "$")(1) & "2:" & _
Split(.Cells(, aCol + 1).Address, "$")(1) & lRow).Formula = _
"=LEFT(MID(CELL(""filename""),SEARCH(""["",CELL(""filename""))+1, SEARCH(""]"",CELL(""filename""))-SEARCH(""["",CELL(""filename""))-1),5)&RC[-1]"
.Columns("A:AK").Columns.AutoFit
Else
MsgBox "NAME Column Not Found"
End If
End With
' Change the Range of the cursor
Range("A1").Select
Application.CutCopyMode = False
End Sub