I have a very large range of complicated formulas that use an external data source. My users don't have access to this data source, therefore I want to convert the range to values before sending the sheet.
Range=
=VALs!$A$1,VALs!$D$1:$D$45,VALs!$F$1:$G$45,VALs!$I$1:$J$45,VALs!$N$1:$N$45,VALs!$R$1:$T$45,VALs!$V$1:$V$45,VALs!$X$1:$Y$45,VALs!$AB$1:$AB$45,VALs!$AE$1:$AE$45,VALs!$AT$1:$AT$45,VALs!$AW$1:$BB$45,VALs!$BD$1:$BD$45,VALs!$BG$1:$BG$45,VALs!$BK$1:$BK$45,VALs!$BQ$1:$BQ$45,VALs!$BT$1:$BT$45,VALs!$CC$1:$CD$45,VALs!$CJ$1:$CJ$45,VALs!$CN$1:$CN$45,VALs!$CP$1:$CQ$45,VALs!$CU$1:$CW$45,VALs!$DA$1:$DC$45,VALs!$DH$1:$DH$45,VALs!$DJ$1:$DJ$45,VALs!$DL$1:$DL$45,VALs!$DO$1:$DO$45,VALs!$DR$1:$DS$45,VALs!$DV$1:$DV$45,VALs!$DZ$1:$EA$45,VALs!$EV$1:$EV$45,VALs!$EY$1:$EY$45,VALs!$FB$1:$FC$45,VALs!$FE$1:$FL$45
Now:
Copy & paste as values does not work on multiple selections
With Sheets("VALs").Range("values").CurrentRegion
.Value = .Value Does not work, I end up with empty cells in place of the range
The range is dynamic and will change often, that's why it's named, there is a separate sub to create the range
Would anyone be able to help out?
Do you mean something as simple as this? (In this case, the Named Range is named "RNG".)
Sub RangeToValues()
For Each cel In Range("RNG").Cells
cel.Value = cel.Value
Next cel
End Sub
Or is the problem more complex and I didn't understand?
Related
Can someone explain why there is a difference between filtering a range and a table? I get the difference between the two, but I dont see why a range filter cannot filter the underlying data when its formatted as a table.
I've been having issues filtering files due to the difference. I am consolidating x number of user files with the same headers. However, some people (thanks a lot, Charles across the hall) decide to format as a table which is causing issues.
Is there a universal way (or more effective way) to filter data that is independent of the format? As an example, I am trying to filter for text "NORAM" in Col A, and then copy/paste visible cells elsewhere.
Do I need to do something like this?
If Range("A1") is range Then
Filter Range("A1") with range method ("NORAM")
Copy Visible Cells
Paste to Destination
Else
Filter Range("A1") with table method ("NORAM")
Copy Visible Cells
Paste to Destination
End If
Or is there a way to do something like this, which is independent of range or table?
Range("A1") filter range regardless of format ("NORAM")
Copy Visible Cells
Paste to Destination
I do not need help copying or pasting data. The above are just generic steps. I'm hoping to see the most effective way to filter a range, a table, and hopefully a way to filter both regardless of format.
I'm not sure what error you're getting but here's a quick filter, tested on both a table (Sheet1) and a range not formatted as a table (Sheet2).
Option Explicit
Private Sub FilterTableOrRange(ws As Worksheet)
Dim lastRow As Long, lastCol As Long
With ws
lastRow = .Cells(.Rows.Count, 1).End(xlUp).Row
lastCol = .Cells(1, .Columns.Count).End(xlToLeft).Column
.Range(.Cells(1, 1), .Cells(lastRow, lastCol)).AutoFilter _
Field:=1, Criteria1:="NORAM"
End With
End Sub
Sub Master()
FilterTableOrRange Sheets("Sheet1")
FilterTableOrRange Sheets("Sheet2")
End Sub
If this is not helpful, then please specify the exact issues you've had.
I am developing a financial model for a bank and come across the below issue which I am not able to resolve in Excel VBA, and would appreciate your help.
I have built a simple macro which essentially does two things: (i) it clears contents in a given range, (ii) it populates the same range with a formula. In a very abbreviated way it looks like the following:
Sub AutoCalculateCashFlows()
Range(D208:L208).ClearContents
Range("L208").FormulaR1C1 = "=+R[-34]C-R[-34]C[-1]"
Range("L208").AutoFill Destination:=Range("E208:L208"), Type:=xlFillDefault
End Sub
My problem is that the range that should be auto populated is dependent on how many cells did the user fill in within the range of E10:L10. Users will start populating this range from right to left, but I don't know how far they will go from column L to the left. The formula that my macro auto populates needs at least two data, ie. at least L10 and K10 should be populated and if the latter is the case then the macro only needs to auto populate L208 with formula, in case J10:L10 is filled out then the macro needs to auto populate the range L208:K208 and so on to the point that in case the full D10:L10 range is filled out then E208:L208 should be populated with formula.
I have thought to resolve this issue via two routes: (i) approaching it as a dynamic range problem in which case I need a vba code to determine the previous to the last cell populated by the user in the range D10:L10 and use the column code of that cell in "Destination:=Range("E208:L208")", or (ii) run a loop which will populate range E208:L208 with formula until the cell in the previous column within range D10:L10 is filled in by the user and stop when it is not.
Hope this makes sense and thanks in advance for the help.
When you need a dynamic range in VBA, you should simply build one. This is probably the easiest method:
Sub TestMe()
Dim colRange As Long
Dim rowRange As Long
Dim rngMain As Range
rowRange = 10
With Worksheets(1)
colRange = .Cells(1, .Columns.Count).End(xlToLeft).Column
Set rngMain = .Range(.Cells(rowRange, colRange), .Cells(100, 200))
MsgBox rngMain.Address
End With
End Sub
It is dynamic, based on the last used column in row 1 of the first Worksheet.
Concerning the second used column in Row 1, one of these 3 would probably do it for you, depending on what exactly do you want:
.Cells(1, 1).End(xlToRight).End(xlToRight).Column
.Cells(1, 1).End(xlToRight).Column
.Cells(1, 1).End(xlToRight).Column + 1
I have three parts to this problem. I have a single cell with a Week number in Sheet1!A1. I have a static range in Sheet1!B1:F1 that needs to be copied. Then I need to paste the value in a dynamic range in Sheet2 offset by the week number for rows. This is part of a larger macro I am writing for a sheet I use regularly, but I seem to have those parts down. I may be either oversimplifying or oversimplifying but this is what I have currently.
Sub CopyPaste()
Sheets(1).Range("B1:F1").Copy
OffsetRange = Sheets(1).Cells(1,1).Value
Sheets(2).Cells(1+OffsetRange,1).Paste
End Sub
When I run this, it either gives me a Runtime Error 9 or Runtime Error 438.
Anyone know whats causing these errors? When I paste the range, does the cells object point towards the first cell of the copied range when I paste in at the location?
Try it as,
Option Explicit
Sub CopyPasteOffset()
Dim OffsetRange As Long
OffsetRange = Worksheets(1).Cells(1, 1).Value
Worksheets(1).Range("B1:F1").Copy _
Destination:=Worksheets(2).Cells(1 + OffsetRange, 1)
End Sub
The .Paste method is a member of Worksheet, not Range or Cells. You may have it confused with .PasteSpecial which is a member of the Range object. In any event, it is unnecessary as a destination can be applied directly to the copy command.
I'm trying to copy only data (excluding blanks created by If statement) from "Data" tab then paste to the bottom of a data column on the "Summary" tab. The trouble arises from trying to figure out how to get VBA to recognize the range of usable data.
There are a couple of different ways to do this, depending on what you need. Here's one SO thread that discusses a few uses. Here's another page that discusses using UsedRange or .Rows. And, as #findwindow noted, you can use .xlEnd.
This is a pretty common use of VBA, so if you Google around (or even look through SO), you'll find some information.
Edit: Per your comment, just set a range, and loop through the cells in the range until you find a non-numeric number:
Dim rng as Range, cel as Range
Dim lastRow as Integer
Set rng = Range("A1:A10000")
for each cel in rng
If not isnumeric(cel.value) then
'Do whatever code you want, when the cell is NOT numeric, ie
lastRow = cel.Row
End if
next cel
I would like to change the following code to compare entire rows instead of individual cells. I'm a beginner at vba so please explain in simple terms.
Sub RunCompare()
Call compareSheets("Sheet1", "Sheet2")
End Sub
Sub compareSheets(shtBefore As String, shtAfter As String)
'Compares sheets by cells and highlight difference
Dim MyCell As Range
Dim mydiffs As Integer
For Each MyCell In ActiveWorkbook.Worksheets(shtAfter).UsedRange
If Not MyCell.Value = ActiveWorkbook.Worksheets(shtBefore).Cells(MyCell.Row, MyCell.Column).Value Then
MyCell.Interior.Color = vbYellow
mydiffs = mydiffs + 1
End If
Next
MsgBox mydiffs & " differences found", vbInformation
ActiveWorkbook.Sheets(shtAfter).Select
End Sub
The Range object is a strange beast in Excel and it can take some getting used to its various characteristics.
The phrase
ActiveWorkbook.Worksheets(shtAfter).UsedRange
delivers a Range object and when you use the loop
For Each MyCell In ActiveWorkbook.Worksheets(shtAfter).UsedRange
what you are actually doing is implicitly relying on the Cells property of the Range object to deliver an object that contains all the cells in that range. Excel's help system (in version Office 2010, at least) also indicates this latter object is a Range object and I suspect this is a source of confusion amongst beginners, because each of the cells is also a Range object in its own right (so the Cells property of a Range delivers an object which is also a Range though different from its parent and which has "elements" each of which is a Range)
The loop above is really a shorthand form of
For Each MyCell In ActiveWorkbook.Worksheets(shtAfter).UsedRange.Cells
The Range object has many properties, one of which is the Rows property. The phrase
ActiveWorkbook.Worksheets(shtAfter).UsedRange.Rows
delivers an object that contains the separate rows of your UsedRange and you can then use a loop such as
For each myRow in ActiveWorkbook.Worksheets(shtAfter).UsedRange.Rows
to look at each row in turn. Here myRow is also a Range object. Again, perhaps confusingly, the Rows property also delivers a Range object which contains "elements", each of which is also a Range object.
Unfortunately, you cannot implicitly rely on the Cells property with the myRow object to loop over the individual cells within each row. So
For each myCell in myRow
doesn't work as you'd hope but by explicitly adding the Cells property
For each myCell in myRow.Cells
does.
In summary, you can achieve your row by row comparison by using two loops: the first for the rows (based on the Rows property) and a second, nested inside the first, for the cells within a row (based on the Cells property).
As an aside, you can do much of what you want without using VBA at all. Array formulas in Excel (the ones that require CTL+SHIFT+ENTER when entered from the formula bar) can compare two arrays. For example, the array formula
{=AND(Sheet1!A1:Z1=Sheet2!A1:Z1)}
tells whether the range A1:Z1 is the same on two different worksheets and there are other array formulas which can be used to count the number of differences between two ranges.
If you want to highlight the differences between cells in two worksheets use conditional formatting. The trick here is to set the conditional formatting using a formula on the first cell and then to copy this formatting to the other cells. So set the conditional formatting for cell Sheet2!A1 using the formula
=Sheet1!A1<>Sheet2!A1
Make sure that the formula uses relative A1 rather than absolute $A$1 cell addresses (editting the formula if necessary) and then Copy the format (using Paste Special Format) from cell Sheet2!A1 to the rest of the cells on Sheet2.
There is no way to compare row. You can improve your current method.
1. Set mydiffs to long type (vba initial value with long, so no need convert to integer)
2. Add Application.ScreenUpdating = False to enhance the script performance.
Sub RunCompare()
Call compareSheets("Sheet1", "Sheet2")
End Sub
Sub compareSheets(shtBefore As String, shtAfter As String)
'Compares sheets by cells and highlight difference
Dim MyCell As Range
Dim mydiffs As Long
Application.ScreenUpdating = False
For Each MyCell In ActiveWorkbook.Worksheets(shtAfter).UsedRange
If Not MyCell.Value = ActiveWorkbook.Worksheets(shtBefore).Cells(MyCell.Row, MyCell.Column).Value Then
MyCell.Interior.Color = vbYellow
mydiffs = mydiffs + 1
End If
Next
MsgBox mydiffs & " differences found", vbInformation
ActiveWorkbook.Sheets(shtAfter).Select
Application.ScreenUpdating = True
End Sub