Copy and paste data from dynamic table while exluding formula blanks - vba

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

Related

Excel VBA Dynamic Range / Loop issue

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

Create a range using named cells

I need to create a range using named cells in vba.
So far I have the following which is not working;
Dim pasteRange As Range
Set pasteRange = Range(firstRow, 11)
pasteRange.Value(11) = slabTemplateSheet.Range("slabTemp").Value(11)
Where firstRow is an Integer. slabTemplateSheet refers to a worksheet and slabTemp is a named range in said sheet.
I thought it would be fairly simple as my paste range is only 1 row and 11 columns (i.e 11 cells in a row) but I can't get it to work.
In your answer, presuming there is one, could you also please give me the ability to paste multiple rows and columns, so for instance if slabTemp refers to A1:F16
Edit: I didn't make it clear what I am trying to do.
I have a named range called slabTemp in the worksheet slabTemplateSheet. In another sheet I want to copy that range, including the formatting, and paste it. I heard that using the copy/paste function was slow so I found the property above that supposedly does the same thing but is faster (i haven't tested it). Source, Durgesh's answer here: fast way to copy formatting in excel
In the new sheet I need to paste it into a range which is to be created (this is what i don't know how to do)
So Range(firstRow, 11) refers to the integer saved as firstRow (a row number) and 11 is the column number. But this doesn't work.
I guess my question, is how do i create a range using names rather than say Range("A1:G6") so instead Range(firstRow1:secondRow:6)
Thanks Again!
Here's a working example.
Public Sub CopyRange()
'Define the Copy Range
Dim CopyRange As Range: Set CopyRange = Range("MyCustomRange")
'Define the range to Paste Value to
Dim PasteRange As Range: Set PasteRange = Range("MyOtherCustomRange")
'Move the Copy Range into the Paste Range
'You don't need to specify the sheet name, the Defined named holds that information
'Important: The ranges should be the same size, otherwise you may get an error
PasteRange.Value() = CopyRange.Value()
End Sub
About how to create a Named Range (worksheet scope, not for Workbook per your code), use the code below. Not sure what are you trying to achieve in this line:
pasteRange.Value(11) = slabTemplateSheet.Range("slabTemp").Value(11)
Anyway, this is the code for the Named Range:
Sub UseNamedRange()
Dim slabTemplateSheet As Worksheet
Dim pasteRange As Range
Set slabTemplateSheet = Sheets("Sheet1")
' create the Named Range "slabTemp" (Named Range for Worksheet)
' you can manualy (or with variables) modify the Range("A1:F16")
slabTemplateSheet.Names.Add "slabTemp", Sheets("Sheet1").Range("A1:F16")
Set pasteRange = slabTemplateSheet.Range("slabTemp")
End Sub
It doesn't seem like you are trying to transfer the xlRangeValueXMLSpreadsheet XlRangeValueDataType enumeration of the cells; rather it sounds like you want 11 cells in a row.
with slabTemplateSheet.Range("slabTemp")
pasteRange.Resize(1, 11) = .Cells.Resize(1, 11).Value
end with

range names not in name manager

Running VBA code allowed me to detect that applying a filter in a sheet creates a range name that does NOT appear in the name manager (the range name either just associates to the headers or the whole table). Is there any way of knowing why this happens and/or a way of preventing it? Is it just an Excel glitch?
the part of the vba code that causes the error I tried to run was the following:
For Each Rng In ActiveWorkbook.Names
Set Rng2 = Range(Rng)
If Not Intersect(Rng2, Range(rng1.Offset(1, 0), rng1.End(xlDown)).EntireRow) Is Nothing Then ActiveWorkbook.Names(Rng.Name).Delete
Next Rng
When I was debugging, I noticed that my Rng (which is a name object, by the way) points towards a range I never created (and I know this because the sheet that it's in has no other range names and I never put any in it)
I used this vba code to verify that the name existed in this sheet:
Sub test()
Dim Rng As Name
For Each Rng In Sheets("WindHail Zone 2").Names
'ActiveWorkbook.Names(Rng.Name).Delete
MsgBox Rng
Next Rng
End Sub
I removed the comment block from the first line of the for loop to the second line to delete it. I removed the filter on that tab and reput it on, only to come to the same issue.
Thanks!
Not so clear to me what your're trying to achieve but as long as "hidden" names and range intersection are involved you must take into account what follows:
filtering does create hidden "names"
but all of them ends with the string "_FilterDatabase"
range intersection wold return an error if applied to ranges not belonging to the same sheet
so here what you should try
For Each Rng In ActiveWorkbook.Names
Set rng2 = Range(Rng)
If rng2.Parent.Name = rng1.Parent.Name And InStr(Rng.Name, "_FilterDatabase") = 0 Then 'the first check is for ranges belonging to the same worksheet and the second is for rng2 not deriving from any filtering
' now you can safely "Intersect"
If Not Intersect(rng2, Range(rng1.Offset(1, 0), rng1.End(xlDown)).EntireRow) Is Nothing Then ActiveWorkbook.Names(Rng.Name).Delete
End If
Next Rng

Convert range (non-continuous) to values

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?

Limit to number of "named ranges" in Range method

I am using the following code:
Dim rng As Range
Set rng = Workbooks("import sheet.xls").Sheets("import").Range("project_name,project_author,project_code,project_breaker,default_fault_ac_mcb,default_fault_ac_mccb,default_fault_dc,default_fault_acb,default_rvdrop,default_svdrop,default_eff,default_pfactor,default_ratio,default_freq,default_sfactor_ac,default_spfactor")
For Each cell In rng.Cells
MsgBox cell
Next cell
Now project_name, project_author, etc. are different named ranges in the sheet. The problem is when I add another named range to the above list (already defined), I get a runtime error 1004 ("select method of range class failed").
Is there a limit to the number of named ranges one can add to range object?
I couldn't find anything on why this happens but I am still looking.
However this works. You can combine the range using UNION
Sub Sample()
Dim rng As Range, cell As Range, newRng As Range
Set rng = Sheets("import").Range("project_name,project_author,project_code,project_breaker,default_fault_ac_mcb,default_fault_ac_mccb,default_fault_dc,default_fault_acb,default_rvdrop,default_svdrop,default_eff,default_pfactor,default_ratio,default_freq,default_sfactor_ac,default_spfactor")
Set newRng = Union(rng, Sheets("import").Range("default_sid"))
For Each cell In newRng.Cells
MsgBox cell
Next cell
End Sub
UPDATE: I couldn't find any Microsoft document which describes this behavior, but after some experimentation, I can confirm that the maximum number of named ranges one can add to range object is 16. This behavior can be seen in in Excel 2003, 2007, and 2010.
Strangely this behavior was not replicated in Excel 2011 (MAC). I tested with 19 names and it worked.
MORE UPDATE
After more testing, I discovered that if you name your ranges as Name1, Name2, Name3 etc then I was able to add more named Ranges in Excel 2003-2007-2010. So it has to do something with the naming of the names.
I have filed a bug with Microsoft.
SAMPLE FILE FOR TESTING:
https://skydrive.live.com/redir.aspx?cid=cdd3f8abe20bbe3b&resid=CDD3F8ABE20BBE3B!157&parid=CDD3F8ABE20BBE3B!150&authkey=!ADP_QVBopsqenBA
I have not investigated your problem but the points below may give you and Sid some hints on the possible solution.
If you access Rng.Address there is a limit on the size of the string. I have found comments on the internet claiming the limit is 253 but I do not believe this. Rng.Address is always truncated at the end of a complete sub-range so the string you get back is syntactically correct just incomplete. So the limit depends on the exact string size of the sub-ranges.
I had the opposite problem. I had a sheet with thousands of rows. I used AutoFilter to select the rows of interest and saved the range. But Rng.Address only gave me the first 19 or so ranges of interest. I had to use For Each Cell In Rng and assemble the range string to get the full string.
Your string is 255 characters long. There is clearly no limit on the number of sub-ranges in a union. You could try adding the sub-ranges to the union individually.
Best of luck.
PS If you want my assemble range string routine you are welcome.