So my issue is that I have an input sheet that I can't change the formatting of. Unfortunately they used a line of equal signs =========== in a cell for differentiating the sections. I now need to copy/paste certain columns (almost as a whole) including theses "separator cells", however those can't be copied because they aren't recognised as strings. The error msg is Run-time error 1004: Application-defined or object-defined error, which is guess stems from the fact that I want to copy paste a cell with an equal sign in the front, so normally a formula. The columns also include different data types, so I can't really force everything to be a string. How can I skip these or even better copy/paste them into my other worksheet?
My problem is a very isolated issue so I'll just give the relevant lines of code. For context: wksh and wksh2 are declared worksheets, LastRow is the declared last row of the column, etc. It works for other columns, just not the one's with these ======== cells.
Dim arrC As Variant
arrC = wksh.Range("A2:" & LastRow)
wksh2.Range("A2").Resize(UBound(arrC), 1).Value = arrC
First note that "A2:" & LastRow is no valid address as the column letter for the second part is missing. It needs to be something like "A2:A" & LastRow
First Option
One option is to loop through the array and test each element if it begins with = and replace it with '= (the apostroph is not shown but ensures that it is handled as text and not as formula). Note that this will kill all formulas in that range.
Dim arrC As Variant
arrC = wksh.Range("A2:A" & LastRow).Value
Dim iRow As Long
For iRow = 1 To UBound(arrC, 1)
Dim iCol As Long
For iCol = 1 To UBound(arrC, 2)
If Left$(arrC(iRow, iCol), 1) = "=" Then
arrC(iRow, iCol) = "'" & arrC(iRow, iCol)
End If
Next iCol
Next iRow
wksh2.Range("A2").Resize(UBound(arrC), 1).Value = arrC
Second Option
The second option is to set the number format of the destination to text: .NumberFormat = "#" then paste the values and turn it back to general.
Dim arrC As Variant
arrC = wksh.Range("A2:A" & LastRow).Value
With wksh2.Range("A2").Resize(UBound(arrC), 1)
.NumberFormat = "#"
.Value = arrC
.NumberFormat = "General"
End With
Note that .NumberFormat = "#" will turn also numbers into text so you need to turn it back to General to ensure that if there were numbers they are turned back to numbers again and you can calculate with them.
This workaround might have some odd effects on dates or other number formats. So this might not be a reliable solution depending on what data you have.
Third Option
Last option is .Copy and .PasteSpecial
wksh.Range("A2:A" & LastRow).Copy
wksh2.Range("A2").PasteSpecial xlPasteValuesAndNumberFormats
'or without number formats
wksh2.Range("A2").PasteSpecial xlPasteValues
Are you performing any special operation post loading the data into an array? If not then I'd suggest using simple copy and paste routine through VBA which should work reliably.
Public Sub CopyPasteData()
Dim wksh As Worksheet, wksh2 As Worksheet
Set wksh = ThisWorkbook.Sheets(1)
Set wksh2 = ThisWorkbook.Sheets(2)
wksh.Range("A2:" & LastRow).Copy wksh2.Range("A2")
End Sub
Related
I am working on some data organization and need to move information into a form. I have a column of IP addresses and some columns have more than one, or even none. I preferably need to be able to go through the column and keep the first 13 characters and remove everything after it, have 581 lines I need it to be able to run through. I think if I could get a VBA that will run through the column and find the first "," and remove that and anything after it will work. I tried this, but it did nothing.
Sub cleanup()
lastrow = Range("G581").End(xlUp).Row
For i = 2 To lastrow
If Len(Cells(i, 1)) > 13 Then
Cells(i, 1) = Left(Cells(i, 1), 14)
End If
Next i
End Sub
As mentioned before, you are counting the rows in column G, hopefully column G and Column A have the same number of rows.
I'm thinking column G may be the issue.
lastrow = Range("A581").End(xlUp).Row
For i = 2 To lastrow
If Len(Cells(i, 1)) > 13 Then
Cells(i, 1) = Left(Cells(i, 1), 14)
End If
Next i
Honestly there could be a few reasons that you're having trouble. The big one is that when you use the Left() function, you tell it to keep 14 characters instead of the 13 you said that you want!
Another option could be that excel thinks you are trying to do this operation on another sheet. To combat this one, I made a worksheet object and qualified all of the ranges mentioned just to be sure that excel knows what location you're talking about.
An (unlikely based on your description, but possible) problem could be with the way that you specify the lastrow. If you have a column of contiguous values (although your description would suggest you do not) and you specify the last row of that column with a value, the .End(xlUp) will actually trace back up to the last cell with no value which could be the first row - so the loop never even runs! You could avoid this just by changing it to lastrow = Range("G582").End(xlUp).Row (one row below what you know to be the last full row), but why bother when you can just let excel do the work for you like in my answer below.
Sub cleanup()
Dim ws As Worksheet
Set ws = sheets("Sheet1")
Dim lastRow As Long
lastRow = ws.Range("G" & rows.count).End(xlUp).row
Dim i As Long
For i = 2 To lastRow
If Len(ws.Cells(i, 1)) > 13 Then
ws.Cells(i, 1) = Left(ws.Cells(i, 1), 13)
End If
Next i
End Sub
I also made a version with the second option that you mentioned in the question about splitting each row by a comma ",".
Sub cleanup2()
Dim ws As Worksheet
Set ws = sheets("Sheet1")
Dim lastRow As Long
lastRow = ws.Range("G" & rows.count).End(xlUp).row
Dim i As Long
For i = 2 To lastRow
ws.Cells(i, 1) = Split(ws.Cells(i, 1), ",")(0) 'option 2
Next i
End Sub
Hope this solves your problem!
I am currently using the below code to add a line break to cell data in column C and copy it to column K. I need to apply line breaks to a range of data. I have 1000 rows of data in column C.
Any help will be much appreciated.
Sub Macro
Dim Stem As Variant
Stem = ThisWorkbook.Worksheets ("Sheet1").Range("C2")
Range ("K2").Select
Range("K2").FormulaR1C1 = Stem & Chr(10) & ""
End Sub
Thanks
Try this:
Sub copyAndNewLine()
'copy column C to K
Columns("C").Copy Destination:=Columns("K")
'loop through all cells in K and add new line
For i = 2 To Cells(Rows.Count, "K").End(xlUp).Row
Cells(i, "K").Value = Cells(i, "K").Value & vbCrLf
Next i
End Sub
A couple of things:
Better to early bind your variables than late (better memory
management, take advantage of intellisense, etc.)
Usually best
practice to avoid using "select" if possible.
Your Stem variable is an object (Range Object) and thus needs to be "Set"
Try this:
Sub Macro
Dim WS As Worksheet
Dim Stem As Range
Dim R2 As Range
Dim Rng as Range
Set WS = ActiveWorkbook.Sheets("Sheet1")
Set Stem = WS.Range("C2", Cells(WS.Range("C2").End(xlDown).Row, WS.Range("C2").Column))
Set R2 = WS.Range("K2", Cells(Stem.End(xlDown).Row, WS.Range("K2").Column))
R2.Value = Stem.Value
'New Code
For Each Rng In R2
Rng.Value = Rng.Value & Chr(10) & ""
Next Rng
'Old Code: R2.End(xlDown) = R2.End(xlDown) & Chr(10) & ""
End Sub
What this does is first sets the worksheet you're using. Then, you set your working range (Stem) using the Range(cell1, cell2) format. Cell1 I defined as "C2". The next expression there is using the Cells() function. It is the VBA equivalent of being in "C2" and hitting Ctl+Down, then seeing what row you're in.
Then, I set your destination range, R2, in a similar manner, but I used the Stem range to determine how large it should be.
Finally, to get an accurate copy your destination range must be the same size as your from range. The .value to .value expression pastes the data. Then, your extra characters are added on to your new data field.
Something to keep in mind with .End(xlDown)... if you have blank rows in the middle of your data it will stop there, not go all the way down to the end. Hope this helps!
EDIT:
The For Each loop will go through every range (i.e. cell) in your destination range, R2, and add your new characters. Hope that fits better for you.
Thanks to all for your answers. I atlas was able to write my first script and got the code to add line break inside the cell.
Sub AddLineBreak()
Dim Stem As Variant
Stem = ThisWorkbook.Worksheets("Sheet1").Range("C2")
Dim i As Integer
i = 2
'this will terminate the loop if an empty cell occurs in the middle of data
Do While Cells(i, "C").Value <> ""
Cells(i, "K").Value = Cells(i, "C").Value & vbCrLf
i = i + 1
Loop
End Sub
All of the below methods have failed to reference the last column. What is a viable method?
For example 'Columns("1:" & llc.Address & "").Select through 'Columns("E:" & llc & "").Selectare trying to select sayColumns("E:N")`. But the last column is dynamic. In one instance it's column N, and in another application of the macro it's column AP.
Sub RestorePivtTable()
Set ws = ThisWorkbook.Sheets("sheet1")
llc = ws.Cells(2, ws.Columns.count).End(xlToLeft).Column
'Columns("1:" & llc.Address & "").Select
'Columns(1, llc).Select
'Range(Columns(1), Columns(llc)).Select
'Columns("E:" & Cells(3, llc).Address & "").Select
'Range("1:" & Cells(3, lc).Address & "").Select
'Range(Cells(1, 1).Address, Cells(3, llc).Address).Select
'Columns("E:" & llc & "").Select
Selection.ClearFormats
End Sub
If you are using your above method you will need to find the correct row to use. ie: you will need to know the row in which the data appears in the right-most column. If you want the last column out of anything, try:
ws.usedrange.columns
This just gives the number of columns in the used range of a sheet, which is defined as A1:[The bottom right cell which contains either values or formatting].
Note that this will not work if, say, you have formatting in E10, but you want to get column D, because D is the last column which has a value [ie: you want to exclude consideration of formatted cells].
I generally use this method, although you have to put checks in in case the sheet is empty (you can't return column 0).
Sub FindLastColumn()
Dim wrkSht As Worksheet
Set wrkSht = ThisWorkbook.Worksheets("Sheet1")
MsgBox wrkSht.Cells.Find(What:="*", After:=wrkSht.Cells(1, 1), SearchDirection:=xlPrevious).Column
End Sub
Basic example of how to find the last column in your sheet - I've included an If block in case the sheet is empty, but then I don't know why you would run this code on an empty sheet anyway...
Sub SO()
Dim lastCol As Integer
Dim lastCell As Excel.Range
'// Assuming the variable 'ws' has already been dimensioned and initialised
On Error Resume Next
Set lastCell = ws.Cells.Find(What:="*", After:=ws.Range("A1"), SearchDirection:=xlPrevious)
On Error GoTo 0
If lastCell Is Nothing Then
lastCol = 1
Else
lastCol = lastCell.Column
End If
MsgBox lastCol
End Sub
UsedRange can be unreliable in this instance, because it can still contain cells that were previously used but are now blank - and I'm guessing you're not interested in these cells. Using the Cells.Find() method means that you don't have to know which row will coincide with the last column (which is needed for Columns.Count.End(xlToLeft) method) so this is a bonus too when working with irregular data sets.
Trying to "read between the lines" of your code, I suspect that this is what you are after:
Public Sub RestorePivtTable()
Sheet1.Cells(2, Sheet1.Columns.Count).End(xlToLeft).EntireColumn.ClearFormats
End Sub
This will work as long as there are data in row 2.
Thanks everyone for your help. The below function and macro solved the issue of converting a column number reference into a letter reference:
Function GetColumnLetter(colNum As Long) As String
Dim vArr
vArr = Split(Cells(1, colNum).Address(True, False), "$")
GetColumnLetter = vArr(0)
End Function
Sub RestorePivtTable2()
Dim lc As Long
Dim ws As Worksheet
Set ws = ThisWorkbook.Sheets("PivtTable")
lc = ws.Cells(5, ws.Columns.count).End(xlToLeft).Column
myCol = GetColumnLetter(lc)
Columns("E:" & myCol & "").Select
Selection.ClearFormats
End Sub
Starting from Sheet "DATA" range B4:Hx, where x is my last row taking by a row count. I need to copy this range and paste it as values on sheet "bat" starting at A1.
Going forward I need to offset columns in 6. So my second copy will be I4:Ox and so one copying appending into bat sheet.
I know where I must stop and I'm informing it using the Funds value.
The first error I'm having is when I try set Column2 = Range("H" & bottomD) value that is giving me "overflow".
And sure I don't know yet if my For loop would work.
Sub Copy_bat()
Dim bottomD As Integer
Dim Column1 As Integer
Dim Column2 As Integer
Dim i As Integer
Dim Funds As Integer
Funds = Sheets("bat").Range("u3").Value
Sheets("DATA").Activate
bottomD = Range("A" & Rows.Count).End(xlUp).Row
Column1 = Range("B4")
Column2 = Range("H" & bottomD)
For i = 1 To Funds
Range(Column1 & ":" & Column2).Copy
Sheets("Data").Cells(Rows.Count, "A").End(xlUp)(2).PasteSpecial Paste:=xlPasteValues, SkipBlanks:=True, Transpose:=False
Column1 = Colum1.Range.Offset(ColumnOffset:=6)
Column2 = Colum2.Range.Offset(ColumnOffset:=6)
Next i
End Sub
Always use Option Explicit at the beginning of every module to prevent from typos. Always! You had typos at the bottom - Colum1 and Colum2.
Avoid Activate and Select (you had Sheets("DATA").Activate) - better performance, smaller error chance. Instead, you should always explicitly tell VBA which sheet you are referring to.
While pasting values you can simply do something like Range2.value = Range1.value. No need to .Copy and then .Paste.
I did my best to understand what you need. From my understanding you did not use Range data type, while you needed that. This caused you errors.
Option Explicit
Sub Copy_bat()
Dim bottomD As Integer
Dim i As Integer
Dim Funds As Integer
Dim rngArea As Range
Funds = Sheets("bat").Range("u3").Value
With Sheets("Data")
bottomD = .Range("A" & .Rows.Count).End(xlUp).Row
Set rngArea = Range(.Range("B4"), .Range("H" & bottomD))
End With
For i = 1 To Funds
Sheets("bat").Cells(Rows.Count, "A").End(xlUp)(2).Resize(rngArea.Rows.Count, rngArea.Columns.Count).Value = _
rngArea.Value
Set rngArea = rngArea.Offset(, 7)
Next
End Sub
I made one rngArea variable of type Range instead of 2 variables (Column1 and Column2). This code takes info from "Data" sheet and puts that to "bat" sheet. Then offsets to right by 7(!) columns in "Data" sheet and puts data in "bat" sheet below the data that was put previously.
I have an issue when trying to use Union(Range, Range). I am trying to copy certain rows from one worksheet and paste them in a new file. My issue is that the Union isn't adding on more rows to the range; it returns the original range. If I flip the order of the parameters, it returns only the .Rows(i + 1) row. My test data has 2 rows that it should copy. The row count at the end is 1. What am I doing wrong?!
Dim lastRow, i As Long
Dim CopyRange As Range
lastRow = ActiveSheet.Rows.count
With Sheets(ActiveSheet.Name)
lastRow = .Range("A" & .Rows.count).End(xlUp).Row
For i = 2 To lastRow
Dim endTime As Date
endTime = DateValue(Trim(.Range("E" & i).Value))
If endTime = Date - 1 Then
If CopyRange Is Nothing Then
Set CopyRange = .Rows(i + 1)
Else
Set CopyRange = Union(CopyRange, .Rows(i + 1))
End If
End If
Next
End With
CopyRange.Copy
Actually CopyRange contains a number of separate ranges (areas). See here for more information.
When you make Union operation with separate rows (I mean that there is another row beetween them) it doesen't actually add Row, but add new Area. So, if you add MsgBox CopyRange.Areas.Count, you will see count of areas (if you will add MsgBox CopyRange.Rows.Count it will get you uncorrect result in case of many areas - it will get count of rows in first area).
As a conclusion, your code works well for me, and should works well for you. You can add CopyRange.Select line before CopyRange.Copy and set breakpoint on this line. You will see that union works well