So the situation is that I would like to copy 4 columns with variable lengths into one column containing all the data from these 4. The problem is I loose data in the process.
As an example it should be 693 rows but I only get 648 rows all in all.
I'm relatively new to VBA and have come up with these lines of code.
Sub Copy()
Dim RngA As Range, RngB As Range, RngC As Range, RngD As Range, Rng As Range
Set RngA = Range(Range("I2"), Range("I" & Rows.Count).End(xlUp))
Set RngB = Range(Range("J2"), Range("J" & Rows.Count).End(xlUp))
Set RngC = Range(Range("K2"), Range("K" & Rows.Count).End(xlUp))
Set RngD = Range(Range("L2"), Range("L" & Rows.Count).End(xlUp))
Range("O2").Resize(RngA.Count).Value = RngA.Value
Range("O" & RngA.Count + 1).Resize(RngB.Count).Value = RngB.Value
Range("O" & RngB.Count + 1).Resize(RngC.Count).Value = RngC.Value
Range("O" & RngC.Count + 1).Resize(RngD.Count).Value = RngD.Value
With Sheets("Keywords")
Columns("O:O").Sort Key1:=.Range("=O1"), Order1:=xlAscending, Header:=xlYes
End With
End Sub
See my comment above, but here is an alternative approach using arrays which saves having to set up multiple similarly-named range variables.(Btw am assuming everything is on the Keywords sheet.)
Sub Copy()
Dim vRng(1 To 4) As Range, i As Long
With Sheets("Keywords")
For i = LBound(vRng) To UBound(vRng)
Set vRng(i) = .Range(.Cells(2, i + 8), .Cells(.Rows.Count, i + 8).End(xlUp))
Next i
For i = LBound(vRng) To UBound(vRng)
.Range("O" & Rows.Count).End(xlUp)(2).Resize(vRng(i).Count).Value = vRng(i).Value
Next i
.Columns("O:O").Sort Key1:=.Range("O1"), Order1:=xlAscending, Header:=xlYes
End With
End Sub
Related
I have some code which I know works, I am applying a SUMIF formula to a range of cells. It works but it add a load of extra row at the bottom that shouldn't be there. I tried adding in a do until loop but it gets stuck in an infinite loop and crashes.
This is my first lot of code which works but adds the extra row in only on the columns which have been copied over.
Dim z As Workbook 'Budget Workbook
Dim y As Workbook 'Formatted - current workbook
Dim lastRow As Integer
Dim budgLastRow As Integer
Dim rng As Range
Set y = Workbooks("DLT.xlsm")
Set z = Workbooks.Open("C:\Reports\Budget.xlsx")
'Apply function to columns to pull costing data
With y.Worksheets("DLT")
lastRow = Cells(Rows.Count, 5).End(xlUp).Row
For Each rng In .Range("AI22:AI" & lastRow)
rng.Formula = "=SUMIF('[Budget.xlsx]DynamicReport'!$C:$C,$E" & rng.Row & ",'[Budget.xlsx]DynamicReport'!H:H)"
rng.Value = rng.Value
Next rng
For Each rng In .Range("AJ22:AJ" & lastRow)
rng.Formula = "=SUMIF('[Budget.xlsx]DynamicReport'!$C:$C,$E" & rng.Row & ",'[Budget.xlsx]DynamicReport'!I:I)"
rng.Value = rng.Value
Next rng
For Each rng In .Range("AN22:AN" & lastRow)
rng.Formula = "=SUMIF('[Budget.xlsx]DynamicReport'!$C:$C,$E" & rng.Row & ",'[Budget.xlsx]DynamicReport'!E:E)"
rng.Value = rng.Value
Next rng
For Each rng In .Range("AO22:AO" & lastRow)
rng.Formula = "=SUMIF('[Budget.xlsx]DynamicReport'!$C:$C,$E" & rng.Row & ",'[Budget.xlsx]DynamicReport'!G:G)"
rng.Value = rng.Value
Next rng
End With
I think the other additional rows have been copied because the budget workbook contains more data then the formatted work book. I have know thought to possibly delete the other unnecessary row which have been copied cross.
So I have added this small piece of code in
With y.Worksheets("Formatted")
lastRow = Cells(Rows.Count, 5).End(xlUp).Row - 1
budgLastRow = Cells(Rows.Count, 35).End(xlUp).Row
Rows("AI" & lastRow & ":AO" & budgLastRow).EntireRow.Delete
End With
I get an application-defined error Object defined error on the line
Rows("AI" & lastRow & ":AO" & budgLastRow).EntireRow.Delete
This is probably not the most efficient way to do this, but its the only way I could think of. I am fairly new to VBA only been coding a couple of months so mostly just try out different ways and see what works. Can anyone help me please.
You didn't properly qualify the ranges for the lastRow variable:
lastRow = .Cells(.Rows.Count, 5).End(xlUp).Row
Note the dots before Cells and Rows.
A couple of additional points:
Always use Long rather than Integer for row variables as there are more rows in a sheet than an Integer can hold.
You don't need to loop to put the same formula in a column of cells.
Dim z As Workbook 'Budget Workbook
Dim y As Workbook 'Formatted - current workbook
Dim lastRow As Long
Dim budgLastRow As Long
Set y = Workbooks("DLT.xlsm")
Set z = Workbooks.Open("C:\Reports\Budget.xlsx")
'Apply function to columns to pull costing data
With y.Worksheets("DLT")
lastRow = .Cells(.Rows.Count, 5).End(xlUp).Row
With .Range("AI22:AJ" & lastRow)
.Formula = "=SUMIF('[Budget.xlsx]DynamicReport'!$C:$C,$E22,'[Budget.xlsx]DynamicReport'!H:H)"
.Value2 = .Value2
End With
With .Range("AN22:AN" & lastRow)
.Formula = "=SUMIF('[Budget.xlsx]DynamicReport'!$C:$C,$E22,'[Budget.xlsx]DynamicReport'!E:E)"
.Value2 = .Value2
End With
With .Range("AO22:AO" & lastRow)
.Formula = "=SUMIF('[Budget.xlsx]DynamicReport'!$C:$C,$E22,'[Budget.xlsx]DynamicReport'!G:G)"
.Value2 = .Value2
End With
End With
I want to copy certain cells (for, if then condition) to an other sheet. I got great help with my code and it smoothly runs through the lines so far, but still it doesn't do exactly what I want.
I want to look for the value 848 in column A, if there is 848 in a certain row X, I want to copy the content of the following cells: XA, XN, XO, XAM, AH, XP XE and XF to the other worksheet. But: the columns do not remain the same. They change from one to the other workbook like:
Copy value in the column X in “source” --> Column Y in “target”
A --> A, N-->B, O-->C, AM -->D, AH -->G, P-->I, E-->J, F-->K
After checking and copy pasting all the needed cells of rows containing 848 in column A, we do the same for the rows containing 618 in column A.
A --> A N-->B O-->C AM -->D T -->G P-->I E-->J F-->K
As I said, the code in general works properly, it's just that I don't get the right values to the cell I want them to. Any ideas? Thanks a lot!
Sub CopyToNewBook()
On Error Resume Next
Dim wbSrc As Workbook: Set wbSrc = Workbooks("invoices_eCMS.xlsx")
Dim wbDest As Workbook: Set wbDest = Workbooks("destination.xlsx")
If wbSrc Is Nothing Or wbDest Is Nothing Then
MsgBox "Please open both workbooks required"
Exit Sub
End If
On Error GoTo 0
Dim SearchValues() As String: SearchValues = Split("848,618", ",")
Dim wsSrc As Worksheet: Set wsSrc = wbSrc.Sheets("Data exAlps")
Dim wsDest As Worksheet: Set wsDest = wbDest.Sheets("Sheet1")
Dim LastRow As Long, i As Long, j As Long, z As Long: z = 63976
With wsSrc
LastRow = .Cells.Find("*", SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
For j = 0 To UBound(SearchValues)
For i = 2 To LastRow
If .Cells(i, 1).Value = SearchValues(j) Then
.Range(.Cells(i, 1), .Cells(i, 14)).Copy
'.Cells(i, 1).Copy
wsDest.Range("A" & z).PasteSpecial xlPasteValues
z = z + 1
', .Cells(i, 14)).Copy
End If
Next i
Next j
End With
End Sub
Updated Code:
Sub CopyToNewBook()
On Error Resume Next
Dim wbSrc As Workbook: Set wbSrc = Workbooks("invoices_eCMS.xlsx")
Dim wbDest As Workbook: Set wbDest = Workbooks("destination.xlsx")
If wbSrc Is Nothing Or wbDest Is Nothing Then
MsgBox "Please open both workbooks required"
Exit Sub
End If
On Error GoTo 0
Dim SearchValues() As String: SearchValues = Split("848,618", ",")
Dim wsSrc As Worksheet: Set wsSrc = wbSrc.Sheets("Data exAlps")
Dim wsDest As Worksheet: Set wsDest = wbDest.Sheets("Sheet1")
Dim LastRow As Long, i As Long, j As Long, z As Long: z = 63976
With wsSrc
LastRow = .Cells.Find("*", SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
For j = 0 To UBound(SearchValues)
For i = 2 To LastRow
If .Cells(i, 1).Value = SearchValues(j) Then
wsDest.Range("A" & z).Value = .Range("A" & i).Value
wsDest.Range("B" & z).Value = .Range("N" & i).Value
wsDest.Range("C" & z).Value = .Range("O" & i).Value
wsDest.Range("D" & z).Value = .Range("AM" & i).Value
wsDest.Range("G" & z).Value = .Range("AH" & i).Value
wsDest.Range("I" & i).Value = .Range("P" & z).Value
wsDest.Range("J" & i).Value = .Range("E" & z).Value
wsDest.Range("K" & i).Value = .Range("F" & z).Value
z = z + 1
', .Cells(i, 14)).Copy
End If
Next i
Next j
End With
End Sub
The problem exists here:
.Range(.Cells(i, 1), .Cells(i, 14)).Copy
wsDest.Range("A" & z).PasteSpecial xlPasteValues
where are you defining a specific range to copy and specific place to paste.
Since you want to copy certain columns in one sheet to different columns in your other sheet, you'll need to specify each one separately. See my example below. I didn't do each iteration, but you can just copy the code I wrote and adjust for each:
wsDest.Range("A" & z).Value = .Range("A" & i).Value
wsDest.Range("B" & z).Value = .Range("N" & i).Value
wsDest.Range("C" & z).Value = .Range("O" & i).Value
'... and so on for each cell that needs to be copied
If it's not clear, replace the code where I stated the problem was with the code I provided as a solution.
So I have a constant 7 number of ranges which are all one cell wide and a large, arbitrary height which is the same between them. Is it possible to join the ranges so that I have one range that only consists of all 7 ranges? I have tried the UnionRange function but it returns numbers far larger than to be expected.
This is what I have so far:
' acquire the range of each column, using its coumn number and the user-defined Col_Letter function
Dim rng1 As Range
Set rng1 = Sheets("sheet_name").Range(Col_Letter(col_1) & ":" & Col_Letter(col_1) & LRow)
Dim rng2 As Range
Set rng2 = Sheets("sheet_name").Range(Col_Letter(col_2) & ":" & Col_Letter(col_2) & LRow)
Dim rng3 As Range
Set rng3 = Sheets("sheet_name").Range(Col_Letter(col_3) & ":" & Col_Letter(col_3) & LRow)
Dim rng4 As Range
Set rng4 = Sheets("sheet_name").Range(Col_Letter(col_4) & ":" & Col_Letter(col_4) & LRow)
Dim rng5 As Range
Set rng5 = Sheets("sheet_name").Range(Col_Letter(col_5) & ":" & Col_Letter(col_5) & LRow)
Dim rng6 As Range
Set rng6 = Sheets("sheet_name").Range(Col_Letter(col_6) & ":" & Col_Letter(col_6) & LRow)
Dim rng7 As Range
Set rng7 = Sheets("sheet_name").Range(Col_Letter(col_7) & ":" & Col_Letter(col_7) & LRow)
' Join the ranges of each column into one range
Dim UnionRange As Range
Set UnionRange = Union(rng1, rng2, rng3, rng4, rng5, rng6, rng7)
Debug.Print "Width of UnionRange: " & UnionRange.width
Debug.Print "Height of UnionRange: " & UnionRange.height
You can't run UNION() on ranges from different worksheets. A Range is limited to a set of cells on a single sheet.
EDIT#1:
I suspect your Debug.Print. Your are printing a pixel-related variable rather than a row count
Sub dural()
Dim r As Range
Set r = Range("A1:C5")
MsgBox r.Height & vbCrLf & r.Rows.Count
End Sub
Try using unionrange.rows.count and unionrange.columns.count instead its is possible that width and height uses difference between border cell addresses instead of counting it
I solved it by acquiring the row number bounds like so:
Dim FirstRow As Long, LastRow As Long
With Get_Workbook.Sheets("Sheet_Name").UsedRange
FirstRow = .Row
LastRow = .Rows(UBound(.Value)).Row
End With
From there, I modified my code to this format:
Dim rng1 As Range
Set rng1 = Get_Workbook.Sheets("Sheet_Name").Range(Col_Letter(col_1) & FirstRow & ":" & Col_Letter(col_1) & LastRow)
This way, I have already found the correct column letter with my Col_Letter function and I can get the correct, used range as well.
Note that Get_Workbook and Col_Letter are user-defined functions.
I need to filter a data table where 3 columns can contain the result I am looking for:
So if the criteria is found in columns 1, 2 or 3 then the row should be returned.
(source: gulfup.com)
So in the above sample data lets say I select the criteria as "Fat"
I am looking for the autofilter to return rows 1 & 2; if I select the criteria as "Funny" I need rows 2 & 6 and so on....
Below is my code which is not working since apparently it tries to find the rows in which all columns contain the criteria, and it is not what I am looking to do.
With Sheet1
.AutoFilterMode = False
With .Range("A1:D6")
.AutoFilter
.AutoFilter Field:=2, Criteria1:="Fat", Operator:=xlFilterValues
.AutoFilter Field:=3, Criteria1:="Fat", Operator:=xlFilterValues
.AutoFilter Field:=4, Criteria1:="Fat", Operator:=xlFilterValues
End With
End With
I have also tried to use Operator:=xlor but when I ran the code it returned no results.
In short: The row must be returned by the filter is the criteria is found in column B or C or D.
Help is definitely appreciated.
As follow up from comments, there are two ways for you.
Use additional column with formula:
Dim copyFrom As Range
With Sheet1
.AutoFilterMode = False
With .Range("A1:E6")
'apply formula in column E
.Columns(.Columns.Count).Formula = "=OR(B1=""Fat"",C1=""Fat"",D1=""Fat"")"
.AutoFilter Field:=5, Criteria1:=True
On Error Resume Next
Set copyFrom = .Offset(1).Resize(.Rows.count - 1).SpecialCells(xlCellTypeVisible)
On Error GoTo 0
End With
End With
If Not copyFrom Is Nothing Then copyFrom.EntireRow.Copy
Use For loop with Union:
Dim copyFrom As Range
Dim i As Long
With Sheet1
For i = 2 To 6
If .Range("B" & i) = "Fat" Or .Range("C" & i) = "Fat" Or .Range("D" & i) = "Fat" Then
If copyFrom Is Nothing Then
Set copyFrom = .Range("B" & i)
Else
Set copyFrom = Union(.Range("B" & i), copyFrom)
End If
End If
Next
End With
If Not copyFrom Is Nothing Then copyFrom.EntireRow.Copy
For copying also header:
Dim copyFrom As Range
Dim i As Long
With Sheet1
Set copyFrom = .Range("B1")
For i = 2 To 6
If .Range("B" & i) = "Fat" Or .Range("C" & i) = "Fat" Or .Range("D" & i) = "Fat" Then
Set copyFrom = Union(.Range("B" & i), copyFrom)
End If
Next
End With
copyFrom.EntireRow.Copy
UPDATE:
Dim hideRng As Range, copyRng As Range
Dim i As Long
Dim lastrow As Long
With Sheet1
lastrow = .Cells(.Rows.Count, "B").End(xlUp).Row
.Cells.EntireRow.Hidden = False
For i = 2 To lastrow
If Not (.Range("B" & i) = "Fat" Or .Range("C" & i) = "Fat" Or .Range("D" & i) = "Fat") Then
If hideRng Is Nothing Then
Set hideRng = .Range("B" & i)
Else
Set hideRng = Union(.Range("B" & i), hideRng)
End If
End If
Next
If Not hideRng Is Nothing Then hideRng.EntireRow.Hidden = True
On Error Resume Next
Set copyRng = .Range("B1:B" & lastrow).SpecialCells(xlCellTypeVisible)
On Error GoTo 0
End With
If copyRng Is Nothing Then
MsgBox "There is no rows matching criteria - nothing to copy"
Exit Sub
Else
copyRng.EntireRow.Copy
End If
I have two sheets containing the employee records.
Sheet1 contains the Event Date, CardNo, Employee Name, Dept Id, Employee No, Entry and Exit Time, Total Working Hours, Status, ConcatinatedColumn and Remarks (copied through vlookup from sheet2)
Sheet2 contains ConcatinatedColumn, Event Date, Employee No, Name, Remarks.
If the data in the remarks column of sheet2 is "Sick Off" then that row should be inserted to sheet1 without effecting the previous records.
I've already written the code for it but it does not work.
Would be really grateful if anyone can help me out !
THANKS IN ADVANCE !
MY CODE :
Sub SickOff()
Dim objWorksheet As Sheet2
Dim rngBurnDown As Range
Dim rngCell As Range
Dim strPasteToSheet As String
'Used for the new worksheet we are pasting into
Dim objNewSheet As Sheet1
Dim rngNextAvailbleRow As Range
'Define the worksheet with our data
Set objWorksheet = ThisWorkbook.Worksheets("Sheet2")
'Dynamically define the range to the last cell.
'This doesn't include and error handling e.g. null cells
'If we are not starting in A1, then change as appropriate
Set rngBurnDown = objWorksheet.Range("G2:G" & objWorksheet.Cells(Rows.Count, "G").End(xlUp).Row)
'Now loop through all the cells in the range
For Each rngCell In rngBurnDown.Cells
objWorksheet.Select
If rngCell.Value = "Sick Off" Then
'select the entire row
rngCell.EntireRow.Select
'copy the selection
Selection.Copy
'Now identify and select the new sheet to paste into
Set objNewSheet = ThisWorkbook.Worksheets("Sheet1" & rngCell.Value)
objNewSheet.Select
'Looking at your initial question, I believe you are trying to find the next available row
Set rngNextAvailbleRow = objNewSheet.Range("A1:A" & objNewSheet.Cells(Rows.Count, "A").End(xlUp).Row)
Range("A" & rngNextAvailbleRow.Rows.Count + 1).Select
ActiveSheet.Paste
End If
Next rngCell
objWorksheet.Select
objWorksheet.Cells(1, 1).Select
'Can do some basic error handing here
'kill all objects
If IsObject(objWorksheet) Then Set objWorksheet = Nothing
If IsObject(rngBurnDown) Then Set rngBurnDown = Nothing
If IsObject(rngCell) Then Set rngCell = Nothing
If IsObject(objNewSheet) Then Set objNewSheet = Nothing
If IsObject(rngNextAvailbleRow) Then Set rngNextAvailbleRow = Nothing
End Sub
Let's say you have data in Sheet2 as shown below
Let's say the end of data in Sheet1 looks like this
Logic:
We are using autofilter to get the relevant range in Sheet2 which match Sick Off in Col G. Once we get that, we copy the data to the last row in Sheet1. After the data is copied, we simply shuffle data across to match the column headers. As you mentioned that the headers won't change so we can take the liberty of hardcoding the column names for shuffling this data.
Code:
Paste this code in a module
Option Explicit
Sub Sample()
Dim wsI As Worksheet, wsO As Worksheet
Dim lRow As Long, wsOlRow As Long, OutputRow As Long
Dim copyfrom As Range
Set wsI = ThisWorkbook.Sheets("Sheet1")
Set wsO = ThisWorkbook.Sheets("Sheet2")
'~~> This is the row where the data will be written
OutputRow = wsI.Range("A" & wsI.Rows.Count).End(xlUp).Row + 1
With wsO
wsOlRow = .Range("G" & .Rows.Count).End(xlUp).Row
'~~> Remove any filters
.AutoFilterMode = False
'~~> Filter G on "Sick Off"
With .Range("G1:G" & wsOlRow)
.AutoFilter Field:=1, Criteria1:="=Sick Off"
Set copyfrom = .Offset(1, 0).SpecialCells(xlCellTypeVisible).EntireRow
End With
'~~> Remove any filters
.AutoFilterMode = False
End With
If Not copyfrom Is Nothing Then
copyfrom.Copy wsI.Rows(OutputRow)
'~~> Shuffle data
With wsI
lRow = .Range("A" & .Rows.Count).End(xlUp).Row
.Range("A" & OutputRow & ":A" & lRow).Delete Shift:=xlToLeft
.Range("F" & OutputRow & ":F" & lRow).Copy .Range("K" & OutputRow)
.Range("F" & OutputRow & ":F" & lRow).ClearContents
.Range("B" & OutputRow & ":B" & lRow).Copy .Range("E" & OutputRow)
.Range("B" & OutputRow & ":B" & lRow).ClearContents
End With
End If
End Sub
Output: