Copy and pasting information within a for loop - vba

I would like to create a function that copies certain excel ranges in worksheets and paste these ranges into a "motherfile".
Now, I am trying with this code:
Sub ranges()
Dim month As Variant
Dim months As Variant
months = Array("V01 DEN HAAG", "V02 AMSTERDAM")
Dim destinationRange As Excel.range
Set destinationRange = Sheets("DATASET").range("B3").End(xlDown).Offset(1, 0)
For Each month In months
Dim sourceRange As Excel.range
Set sourceRange = Sheets(month).range("H7", range("H7").End(xlToRight))
Call sourceRange.Copy
Call destinationRange.PasteSpecial
Next month
End Sub
But, I get an Application-defined or object-defined error. Any thoughts on what goes wrong? Thanks!

Adding to mielk's anwser the problem is in the codeline:
Set sourceRange = Sheets(month).range("H7", range("H7").End(xlToRight))
This is because if you are collecting from multiple sheets data and you use range("H7").End(xlToRight it will search for this on the active sheet. Therefor it can only find the correct range if its on the correct sheet.
by using the following code:
Set sourceRange = Sheets(month).Range("H7", Sheets(month).Range("H7").End(xlToRight))
it will work no matter which sheet is active at that moment.
another addition is you can copy and paste in 1 code line:
sourceRange.Copy Destination:=destinationRange
see below the entire code:
Sub ranges()
Dim month As Variant
Dim months As Variant
months = Array("V01 DEN HAAG", "V02 AMSTERDAM")
For Each month In months
Dim sourceRange As Excel.Range
Dim destinationRange As Excel.Range
With Sheets("DATASET")
Set destinationRange = .Cells(.Rows.Count, 2).End(xlUp).Offset(1, 0)
End With
Set sourceRange = Sheets(month).Range("H7", Sheets(month).Range("H7").End(xlToRight))
sourceRange.Copy Destination:=destinationRange
Next month
End Sub

The possibly reason for this error is that you don't have any values in worksheet "DATASET", column B, below 3. row.
Look at this line of code:
Set destinationRange = Sheets("DATASET").range("B3").End(xlDown).Offset(1, 0)
First it takes the range from cell B3 to the last cell in this column (B1048576 in Excel 2007+).
After that it tries to offset this range by one row down (so it tries to create a range having the same number of rows and columns but starting one cell below).
However, it is not possible, because such range would have to start in cell B4 and end in cell B1048577 and Excel has only 1048576 rows.
If you want to assign the first empty row to the variable destinationRange you should replace this code:
Set destinationRange = Sheets("DATASET").range("B3").End(xlDown).Offset(1, 0)
with the below:
With Sheets("DATASET")
Set destinationRange = .Cells(.Rows.Count, 2).End(xlUp).Offset(1, 0)
End With
Both those statements are similar. The difference is that the second one
starts from the last cell in the column B and look for the first non-empty
cell above.

Related

Setting range on other sheet, based on abstract row number

With a sub on sheet1, I need to get data from a range of cells (P.. to W..) from row X, where X is take from a reference cell on sheet2.
Dim final_range As Range
Dim ref_cell_range As Range
Dim ref_cell As Range
Set ref_cell_range = Worksheets("sheet2").Range("O9:O15")
For Each ref_cell In ref_cell_range
Set final_range = Worksheets("sheet2").Range(Cells(ref_cell.Row, "P"), Cells(ref_cell.Row, "W"))
Next ref_cell
This code works if I leave out the 'Worksheets("sheet2").' part, but then it takes the cells from sheet1.
If I run it like this (refencing the sheet to take the range from), I get an error ('Run-time error '1004': Application-defined or object-defined error')
Does anybody know what I might be doing wrong?
Your final_range reference is looking at two sheets.
This is looking at Sheet2:
Worksheets("sheet2").Range(
This is looking at whichever sheet is active as you haven't qualified the sheet for Cells to reference:
Cells(ref_cell.Row, "P"), Cells(ref_cell.Row, "W")
The full line should be:
Set final_range = Worksheets("sheet2").Range(Worksheets("sheet2").Cells(ref_cell.Row, "P"), Worksheets("sheet2").Cells(ref_cell.Row, "W"))
or using With...End With:
Sub Test()
Dim final_range As Range
Dim ref_cell_range As Range
Dim ref_cell As Range
With ThisWorkbook.Worksheets("sheet2")
Set ref_cell_range = .Range("O9:O15")
For Each ref_cell In ref_cell_range
Set final_range = .Range(.Cells(ref_cell.Row, "P"), .Cells(ref_cell.Row, "W"))
Next ref_cell
End With
End Sub

VBA Excel - Putting columns into range in right order

so recently I have been looking into using defined ranges to copy data instead of selecting, copying and pasting cells. This way I hope to optimise the performance and the runtime of my code.
Unfortunately I have come to face a problem I wasn't able to solve on my own.
When defining a range I want to rearrange the columns in a different order.
For example:
Set my_range = Sheets("Sheet1").Range("A2:E2,G2:H2,J2:K2,M2")
Works well, as the columns I fill into the range are behind each other in the sheet. But now I have this:
Set yo_range = Sheets("Sheet2").Range("D2,AV2,L2,H2,Q2,AE2,AG2")
If I fill these ranges into a new sheet the yo_range will fill the columns I put into it but not in the order I written down. It will put it down in the order according to the original one. In this example yo_range would put the data in this order into the new sheet:
D2 | H2 | L2 | Q2 | AE2 | AG2 | AV2
How can I fix this? I want the order to be another one than the original one.
Also - as you can see my_range has more columns than yo_range. How can I let yo_range be filled into the new sheet but at certain points leave columns out? For example:
my_range(A2:E2) goes into A2:E2 in the new sheet
yo_range(D2,AV2) goes into A:B in the new sheet, then leave C out and then paste yo_range(L2,H2) into D:E in the new sheet
I hope that I was able to explain my problem well and that there is somebody able and willing to help me. Any help is appreciated.
Edit:
Here's the code that puts the values from the ranges into the new sheet
Do
If Application.WorksheetFunction.CountA(my_range) > 0 Then
my_range.Copy ws.Range("A" & ws.Rows.Count).End(xlUp).Offset(1, 0)
Set my_range = my_range.Offset(1, 0)
Else
Exit Do
End If
Loop
Do
If Application.WorksheetFunction.CountA(yo_range) > 0 Then
yo_range.Copy ws.Range("A" & ws.Rows.Count).End(xlUp).Offset(1, 0)
Set yo_range = yo_range.Offset(1, 0)
Else
Exit Do
End If
Loop
We can see that the Copy method will re-arrange the data left-to-right. Try this:
Option Explicit
Public Sub CheckClipboard()
Dim ws As Worksheet
Dim rngToCopy As Range
Dim objData As Object
Dim varContents As Variant
' test data b,c,d,e,f,g in Sheet1!B1:G1
Set ws = ThisWorkbook.Worksheets("Sheet1")
ws.Range("B1:G1").Value = Array("b", "c", "d", "e", "f", "g")
Set rngToCopy = ws.Range("E1:F1,G1,B1:C1") '<-- note not left-to-right order
rngToCopy.Copy '<-- copy
' this is a late bound MSForms.DataObject
Set objData = CreateObject("New:{1C3B4210-F441-11CE-B9EA-00AA006B1A69}")
' copy current cell formula to clipboard
objData.GetFromClipboard
varContents = objData.GetText
Debug.Print varContents '<-- re-arranged left-to-right
' cancel copy
Application.CutCopyMode = False
End Sub
I get this in the immediate window:
b c d e f g
So, using Copy is not going to work for what you want to do.
In order to 'paste' the data in the order that you set it in the Range, you need to iterate each Area of the Range and then each cell (i.e. Range) in each Area. See the test code below which replicates your issue and presents a solution:
Option Explicit
Sub MixColumns()
Dim ws As Worksheet
Dim rngIn As Range
Dim rngOut As Range
Dim lng As Long
Dim rngArea As Range
Dim rngCell As Range
Set ws = ThisWorkbook.Worksheets("Sheet1")
' example 1
Set rngIn = ws.Range("B1:C1,E1:F1,G1") '<-- 5 cells, non-contiguous, forward order
Set rngOut = ws.Range("B2:F2") '<-- 5 contiguous cells
rngIn.Copy rngOut '<-- works
' example 2 - OP problem
Set rngIn = ws.Range("E1:F1,G1,B1:C1") '<-- 5 cells, non-contiguous, odd order
Set rngOut = ws.Range("B3:F3") '<-- 5 contiguous cells
rngIn.Copy rngOut '<-- should be e,f,g,b,c but gets b,c,e,f,g
' example 3 - solution for OP problem
Set rngIn = ws.Range("E1:F1,G1,B1:C1") '<-- 5 cells, non-contiguous, odd order
Set rngOut = ws.Range("B4:F4") '<-- 5 contiguous cells
lng = 1 '<-- rngOut cell counter
' iterate areas
For Each rngArea In rngIn.Areas
' iterate cells in area
For Each rngCell In rngArea.Cells
rngOut.Cells(1, lng).Value = rngCell.Value '<-- copy single value
lng = lng + 1 '<-- increment rngOut counter
Next rngCell
Next rngArea '<-- results in e,f,g,b,c
End Sub
Give this output:

VBA - how to select the just pasted values and then apply changes to them?

What I am trying to do is, in each worksheet:
1. Copy all numbers in column G (G23 and down) and paste special at the end of column A.
2. Format the pasted numbers to have only one decimal point.
3. Copy the pasted numbers and paste them at the end of column B, and remove duplicates.
I finished Step 1, but I don't know how to do Step 2 and 3.... I could not find ways to select the just pasted numbers at the bottom of Column A. I am new to VBA - Many thanks for your help.
Here is the code I have so far:
Sub WorksheetLoop()
Dim WS_Count As Integer
Dim I As Integer
Dim rng As Range
Dim last As Long
'Set WS_Count equal to the number of worksheets in the active workbook.
WS_Count = ActiveWorkbook.Worksheets.Count
'Begin loop, starts from the sixth worksheet
For I = 6 To WS_Count
last = Worksheets(I).Cells(Rows.Count, "G").End(xlUp).Row
Set rng = Worksheets(I).Range("G23:G" & last)
Worksheets(I).Select
rng.Copy
Worksheets(I).Cells(Rows.Count, 1).End(xlUp)(2).PasteSpecial xlPasteFormulasAndNumberFormats
....(what should I do next here?)
Next I
Application.ScreenUpdating = True
End Sub
You already know the range of the values to be pasted. The first cell of that range is the one used for PasteSpecial and the number of rows will be equal to the number of rows in rng. The required information is already there, all that is needed is to set it to a variable.
Here's a snippet to illustrate:
Sub Example()
Dim last As Long
Dim copyRange As Range
Dim pasteRange As Range
last = Worksheets(1).Cells(Rows.Count, "G").End(xlUp).Row
Set copyRange = Worksheets(1).Range("G23:G" & last)
Set pasteRange = Worksheets(1).Cells(Rows.Count, 1).End(xlUp)(2) _
.Resize(copyRange.Cells.Count, 1)
copyRange.Copy
pasteRange.PasteSpecial xlPasteFormulasAndNumberFormats
' use pasteRange for modifying the pasted data
End Sub
Changing the format can be done with range.NumberFormat. In this case you could do:
pasteRange.NumberFormat = "0.0"
I find it rather strange that you first copy the NumberFormat and then change it, though! Maybe you could choose to format the entire column A and not paste the formatting?
range.RemoveDuplicates is built in Excel for the third step. Here are a couple of answers that showcase how it can be used:
Remove Duplicates from range of cells in excel vba
Delete all duplicate rows Excel vba

For each loop in VBA unexpectedly skipping rows

I have written this code, that is part of a bigger problem. Initially it was supposed to start transfer some data from a worksheet to another.. but it keeps skipping rows, it copies one, skips then copies the other one, skips ,copies,skip... I am using the Select method just to test, instead of copy.
Dim sharepointSheet As Worksheet
Dim masterSheet As Worksheet
Dim sharepointTable As Range
'Auxliary variable
Dim row As Range
Dim cell As Range
Dim RowLast As Long
Set sharepointSheet = ThisWorkbook.Worksheets("Sharepoint List")
Set masterSheet = ThisWorkbook.Worksheets("Master List")
Set sharepointTable = masterSheet.Range("A2", "F133")
For Each row In sharepointTable.Rows
row.Cells(row.row, 3).Select
' RowLast = masterSheet.Cells(Rows.Count, "A").End(xlUp).row + 1
' masterSheet.Cells(RowLast, "A").PasteSpecial
Next row
You are having the problem because you are selecting the row relative to you row range rather than the worksheet range.
If you change the select line to the following it will solve your problem:
masterSheet.Cells(row.row, 3).Select
Also not that it's not a good idea to use variables such as row for a range because it can get very confusing!

Copied range searches for next empty cell in a row but skips merged cells

I've seen a lot of people talking about this problem but more from a point of view of copying merged cells into single cells. I have a procedure that gets a range from a workbook, opens the second workbook and attempts to paste it into the next blank cell in the specified row;
Public Sub BankBalances()
Dim fname As String
Dim fname2 As String
Dim mt As String, yr As String
'get the selected month
mt = MonthBox.Value
'get the selected year
yr = YearBox.Value
'set the file path to the selected year, month and the saving format
fname = yr & mt & "DB" & ".xlsx"
'set the file path to the selected year, month to open the trial balance sheet
fname2 = yr & mt & "TB" & ".xlsx"
'get the ranges
Dim rng1 As Range
Dim rng2 As Range
Set rng1 = Workbooks(fname2).Worksheets("excel").Range("I100")
'check for the next empty cell in row 55
Set rng2 = Workbooks(fname).Worksheets("UK monthly dashboard").Cells(55, Columns.Count).End(xlToLeft).Offset(0, 1)
'set the new range to hold the data from the original range
rng2.Value = rng1.Value
'set the result to format according to the other information in the workbook
rng2.Value = Round(rng1.Value / 1000, 0)
End Sub
The above code will paste the data in the next blank cell in the row that is not merged. I need it to be able to paste into the merged cells.
What I want to know is if there is a way using VBA to put the data into the merged cell or if I will need to do a separate procedure to check for merged cells, unmerge them and then merge them once the data has copied across.
If that is the way, would I be able to do that in a similar way to checking for the next empty cell, then check if it's merged, then carry out the unmerge and merge again.
If I understand what you are saying then yes, but if I may alter your example I want the contents of say A1 put into D1:D2 that are merged. So its a value of one cell going into two merged together – JamesDev 4 hours ago
Is this what you are trying?
Sub Sample()
Dim Rng1 As Range
Dim Rng2 As Range
Dim ws As Worksheet
Set ws = ThisWorkbook.Sheets("Sheet1")
With ws
Set Rng1 = .Range("A1")
Set Rng2 = .Range("D1:D2")
'~~> Check if cells are merged
If Rng2.MergeCells Then
'~~> If merged then write to first cell
Rng2.Cells(1, 1).Value = Rng1.Value
Else
Rng2.Value = Rng1.Value
End If
End With
End Sub