Add If Statement to Code that Copies Using Column Headers - vba

This code copies data from one worksheet to another using column headers.
But I need a condition so that it copies only the rows where column L matches the value of $AB$1. I just can't seem to get the syntax right, it's just ignoring my added if statement and is just copying everything.
EDIT to clarify...I'm looking to copy only the rows where L# = $AB$1. Not copy all if L2 = AB1. Make sense?
Any ideas?
Sub Test()
Dim ws As Worksheet
Dim ws2 As Worksheet
Set ws = Worksheets("CurrentPayrollNonExempt")
Set ws2 = Worksheets("BiWkly Template")
With ws
For i = 1 To .UsedRange.Columns.Count
If (.Cells(2, "L").Value) = .Range("$AB$1").Value Then
Set x = ws2.Rows(4).Find(ws.Cells(1, i).Value, LookIn:=xlValues, Lookat:=xlWhole)
If Not x Is Nothing Then
y = .Cells(Rows.Count, i).End(3).Row
.Range(.Cells(2, i), .Cells(y, i)).Copy
ws2.Cells(5, x.Column).PasteSpecial xlValues
End If
Set x = Nothing
End If
Next i
End With
End Sub

Here's the minimal-edit version of what you want to do:
Sub Test()
Dim ws As Worksheet
Dim ws2 As Worksheet
Set ws = Worksheets("CurrentPayrollNonExempt")
Set ws2 = Worksheets("BiWkly Template")
With ws
' Filter out the rows to ignore
.UsedRange.Autofilter 12, .Range("$AB$1").Value ' column 12 = "L"
For i = 1 To .UsedRange.Columns.Count
Set x = ws2.Rows(4).Find(ws.Cells(1, i).Value, LookIn:=xlValues, Lookat:=xlWhole)
If Not x Is Nothing Then
y = .Cells(Rows.Count, i).End(3).Row
.Range(.Cells(2, i), .Cells(y, i)).Copy
ws2.Cells(5, x.Column).PasteSpecial xlValues
End If
Set x = Nothing
Next i
End With
End Sub
It just hides the rows where column L is not equal to the desired value. Pretty sure everything else should work as expected.
Be careful using UsedRange (in my edit and your original). Depending on your worksheet this can go very poorly, as UsedRange might not be what you're expecting. Better if you looped through the columns like: For i = 1 To .Cells(1, .Columns.Count).End(xlLeft).Column) and if you filtered on your known table range like .Range("A1:Z1000").Autofilter 'etc.

What is x and what is y? You never defined those. If you insert
Option Explicit
at the top of your module, it will force you to declare your variables.
I can't speak for everyone, but that seems like you're asking an awful lot out of a WITH statement. Personally, I would break that code up into more manageable and easier to read chunks, and your debugging will probably be easier to boot. There's no bonus points for condensing your code to the absolute minimum ;).
What is the data format of your spread sheets? Are you using tables? The table ListObject normally makes data handling a lot easier.
EDIT:
It appears you are only looping across all the columns in row 2, and you are never going down the rows. Add another control index, like this:
With ws
for j = 2 to .UsedRange.Rows.Count - 1
For i = 1 To .UsedRange.Columns.Count
If (.Cells(j, "L").Value) = .Range("$AB$1").Value Then
Set x = ws2.Rows(4).Find(ws.Cells(1, i).Value, LookIn:=xlValues, Lookat:=xlWhole)
If Not x Is Nothing Then
y = .Cells(Rows.Count, i).End(3).Row
.Range(.Cells(2, i), .Cells(y, i)).Copy
ws2.Cells(5, x.Column).PasteSpecial xlValues
End If
Set x = Nothing
End If
Next I
Next j
End With
The j loop should go down the rows, starting in row 2, and taking into account the end will be -1 due to the header row. The .Cells(j,"L") will thus be a constant value for each row pass.

I hope I understand what you want, since you are not advancing your search criteria, I took it outside the For loop.
Sub Test()
Dim ws As Worksheet
Dim ws2 As Worksheet
Set ws = Worksheets("CurrentPayrollNonExempt")
Set ws2 = Worksheets("BiWkly Template")
With ws
If Not IsError(Application.Match(.Range("$AB$1").Value, .Columns("L:L"), 0)) Then
For i = 1 To .UsedRange.Columns.Count
Set x = ws2.Rows(4).Find(ws.Cells(1, i).Value, LookIn:=xlValues, Lookat:=xlWhole)
If Not x Is Nothing Then
y = .Cells(Rows.Count, i).End(3).Row
.Range(.Cells(2, i), .Cells(y, i)).Copy
ws2.Cells(5, x.Column).PasteSpecial xlValues
End If
Set x = Nothing
Next i
Else
' for debug purposes only
MsgBox "Value in Cell $AB$1 not found"
End If
End With
End Sub

