Copy Column over, delete original column using Excel VBA - excel

I've been working on simplifying a script, I wanted to create a script that would copy within a range of columns over to the furthest left unoccupied column. Then delete the original column.
I've tweaked this script I found to do mostly what I wanted, it copies everything over, but doesn't delete.
I also need it to make it so that it can choose which row in said column range to look at for reference.
Could someone take a look at the script and help finish what I've started? I've been learning Excel VBA slowly, and my work has caused me to jump ahead in multiple directions before I have been ready, so I know that the copy and delete options should be simple, but because of the my line of work, I haven't had the time to learn everything correctly, so sorry if this is a simple answer, but I need to keep this script as lean as possible, and this is what I've come up with so far with what I know and scoured the internet for code and help.
Option Explicit
Sub MoveColumns()
Dim cel As Range
With ActiveSheet
For Each cel In Intersect(.UsedRange, .[B:N]).SpecialCells(xlCellTypeBlanks).Cells
cel = cel.Offset(0, 1)
Next
End With
End Sub
Thanks in advance.

don't mind the data under the main, I have a script that will get rid of it, so when this script runs it will run with nothing under it. In the case I want to move Copy G:G to F:F, then Delete G:G, move H:H to G:G, delete H:H, and continue till all the columns are side by side, with no space inbetween and no duplicates anywhere else.
What you want is complicated way of moving things. The easiest way is to delete the blank column. The rest of the columns will automatically move to the left :) I am assuming that there is no data after row 194.
Try this
Option Explicit
Sub MoveColumns()
Dim ws As Worksheet
Dim lastCol As Long, i As Long
Dim DelRange As Range
Set ws = Sheets("BackOrder")
With ws
lastCol = .Cells.Find(What:="*", _
After:=.Range("A1"), _
Lookat:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByColumns, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Column
For i = 1 To lastCol
If Application.WorksheetFunction.CountA(.Columns(i)) = 0 Then
If DelRange Is Nothing Then
Set DelRange = .Columns(i)
Else
Set DelRange = Union(DelRange, .Columns(i))
End If
End If
Next
If Not DelRange Is Nothing Then DelRange.Delete
End With
End Sub
HTH

Related

Copying a column from one workbook to another, finding the first empty column in the target worksheet

I have a workbook that tracks long-term trends in experimental data. Independant workbooks for various experiments generate 2-3 columns of data that need to be copied to this tracking workbook. I would probably go for something like this:
Workbooks(source_book).Worksheets(source_sheet).Range(Source_Range_Variable).Copy
Workbooks(target_book).Worksheets(target_sheet).Range(Target_Range_Variable).PasteSpecial xlPasteValues
My issue is that I have no idea how to find "Target_Range_Variable" which would be the first empty column in the target sheet. I have some ideas on how to set "Source_Range_Variable" because it can be one or two columns by using nested if's to find if columns are populated and going from there. Inelegant for sure.
Sorry I don't have any real code, but I truly don't know how to start. There are several hundred columns already, and there will be several hundred more. If it wasn't so big, I would brute force my way by nesting if statements until it finds an empty column.
Note: I'm very inexperienced with this, forgive me if I miss anything obvious.
You can use Cells.Find method to find the last column. Here is an example (Untested)
Sub Sample()
'
'~~> Rest of code
'
Workbooks(source_book).Worksheets(source_sheet).Range(Source_Range_Variable).Copy
Dim LastCol As Long
Dim ColName As String
'~~> Get the last column number
LastCol = LastColumn(Workbooks(target_book).Worksheets(target_sheet))
'~~> Column number to column letter
ColName = Split(Cells(, LastCol).Address, "$")(1)
'~~> Your final range. Ex "A1"
Target_Range_Variable = ColName & 1
Workbooks(target_book).Worksheets(target_sheet).Range(Target_Range_Variable).PasteSpecial xlPasteValues
End Sub
Private Function LastColumn(wks As Worksheet) As Long
If Application.WorksheetFunction.CountA(wks.Cells) <> 0 Then
LastColumn = wks.Cells.Find(What:="*", _
After:=wks.Range("A1"), _
Lookat:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByColumns, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Column
Else
LastColumn = 1
End If
End Function

Do Until Nothing Found

I am working on a macro to go through stock data. When no data is available on a given date, the data shows "-" in those rows. I need to delete those rows.
I have come up with a macro that deletes the first row found. I need to keep it going until all rows with "-" are deleted.
How can I do this with a Do Until loop?
Sub removejank()
'
' removejank Macro
Cells.Find(What:="-", After:=ActiveCell, LookIn:=xlFormulas2, LookAt:= _
xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=False _
, SearchFormat:=False).Activate
If ActiveCell.Value = "-" Then
Rows(ActiveCell.Row).Delete
End If
End Sub
There are many possible ways to loop through and delete rows.
In your example, using Activate significantly slows down runtime when you are looping data. .Activate is almost completely avoidable in just about every circumstance. Obviously, I see that what you've done was with the macro recorder, so it's understandable that it's there.
Anyway, try the following code. This only looks in column A. If you need to look in a different column, then change the "1" in this line:
If .Cells(r, 1).Value = "-" Then
To the column # you want to look in. Also, you will likely need to change the sheet name in this line:
With ThisWorkbook.Worksheets("Sheet3")
to match the sheet name you are wanting the code to run in.
So the way this method of the many methods out there works is that you will loop through every line in column A with For r = 1 .... Each loop iteration you are looking in the cell's value. If it equals -, then the If statement is true and proceeds to add the cell to our special range that we named delRng. You are not actually deleting the row just yet, only tracking them in this range.
Once finished, you will take all the single cells in the delRng, and using the .EntireRow property, delete them all at once. This is generally faster than deleting rows one at a time.
Public Sub RemoveJunk()
Dim delRng As Range, r As Long
With ThisWorkbook.Worksheets("Sheet3")
For r = 1 To .Cells(.Rows.Count, 1).End(xlUp).Row
If .Cells(r, 1).Value = "-" Then
If delRng Is Nothing Then
Set delRng = .Cells(r, 1)
Else
Set delRng = Union(delRng, .Cells(r, 1))
End If
End If
Next r
End With
If Not delRng Is Nothing Then delRng.EntireRow.Delete
End Sub

VBA Macro with offset

I'm trying to create a simple macro for a sheet I use every day at work.
Basically it's about:
Sheet 1 Cell A2:A11 has values in it those values need to be copy pasted into sheet 2 to with an offset each day to the next free column.
What I've got so far is the copy paste with one offset...but I don't know how to say that the offset should happen for the next free column.
Dim rng As Range
Dim ws As Worksheet
Range("A2:A11").Select
Selection.Copy
Sheets("Sheet2").Select
If rng Is Nothing Then
'if nothing found - search for last non empty column
Set rng = ws.Range("2:2").Find(What:="*", LookAt:=xlWhole, MatchCase:=False, SearchOrder:=xlByColumns, SearchDirection:=xlPrevious)
If rng Is Nothing Then
Set rng = rng.Offset(, 1)
ActiveSheet.Paste
End If
If I understand correctly, try just using this instead of all your current code
Range("A2:A11").Copy Sheets("Sheet2").Cells(2, Columns.Count).End(xlToLeft).Offset(, 1)
Set rng = rng.End(xlToRight).Offset(0, 1)
You go all the way right and then one more for the next free column.

Excel resetting "UsedRange"

Don't know what I'm missing, but the examples I see posted do not appear to work.
I import data from a web query. I set the query to clear unused cells when it re-queries.
I used this imported data to generate a report of variable length.
However if the user (as they need to do in my case) insert rows then the ActiveSheet.UsedRange is expanded. This means I cannot any longer just do a "Ctrl-End" to find the last row in the data set when a new query is performed.
I can easily clear any data with ActiveSheet.UsedRange.Clear. However if the previous query generated a 2 or 3 page report any subsequent query will also be that long even when there is less data because the "UsedRange" still points to that last row way down there.
The examples shown like
ActiveSheet.UsedRange
ActiveSheet.UsedRange.Clear
a = ActiveSheet.UsedRange.Rows.Count
do not reset the range.
MS defines UsedRange as a readOnly property.
It appears what needs to happen is a "File Save" in order to complete the action.
ActiveWorkbook.Save
One post noted that in older versions of Excel you also had to close the workbook and reopen it to complete the action.
I would like to know
1. What is the version cutoff where this behavior changed?
2. Is there some other method using a VBA macro which will reset the range?
I only needed to use Worksheets("Sheet1").UsedRange.Calculate
after deleting rows to reset the range.
Best code that worked for me:
Sub DeleteUnused()
Dim myLastRow As Long
Dim myLastCol As Long
Dim dummyRng As Range
Dim AnyMerged As Variant
'http://www.contextures.on.ca/xlfaqApp.html#Unused
'Helps to reset the usedrange by deleting rows and columns AFTER your true used range
'Check for merged cells
AnyMerged = ActiveSheet.UsedRange.MergeCells
If AnyMerged = True Or IsNull(AnyMerged) Then
MsgBox "There are merged cells on this sheet." & vbCrLf & _
"The macro will not work with merged cells.", vbOKOnly + vbCritical, "Macro will be Stopped"
Exit Sub
End If
With ActiveSheet
myLastRow = 0
myLastCol = 0
Set dummyRng = .UsedRange
On Error Resume Next
myLastRow = _
.Cells.Find("*", after:=.Cells(1), _
LookIn:=xlFormulas, lookat:=xlWhole, _
searchdirection:=xlPrevious, _
searchorder:=xlByRows).Row
myLastCol = _
.Cells.Find("*", after:=.Cells(1), _
LookIn:=xlFormulas, lookat:=xlWhole, _
searchdirection:=xlPrevious, _
searchorder:=xlByColumns).Column
On Error GoTo 0
If myLastRow * myLastCol = 0 Then
.Columns.Delete
Else
.Range(.Cells(myLastRow + 1, 1), _
.Cells(.Rows.Count, 1)).EntireRow.Delete
.Range(.Cells(1, myLastCol + 1), _
.Cells(1, .Columns.Count)).EntireColumn.Delete
End If
End With
End Sub
I've used Jeeped solution and worked for me when i add .Activate, so:
With Worksheets("Sheet1")
Debug.Print .UsedRange.Address(0, 0)
.UsedRange.Clear
.UsedRange
.Activate
Debug.Print .UsedRange.Address(0, 0)
End With
I'm using Excel2013
select cell 1,1 in any sheets you want to reset the UsedRange property
Calculate all worksheets in all open workbooks,
regardless of whether they changed since last calculation
(To Calculate Fully Ctrl+Alt+F9)
Save the workbook
Works for me on all versions of excel
This is what ended up working for me. I feel there has to be a better way but no others worked for me.
Sub ClearRangeData()
Dim S1 As Worksheet
Set S1 = Sheets("Your Sheet Name") 'Define what sheets we are using
'------- Remove all the old data -----
S1.Activate
With ActiveSheet
S1_rows = S1.UsedRange.Rows.Count
For I = S1_rows To 1 Step -1
Cells(I, 1).EntireRow.Delete
Next
End With
End Sub
If you call the Worksheet.UsedRange property by itself, it will reset.
With Worksheets("Sheet1")
Debug.Print .UsedRange.Address(0, 0)
.UsedRange.Clear
.UsedRange '<~~ called by itself will reset it
Debug.Print .UsedRange.Address(0, 0)
End With
This extra step is unnecessary in xl2010 and above with all appropriate service packs installed.
Here is how I inserted your code.
Sheets("Edit Data").Select
' Range("A6").Select
' Range(Selection, ActiveCell.SpecialCells(xlLastCell)).Select
' Selection.Delete Shift:=xlUp
' ActiveWorkbook.Save
With Worksheets("Edit Data")
Debug.Print .UsedRange.Address(0, 0)
.UsedRange.Clear
.UsedRange '<~~ called by itself will reset it
Debug.Print .UsedRange.Address(0, 0)
End With
Here is the full used range with data
Here is the used range after your code executed
The end of range should be i7 instead it is still i26
However the code which I commented out does reset range to i7
From what you are saying just to confirm. My commented out code will only work for Excel 2010 and newer. We have some 2007 versions hanging around. For those the workbook will actually have to be closed and reopened for the range to reset?
Note- the code examples were executed on version 2016
This may or may not suit your data needs, but if your data is all in one contiguous block, you can use CurrentRegion instead of UsedRange, like this:
With Cells(1, 1).CurrentRegion
MsgBox "I have " & .Rows.Count & " rows and " & .Columns.Count & " columns of data."
End With
Of course, if the region you care about does not start at cell A1, or if your sheet contains multiple contiguous regions that you care about, this option will not work. Depending on how predictable your data is, you can usually find at least one cell in each block of data, and once you have that, CurrentRegion will give you the range of the entire block.
Thanks to Claus for having the correct answer, but it is incomplete, and he completed it with a comment on the main post. I'll answer here to combine the useful bits into a single working solution.
Note: I have not tried variations to see which steps are necessary, but these steps work to clear unnecessary used range.
Sub ResetUsedRange()
dim rngToDelete as range
set rngToDelete = 'Whatever you want
rngToDelete.EntireRow.Clear
rngToDelete.EntireRow.Select
Selection.Delete Shift:=xlUp
ActiveWorkbook.Save 'This step has been stated to be necessary for the range to reset.
End Sub
This is the solution I used.
Sub CorrectUsedRange()
Dim values
Dim usedRangeAddress As String
Dim r As Range
'Get UsedRange Address prior to deleting Range
usedRangeAddress = ActiveSheet.UsedRange.Address
'Store values of cells to array.
values = ActiveSheet.UsedRange
'Delete all cells in the sheet
ActiveSheet.Cells.Delete
'Restore values to their initial locations
Range(usedRangeAddress) = values
End Sub
My Excel VBA program imports data from access database by a given date range.
Whenever a new set of data is populated, the worksheet UsedRange does not shrink to reflect only rows with data but instead stays the same even when there are blank rows after the last row with data.
This creates difficulty scrolling down the worksheet to view data.
So to reset UsedRange in order to just reflect rows with data, see below code.
I hope it helps someone.
Sub ResetUsedRange(ByVal oSht As Excel.Worksheet)
' ________________________________________________________________
' Resetting UsedRange to reset vertical scroll bar on Worksheet,
' to reflect ONLY rows with data. This can reset UsedRange on any
' Worksheet. Just pass the worksheet object to this sub.
' ================================================================
Dim iRow1 As Long ' This will get the last row with data
Dim iRow2 As Long ' This will get the number of rows in UsedRange
With oSht
' Format cells to remove wrap text option
' Importing from Access makes some cells with wrap text
.UsedRange.Cells.WrapText = False
' Find last row with data on worksheet
iRow1 = .Cells(.Rows.count, 1).End(xlUp).Row
' Get number of rows in UsedRange
iRow2 = .UsedRange.Rows.count
' If UsedRange rows exceeds the last row with data
' then delete the rows which obviously are blank
If iRow2 > iRow1 Then
.Range(.Cells(iRow1 + 1, 1), .Cells(iRow2, 1)).EntireRow.Delete
End If
' following code forces UsedRange to recalculate and resets
' to reflect ONLY rows with data if blank rows in the UsedRange
' were deleted making the vertical scroll bar to reset itself.
.UsedRange.Calculate
End With
End Sub
I just wanted to put my 2 cents in here since I was also having this problem until I realized the problem.
If you have a UsedRange that is A1:H20 and you delete the row by saying ActiveSheet.Range("A2:H2).Delete you'll find that your UsedRange hasn't updated, it will still show A1:H20. However, if you either say ActiveSheet.Rows(2).Delete or ActiveSheet.Range("A2:H2).EntireRow.Delete You'll find that the UsedRange does update. For some reason you have to delete entire rows or columns (depending your condition) to get that UsedRange to update.
Hope this helps someone!
This worked for me:
Worksheets("Sheet1").UsedRange.Clear
Worksheets("Sheet1").UsedRange = ""
It appears that inserting a value into the UsedRange resets it. After this action I can go
MyCurrentRow = Worksheets("Sheet1").Range("A:A").SpecialCells(xlCellTypeLastCell).Row
MyCurrentRow comes now back as 1, and I can just count from there. When I did not assign a value into UsedRange, that LastCell value did not reset. No Save required.
This works for me in Excel 2010:
Worksheets("Sheet1").UsedRange.Clear
Worksheets("Sheet1").UsedRange.Calculate
I double checked to make sure all the latests patches and service packs have been installed and they were.
I'm running
Windows 10 and
Excel 2016 version 16.0.6568.2034
I found that the range would only reset with the
ActiveSheet.UsedRange.Clear
And most importantly
ActiveWorkbook.Save
without the save command the range is not reset

Copy and paste last used column into next available column

I know this has probably already been answered but I have searched through the previous questions and cannot find an answer that works for me.
Basically I have an excel spreadsheet which can be updated daily/weekly/monthly depending on the workflow. What I need is a macro that finds the last 'used' column(Headers are in row 5), inserts a blank column directly to the right of that - (we have a totals table at the end that needs to move along) & copies the entire last used columns data into that newly created column.
It's probably quite a simple code but I've only just started using VBA and hope someone can help!! I'm hoping once I've started doing some bits and pieces I can build up my knowledge!
Thanks in advance
Emma
From here: Copy last column with data on specified row to the next blank column and here: Excel VBA- Finding the last column with data
Sub Test()
Dim ws As Worksheet
Set ws = ActiveSheet
Dim rLastCell As Range
Dim LastCol As Integer
Set rLastCell = ws.Cells.Find(What:="*", After:=ws.Cells(1, 1), LookIn:=xlFormulas, LookAt:= _
xlPart, SearchOrder:=xlByColumns, SearchDirection:=xlPrevious, MatchCase:=False)
LastCol = rLastCell.Column
ws.Columns(LastCol).Copy ws.Columns(LastCol + 1)
End Sub
Lazy minimalist solution:
Sub Macro1()
Dim col As Integer
col = Range("A5").End(xlToRight).Column
Columns(col).Copy
Columns(col + 1).Insert Shift:=xlToRight
End Sub
Though this will crash if there's nothing in cell A5.

Resources