Related

Looking for specific contents in each of the cells in the column and delete the row in some cases

I'm trying to take the output of our scheduling software for a TV station and get rid of anything for given times. Unfortunately the output of the scheduling software creates a text field for time, not a field that can be formatted to time. I haven't done any real programming in over a decade and this is frustrating me. Here's a sample of the first few rows of the sheet - every day of the month contains entries for each program from 6:00a to the next day at 5:30a.
The code I've got so far is:
Sub delete_extraneous()
Dim rng As Range
Dim j As Integer
Dim m As Integer
m = 1
j = 3
Goto ActiveSheet.Cells(j, m)
With ActiveSheet
lastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
End With
For m = 1 To lastRow
If rng = "6:30a" Or "7:00a" Or "7:30a" Or "8:00a" Or "8:30a" Or "9:00a" Or "9:30a" Or "10:00a" Or "10:30a" Or "11:00a" Or "11:30a" Then
ActiveCell.EntireRow.Delete Shift:=xlShiftUp
End If
Next m
End Sub
Use an array of text-that-looks-like-time and match against it.
Sub delete_extraneous()
dim tms as variant, lastRow as long
tms = array("6:30a", "7:00a", "7:30a", "8:00a", "8:30a", "9:00a", "9:30a", _
"10:00a", "10:30a", "11:00a", "11:30a")
with activesheet
lastRow = .Cells(.Rows.Count, "C").End(xlUp).Row
For m = lastRow to 1 step-1
If not iserror(application.match(.Cells(m, "C").value, tms, 0)) Then
.rows(m).EntireRow.Delete Shift:=xlShiftUp
End If
Next m
.
end with
end sub
You could use Autofilter():
Sub test()
Dim hours As Variant
hours = Array("6:30a", "7:00a", "7:30a", "8:00a", "8:30a", "9:00a", "9:30a", "10:00a", "10:30a", "11:00a", "11:30a")
With Range("C1", Cells(Rows.Count, 3).End(xlUp))
.AutoFilter Field:=1, Criteria1:=hours, Operator:=xlFilterValues
.Offset(1, 0).SpecialCells(xlCellTypeVisible).EntireRow.Delete
If Not IsError(Application.Match(.Cells(1, 1).value, hours, 0)) Then .Rows(1).Delete
End With
ActiveSheet.AutoFilterMode = False
End Sub
You don't state what your specific issue is in the code, but I can tell you a few problems you have.
1) This is not valid syntax Goto ActiveSheet.Cells(j, m). There is a GoTo statement in VBA, but only use when absolutely necessary. (this case does not require it).
2) Don't rely on ActiveSheet. Instead reference the selected worksheet you desire to work with directly.
3) You never actually define rng so it's meaningless and your code will always bypass range. Using Option Explicit at the top of your modules can help avoid this issue.
4) Using active cell is also dangerous and may produce unintended consequences. In your case it will delete the same cell over and over and over again since you never activate any other cell. It's not needed.
See this code below. It also checks for row deletion and loads into a range for one delete statement later (which will be faster than deleting line by line, and doesn't require backwards looping).
Option Explicit
Sub delete_extraneous()
Dim mySheet As Worksheet
Set mySheet = Worksheets("mySheet") 'replace as needed
Dim lastRow As Long
lastRow = mySheet.Cells(mySheet.Rows.Count, 1).End(xlUp).Row
Dim m As Long
For m = 1 To lastRow
Select Case mySheet.Cells(m, 3).Value 'check each row against column C
Case Is = "6:30a", "7:00a", "7:30a", "8:00a", "8:30a", "9:00a", "9:30a", "10:00a", "10:30a", "11:00a", "11:30a"
Dim deleteRng As Range
If deleteRng Is Nothing Then
Set deleteRng = mySheet.Cells(m, 3)
Else
Set deleteRng = Union(deleteRng, mySheet.Cells(m, 3))
End If
End Select
Next
deleteRng.EntireRow.Delete
End Sub

Sort, Loop, copy into new worksheet with cell value name VBA

I know this has been asked lot of times but I'm having a trouble with VBA, I am very new to VBA.
I'm working with a single workbook that has a working worksheet. basically I need to sort the Currency column, currently have 14 currencies, I need loop through it (since currency may add through time depending on the customer) then copy the row with the criteria paste it to another sheet with its cell value.
my code below.
Option Explicit
Sub SortCurrency()
Dim rng As Range
Dim xCell As Range
Dim I As Long
Dim J As Long
I = Worksheets("Sheet1").UsedRange.Rows.Count
J = Worksheets("Sheet2").UsedRange.Rows.Count
If J = 1 Then
If Application.WorksheetFunction.CountA(Worksheets("Sheet2").UsedRange) = 0 Then J = 0
End If
Set rng = Worksheets("Sheet1").Range("AB2:AB" & I)
On Error Resume Next
Application.ScreenUpdating = False
For Each xCell In rng
If CStr(xCell.Value) = "USD" Then
Sheets.Add After:=Sheets(Sheets.Count)
Sheets(Sheets.Count).Name = xCell.Value
xCell.EntireRow.Copy Destination:=Sheets(Sheets.Count).Name = xCell.Value.Range("A" & J + 1)
'Sheets.Add After:=Sheets(Sheets.Count)
'Sheets(Sheets.Count).Name = xCell.Value
Application.CutCopyMode = False
J = J + 1
End If
Next
Application.ScreenUpdating = True
End Sub
I basically got the codes from my research, add them up and not coming into the way I wanted. I wanted to keep the header and the values with criteria,
i,e currency column "AB" is USD as per example above, but the problem is it'll be a lot of coding because I have to go through all 14 currencies plus if there will be new currency that will be added,
also I know there is a way of not declaring multiple sheets and just having another new worksheet with the cell value name but I'm having a problem getting it done all at once. if there will be a simpler and powerful code. I am greatly thankful.
you may want to try this code, exploiting Autofilter() method of Range object
Option Explicit
Sub SortCurrency()
Dim currRng As Range, dataRng As Range, currCell As Range
With Worksheets("Currencies") '<--| change "Currencies" to your actual worksheet name to filter data in and paste from
Set currRng = .Range("AB1", .Cells(.Rows.Count, "AB").End(xlUp))
Set dataRng = Intersect(.UsedRange, currRng.EntireRow)
With .UsedRange
With .Resize(1, 1).Offset(, .Columns.Count)
With .Resize(currRng.Rows.Count)
.Value = currRng.Value
.RemoveDuplicates Array(1), Header:=xlYes
For Each currCell In .SpecialCells(xlCellTypeConstants)
currRng.AutoFilter field:=1, Criteria1:=currCell.Value
If Application.WorksheetFunction.Subtotal(103, currRng) - 1 > 0 Then
dataRng.SpecialCells(xlCellTypeVisible).Copy Destination:=GetOrCreateWorksheet(currCell.Value).Range("A1")
End If
Next currCell
.ClearContents
End With
End With
End With
.AutoFilterMode = False
End With
End Sub
Function GetOrCreateWorksheet(shtName As String) As Worksheet
On Error Resume Next
Set GetOrCreateWorksheet = Worksheets(shtName)
If GetOrCreateWorksheet Is Nothing Then
Set GetOrCreateWorksheet = Worksheets.Add(After:=Sheets(Sheets.Count))
GetOrCreateWorksheet.name = shtName
End If
End Function
You're pretty close with what you've got, but there's a few things to note:
On Error Resume Next is normally a bad plan as it can hide a whole lot of sins. I use it in the code below, but only because I immediately deal with any error that might have happened.
xCell.Value.Range("A" & J + 1) makes no sense. Chop out the middle of that line to leave xCell.EntireRow.Copy Destination:=Sheets(Sheets.Count).Range("A" & J + 1)
Rather than checking if the value is a specific currency, you should be taking the value, whatever currency it is, and dealing with it appropriately.
Using J as a counter works for one currency, but when dealing with multiple, it'll be easier to just check where it should go on the fly.
All told, the below code should be close to what you're looking for.
Option Explicit
Sub SortCurrency()
Dim rng As Range
Dim xCell As Range
Dim targetSheet As Worksheet
Dim I As Long
Dim J As Long
I = Worksheets("Sheet1").UsedRange.Rows.Count
J = Worksheets("Sheet2").UsedRange.Rows.Count
If J = 1 Then
If Application.WorksheetFunction.CountA(Worksheets("Sheet2").UsedRange) = 0 Then J = 0
End If
Set rng = Worksheets("Sheet1").Range("AB2:AB" & I)
Application.ScreenUpdating = False
For Each xCell In rng
Set targetSheet = Nothing
On Error Resume Next
Set targetSheet = Sheets(xCell.Value)
On Error GoTo 0
If targetSheet Is Nothing Then
Sheets.Add After:=Sheets(Sheets.Count)
Set targetSheet = Sheets(Sheets.Count)
targetSheet.Name = xCell.Value
xCell.EntireRow.Copy Destination:=targetSheet.Range("A" & J + 1)
Else
xCell.EntireRow.Copy Destination:=targetSheet.Range("A" & targetSheet.Range("A" & Rows.Count).End(xlUp).Row + 1)
End If
Application.CutCopyMode = False
Next
Application.ScreenUpdating = True
End Sub
OK, there's quite a lot going on here... I'm going to try and tackle one problem at a time.
1 - You could do with testing whether a worksheet already exists rather than creating it every time
Assuming you want to do something for each and every currency in your loop, I would suggest not using the if condition you're using at the moment, "if value = "USD"", and instead use the cell value to determine the name of the sheet, whatever the cell value is.
First of all you need a seperate function to test whether the sheet exists, like
Public Function DoesSheetExist(SheetName as String)
On Error Resume Next
Dim WkSheet as WorkSheet
'sets worksheet to be the sheet NAMED the current currency name
Set WkSheet = Sheets(SheetName)
'because of on error resume next, WkSheet will simply be "Nothing" if no such sheet exists
If WkSheet is Nothing Then
DoesSheetExist = False
Else
DoesSheetExist = True
End If
End Function
You can then call this function in your code, and only create new sheets when you need to
2 - The loop itself
So instead, I would suggest your loop probably wants to look more like this:
Dim xSheet as Worksheet 'declare this outside the loop
For Each xCell In rng
If DoesSheetExist(xCell.Value) Then
set xSheet = Sheets(xCell.Value) 'this is the code for if the sheet does exist - sets the sheet by the sheet name rather than index
Else
set xSheet = Sheets.Add After:=Sheets(Sheets.Count)
xSheet.Name = xCell.Value
End if
With this setup, for every currency your loop will either set xSheet to the currency sheet that already exists, or create that sheet. This assumes that you want to do the same thing to all currencies, if not then extra conditions will need adding in
3 - the copy/paste line itself
xCell.EntireRow.Copy Destination:=Sheets(Sheets.Count).Name = xCell.Value.Range("A" & J + 1)
I don't think this code says what you think it does - what this code actually says is "Copy the Entire Row to the last Sheet's name, and make it equal to the range within xCell's Value at A, (J)+1
I think what you actually wanted to say was this:
xCell.EntireRow.Copy Destination:=Sheets(Sheets.Count).Range("A" & J + 1)
However, if you're using the code I gave you above you can instead use this now:
xCell.EntireRow.Copy Destination:=xSheet.Range("A" & J + 1)
In fact, you'd be better off doing that, especially if there is a chance that the sheets already existed and were picked up by DoesSheetExist
Personally I would also rather transfer values over than use copy/paste any day, but that's just an efficiency thing, the above should function fine.

if else statement at copying and pasting a cell value

I have the following code which will copy/paste some columns from "data" worksheet and pastes to the next empty column in to the column that i specify in the mastersheet called "KomKo".
Dim copySheet As Worksheet
Dim pasteSheet As Worksheet
Set copySheet = Worksheets("data")
Set pasteSheet = Worksheets("KoMKo")
lRow = copySheet.Cells(copySheet.Rows.Count, 1).End(xlUp).Row
With copySheet.Range("BX2:BX" & lRow)
pasteSheet.Cells(Rows.Count, "A").End(xlUp).Offset(1, 0).Resize(.Rows.Count, .Columns.Count) = .Value
End With
Now i would like to add an if condition for another column; which should say "if column U in Worksheet "data" has cell value "8636" then these values should be pasted to Column H in Worksheet "KomKo"(pastesheet); to the next row as i used the code above in the "with" part.
Else( If the value in Column H is not 8636) then it should paste the value inside this column to Column G at Worksheet "KomKo"(pastesheet) with same preferences as above again.
How can i do this ?
So, I've come up with a suggestion below using an if-then within a loop. I think it's close to what you want...
Sub try6()
Dim ws As Worksheet
Dim ws2 As Worksheet
Dim x As Range
Set ws = Worksheets("data")
Set ws2 = Worksheets("KomKo")
For Each x In ws.Range("C1:C100")
If x.Value = 8636 Then
ws2.Range("H:H").Value = ws.Cells(Rows.Count, "A").Value
ElseIf x <> 8636 Then
ws2.Range("G:G").Value = ws.Range(Rows.Count, "B").Value
End If
Next x
End Sub
Testing it, it took a while to execute. I'd say, set a dynamic range at something like A10000 and copy it directly without needing to necessarily test for whether there is a value in the range being copied.
You can also use the Select method for the purpose and copy the selection - from personal experience, I've had mixed success with it and I've seen people advise against using it here.
These are my .02, hope it helps! Cheers.

VBA check for value in a range

I am trying to loop through a column and if cells = "what i'm lookng for" then do something.
I have this so far, where I'm off is in the if statement where I check for the "name":
Option Explicit
Sub test()
Dim wksDest As Worksheet
Dim wksSource As Worksheet
Dim rngSource As Range
Dim name As String
Dim LastRow As Long
Dim LastCol As Long
Dim c As Long
Application.ScreenUpdating = False
Set wksSource = Worksheets("Sheet1")
With wksSource
LastCol = .Cells(1, .Columns.Count).End(xlToLeft).Column
For c = 16 To 20
LastRow = .Cells(.Rows.Count, c).End(xlUp).Row
Set rngSource = .Range(.Cells(5, 16), .Cells(LastRow, 16))
name = rngSource.Value
If name = "mark"
do something
End If
Next c
End With
Application.ScreenUpdating = True
'MsgBox "Done!", vbExclamation
End Sub
OK Chris
Maybe a bit of simplification is required but also a few assumptions.
It doesn't seem like LastCol is being used for anything - so let's assume this is the Column you want to loop through.
Your loop has fixed start and end values yet you are determining the LastRow - so let's assume you want to start from row 5 (in your code) and loop to the LastRow in the LastCol.
In order to determine LastCol you must have data in the row you are using to do this - so let's assume that there are values in row 1 in all columns up to column you want to loop say 16 (in your code).
If you want to (IF) test for a single (string) value in this case then you must arrange for your rngSource to be a single cell value. You also don't need to assign this to a variable unless you need to use it again.
Finally, if you want to check for other values you may want to consider using a SELECT CASE structure in place of your IF THEN structure.
Have a look at the following and change my assumptions to meet your requirement - good luck.
Sub test()
Dim wksDest As Worksheet
Dim wksSource As Worksheet
Dim rngSource As Range
Dim name As String
Dim LastRow As Long
Dim LastCol As Long
Dim c As Long
Application.ScreenUpdating = False
Set wksSource = Worksheets("Sheet1")
With wksSource
LastCol = .Cells(1, .Columns.Count).End(xlToLeft).Column
LastRow = .Cells(Rows.Count, LastCol).End(xlUp).Row
FirstRow = 5
For c = FirstRow To LastRow
If .Range(.Cells(c, LastCol), .Cells(c, LastCol)).Value = "Mark" Then
MsgBox ("do something")
End If
Next c
End With
End Sub
You can just do that with one line.
If Not IsError(Application.Match(ValueToSearchFor, RangeToSearchIn, 0)) Then
'The value found in the given range
End If
Example:
Search for "Canada" in column C of sheet named "Country"
If Not IsError(Application.Match("Canada", Sheets("Country").Range("C:C"), 0)) Then
'The value found in the given range
End If
Pass value to find and Column where value need to be checked. It will return row num if its found else return 0.
Function checkForValue(FindString As String,ColumnToCheck as String) As Long
SheetLastRow = Sheets("Sheet1").Cells.Find(What:="*", SearchOrder:=xlRows, SearchDirection:=xlPrevious, LookIn:=xlValues).row
With Sheets("Sheet1").Range("$" & ColumnToCheck & "$1:$" & ColumnToCheck & "$" & CStr(SheetLastRow) )
Set rng = .Find(What:=FindString, _
After:=.Cells(.Cells.Count), _
LookIn:=xlValues, _
lookat:=xlWhole, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False)
If Not rng Is Nothing Then
checkForValue = rng.row 'return row its found
'write code you want.
Else
checkForValue = 0
End If
End With
End Function
I tried Hari's suggestion, but Application.Match works weird on range names (not recognizing them...)
Changed to: WorksheetFunction.Match(...
It works, but when value is not present A runtime ERROR jumps before IsError(...) is evaluated.
So I had to write a simple -no looping- solution:
dim Index as Long
Index = -1
On Error Resume Next
Index = WorksheetFunction.Match(Target,Range("Edificios"), 0) 'look for Target value in range named: Edificios
On Error GoTo 0
If Index > 0 Then
' code for existing value found in Range # Index row
End If
Remeber Excel functions first index = 1 (no zero based)
Hope this helps.
I'm guessing what you really want to do is loop through your range rngSource. So try
Set rngSource = .Range(.Cells(5, 16), .Cells(LastRow, 16))
for myCell in rngSource
if myCell.Value = "mark" then
do something
end if
next myCell

I have this code but it only pastes header and not complete column of data.

I want the code to paste the entire column if the headers Match. As of now it is only pasting values from Row(1). Thanks a lot. If there are any other questions I will be commenting back quickly. I took out all the Dims and whatnot.
Sub sample()
Set sh1 = Sheets("Dec Demand")
Set sh2 = Sheets("List")
Set sh3 = Sheets("Results")
With sh2
Set rngLookupValues = .Range("J2", .Range("J" & .Rows.Count).End(xlUp))
End With
Debug.Print rngLookupValues.Address
With sh1
Set rngHeaders = .Range("A1", .Range("A1").End(xlToRight))
End With
Debug.Print rngHeaders.Address
For Each cValue In rngLookupValues
lngColumnToCopy = WorksheetFunction.Match(cValue, rngHeaders, 0)
Debug.Print lngColumnToCopy
With sh1
Set rngCellsToCopy = .Range(.Cells(1, lngColumnToCopy), .Cells(Rows.Count, lngColumnToCopy).End(xlUp)) ' HERE i want to have a copy entire column
End With
Debug.Print rngCellsToCopy.Address
With sh3
lngCurFirstEmptyColumn = .Cells(1, Columns.Count).End(xlToLeft).Column + 1
End With
Debug.Print lngCurFirstEmptyColumn
sh3.Cells(1, lngCurFirstEmptyColumn).Resize(rngCellsToCopy.Rows.Count) = rngCellsToCopy
Next cValue
With sh3.Range("A1")
If Len(.Value) < 1 Then
.EntireColumn.Delete
End If
End With
End Sub
You need to change
sh3.Cells(1, lngCurFirstEmptyColumn).Resize(rngCellsToCopy.Rows.Count) = rngCellsToCopy
to
sh3.Cells(1, lngCurFirstEmptyColumn).Resize(rngCellsToCopy.Rows.Count).Value = rngCellsToCopy.Value
or
rngCellsToCopy.Copy sh3.Cells(1, lngCurFirstEmptyColumn)
(don't know why, but your statement doesn't copy anything, but adding .Value fixes the problem)
' HERE i want to have a copy entire column
If you really need to copy EntireColumn (which make your code very slow), follow next steps
1) change
Set rngCellsToCopy = .Range(.Cells(1, lngColumnToCopy), .Cells(Rows.Count, lngColumnToCopy).End(xlUp))
to
Set rngCellsToCopy = .Cells(1, lngColumnToCopy).EntireColumn
2) and then change
sh3.Cells(1, lngCurFirstEmptyColumn).Resize(rngCellsToCopy.Rows.Count) = rngCellsToCopy
to
sh3.Cells(1, lngCurFirstEmptyColumn).EntireColumn.Value = rngCellsToCopy.Value
or you could use rngCellsToCopy.Copy sh3.Cells(1, lngCurFirstEmptyColumn) as well.
And one little note: use forgot to add period . before Rows.Count in the line Set rngCellsToCopy = .Range(.Cells(1, lngColumnToCopy), .Cells(Rows.Count, lngColumnToCopy).End(xlUp))