I have a number of named ranges within the scope of my Workbook.
Now I want to loop over each named range by following the second answer of this related topic
Sub Range1()
Dim i As Integer
For i = 2 To ActiveWorkbook.Names.Count
MsgBox ActiveWorkbook.Names(i).Name
Next i
End Sub
This macro shows me all the names such as C_1 in a msgbox. I modify the script in the following way to "call" each of the ranges based on the name of the corresponding range.
Sub Range2()
Dim i As Integer
Dim rng As Range
Dim nm As Name
Dim rng_name As String
For i = 2 To ActiveWorkbook.Names.Count
Set nm = ActiveWorkbook.Names(i)
rng_name = nm.Name
Set rng = Application.Range(nm.Name) `This line yields an error
MsgBox rng.Address
Next i
End Sub
The error I get is:
"Run-time error '1004': Method 'Range' of object'_ Apolication failed
Any idea what I am doing wrong? If I inspect the elements I see that rng_name is equal to "C_1".
EDIT
I believe that it has something to do with how the named ranges are created. This is done in the following way:
Sub createRanges()
Dim LastRowAll As Long, LastRowUnique As Long
Dim x, y
Dim rng As Range
Dim rng_name As String
LastRowUnique = Sheets("Lists").Range("J2").End(xlDown).Row
LastRowAll = Sheets("Deribit").Range("D8").End(xlDown).Row
For Each x In Sheets("Lists").Range("J2:J" & LastRowUnique)
For Each y In Sheets("Deribit").Range("D8:D" & LastRowAll)
If y.Offset(0, -1).value = "Call" Then
If rng Is Nothing And y = x Then
Set rng = y.Offset(0, -2)
ElseIf y = x Then
Set rng = Union(rng, y.Offset(0, -2))
End If
Else:
End If
Next y
rng_name = "C_" & x.Offset(0, -1).value
ThisWorkbook.Names.Add Name:=rng_name, RefersTo:=rng.Address
Set rng = Nothing
Next x
See:
For i = 1 To ActiveWorkbook.Names.Count
Set nm = ActiveWorkbook.Names(i)
Set rng = ActiveWorkbook.Names(nm.Name) 'This is where the error was
MsgBox rng.Address
Next i
I also dropped the rng_name, since you didn't use it.
Edit1:
I was unable to get Application.Range(nm.Name) to work originally, but was able to get ActiveWorkbook.Range(nm.Name) to work, using ranges on different sheets named "c" and "d". I noted that nm = ActiveWorkbook.Names(i) did not include the sheet name when it was recorded.
When naming ranges with the underscore and number in them (I tried c_1 & c_2) I was unable to get my code to work; on inspecting nm in debug mode, I noticed that it also listed the sheet name. I had to specify that than rng would .RefersToRange to get the cell address to show up without the sheet name.
I did two additional checks, one with the underscore without number (used "d_") and another with a number and no underscore (used "name2"), and both did give me the same error. Both scenarios kept the Sheet name in the string nm.
Here is the testing code that I was executing/stepping through to sort that out:
Dim rng As Range, i As Long
For i = 1 To ActiveWorkbook.Names.Count
Set nm = ActiveWorkbook.Names(i)
'Set rng = ActiveWorkbook.Names(nm.Name)
Set rng = nm.RefersToRange
'Debug.Print rng.Address
MsgBox rng.Address
Next i
Using Set rng = nm.RefersToRange worked in all scenarios, while alpha-character only names worked with the Set rng = ActiveWorkbook.Names(nm.Name).
Suggestion, stick with .RefersToRange as the robust code.
Related
I have been working on automating different parts of the process of formatting a very large data set. I am stuck on trying to automate the "remove duplicates" command across all blocks of my data:
I have blocks of data (9 columns wide, x rows long) as on the image attached. In the column called "#Point ID" are values 0-n. Some values appear once, some values appear more than once. Different blocks have different "#Point ID" columns
I would like to delete all rows in the block where the value in the "#Point ID" column has already occurred (starting from the top, moving down the rows). I would like the deleted rows removed from the blocks, so only the rows (which are blue on the image) with unique values in "#Point ID" column (green on the image) remain.
I have found VBA modules that work on a single block, but I don't know how to make it function across all my blocks. Delete rows in Excel based on duplicates in Column
I have also tried combinations of functions (inc. UNIQUE and SORTBY) without any success.
What's a function or a VBA module that works?
Use this
Public Sub cleanBlock(rng As Range)
Dim vals As Object
Set vals = CreateObject("Scripting.Dictionary")
Dim R As Range
Dim adds As Range
For Each R In rng.Rows
If (vals.exists(R.Cells(1, 2).Value)) Then
If adds Is Nothing Then
Set adds = R
Else
Set adds = Union(adds, R)
End If
Else
vals(R.Cells(1, 2).Value) = True
End If
Next R
Debug.Print (adds.Address)
If Not adds Is Nothing Then adds.Delete shift:=xlUp
Set vals = Nothing
End Sub
Public Sub test()
cleanBlock Range("b3:j20")
cleanBlock Range("l3:t20")
cleanBlock Range("y3:ad20")
End Sub
Remove Duplicates in Areas of a Range
Sub RemoveDupesByAreas()
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
Dim ws As Worksheet: Set ws = wb.Sheets("Sheet1") ' adjust!
Dim rg As Range: Set rg = ws.UsedRange.SpecialCells(xlCellTypeConstants)
Dim aCount As Long: aCount = rg.Areas.Count
Dim arg As Range, a As Long
For a = aCount To 1 Step -1
Set arg = rg.Areas(a)
Debug.Print a, arg.Address(0, 0)
' Before running the code with the next line, in the Immediate
' window ('Ctrl+G'), carefully check if the range addresses
' match the areas of your data. If they match, uncomment
' the following line to apply remove duplicates.
'arg.RemoveDuplicates 2, xlYes
Next a
MsgBox "Duplicates removed.", vbInformation
End Sub
Find and FindNext feat. CurrentRegion
Sub RemoveDupesByFind()
Const SEARCH_STRING As String = "Source.Name"
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
Dim ws As Worksheet: Set ws = wb.Sheets("Sheet1") ' adjust!
Dim rg As Range: Set rg = ws.UsedRange
Dim fCell As Range: Set fCell = rg.Find( _
SEARCH_STRING, , xlFormulas, xlWhole, xlByRows, xlPrevious)
If fCell Is Nothing Then
MsgBox """" & SEARCH_STRING & """ not found.", vbCritical
Exit Sub
End If
Dim FirstAddress As String: FirstAddress = fCell.Address
Do
fCell.CurrentRegion.RemoveDuplicates 2, xlYes
Set fCell = rg.FindNext(fCell)
Loop Until fCell.Address = FirstAddress
MsgBox "Duplicates removed.", vbInformation
End Sub
Another way, maybe something like this :
Sub test()
Dim rgData As Range
Dim rg As Range: Dim cell As Range
Dim rgR As Range: Dim rgDel As Range
Set rgData = Sheets("Sheet1").UsedRange 'change as needed
Set rgData = rgData.Resize(rgData.Rows.Count - 1, rgData.Columns.Count).Offset(1, 0)
For Each rg In rgData.SpecialCells(xlConstants).Areas
For Each cell In rg.Columns(2).Cells
Set rgR = cell.Offset(0, -1).Resize(1, rg.Columns.Count)
If cell.Value = 0 And cell.Offset(1, 0).Value <> 0 And cell.Offset(0, 1).Value = 0 And cell.Address = rg.Columns(2).Cells(1, 1).Address Then
Else
If Application.CountIf(rg.Columns(2), cell.Value) > 1 And cell.Offset(0, 1).Value = 0 Then
If rgDel Is Nothing Then Set rgDel = rgR Else Set rgDel = Union(rgDel, rgR)
End If
End If
Next cell
Next rg
rgDel.Delete Shift:=xlUp
End Sub
The code assumed that there'll be no blank cell within each block and there will be full blank column (no value at all) between each block. So it sets the usedrange as rgData variable, and loop to each area/block in rgData as rg variable.
Within rg, it loop to each cell in rg column 2, and check if the count of the looped cell value is > 1 and the value of the looped cell.offset(0,1) is zero, then it collect the range as rgDel variable.
Then finally it delete the rgDel.
If you want to step run the code, try to add something like this rg.select ... rgR.select .... after the variable is set. For example, add rgDel.select right before next area, so you can see what's going on.
The code assume that :
the first value right under "#Point" in each block will be always zero. It will
never happen that the value is other than zero.
the next value (after that zero value) is maybe zero again or maybe one.
if there are duplicates (two same value) in column #Point then in column X, it's not fix that the first one will always have value and the second one will always zero value.
If the data is always fix that the first one will always have value and the second one will always zero value (if there are duplicate), I suggest you to use Mr. VBasic2008 or Mr. wrbp answer. Thank you.
I'm looping through a list of items most of which are numbers but occasionally I get a string.
I would like to skip the strings and go to the next row without breaking the loop.
I am defining the numbers as doubles so the strings give me a type mismatch errror.
I think I should be using some sort of IF test but am unsure how to tell VBA to skip the 'wrong' variables.
I have tried using variants to avoid the error but can't find an IF test to tell them apart.
If you want to get only explicit numbers excluding thereby also any text-formatted numbers (NumberFormat = "#"), which would be interpreted as Double anyway, you might code as follows checking for the variable type (VarType) as well as for the NumberFormat:
Sub ExplicitNumbersOnly()
Dim rng As Range
Set rng = Tabelle1.Range("A2:A10")
Dim i As Long
For i = 1 To rng.Rows.Count
Dim currCell As Range: Set currCell = rng.Cells(i, 1)
If VarType(currCell) = vbDouble And currCell.NumberFormat <> "#" Then
Debug.Print "OK:", currCell.Value
'... do something
'...
Else
'Debug.Print "Omitted: " & currCell.Address
End If
Next i
End Sub
Here is a tiny example of explicitly testing each value to see if it is "double compatible":
Sub NumCk()
Dim r As Range, rng As Range, v As Variant, d As Double
Set rng = Range("A1:A10")
For Each r In rng
v = r.Value
On Error Resume Next
d = CDbl(v)
If Err.Number = 0 Then
r.Offset(0, 1) = d / 2
Else
Err.Number = 0
End If
On Error GoTo 0
Next r
End Sub
It will reject a text value like "hello world" but accept a value like "1.2" as a text string.
I am trying to search a whole sheet with the below code. Essentially workbook A has a column of values (codes) in (column A) and searches that code in workbook B. Workbook B has codes with their descriptions, those descriptions are to the right of the code, but the distance is variable (which is why it's xlToRight, rather than offset).
It works when I put a breakpoint and run the code manually, however, running the code turns up empty searches. I have tried setting the search value as
FindString = MainSht.Range("A" & x).Value
as well as
FindString = MainSht.Range("A" & x).Text
but both turn up empty searches (or rather they return an "N/a" which is wrong cause I find the values when searching manually).
I need to be able to search the whole worksheet so I am setting the range as .Cells.
Sub One_Find()
Dim Compld As Range
Dim FindString As String
Dim x, NumRows As Integer
Dim MainSht, SearchSht As Worksheet
Dim MainBk, SearchBk As Workbook
Set MainBk = ThisWorkbook
Set SearchBk = ActiveWorkbook
Set MainSht = MainBk.Sheets("Curr des")
Set SearchSht = SearchBk.Sheets("Current")
Set SearchRng = SearchSht.UsedRange '.Cells
NumRows = MainSht.Cells(MainSht.Rows.Count, "A").End(xlUp).Row
For x = 2 To NumRows
FindString = MainSht.Range("A" & x).Text
With SearchRng
Set Compld = .Find(what:=FindString, LookIn:=xlValues, lookat:=xlWhole)
If Not Compld Is Nothing Then
MainSht.Range("B" & x).Value = Compld.End(xlToRight).Value
Else
MainSht.Range("B" & x).Value = "N/a"
End If
End With
Next
End Sub
Let me know if you need more information!
I tried here, here, and here.
I'm trying to highlight a row based on the string contents of a cell in the first column.
For example, if a cell in the first column contains the string "Total", then highlight the row a darker color.
Sub tryrow()
Dim Years
Dim rownum As String
Years = Array("2007", "2008", "2009") ' short example
For i = 0 To UBound(Years)
Set rownum = Range("A:A").Find(Years(i) & " Total", LookIn:=xlValues).Address
Range(rownum, Range(rownum).End(xlToRight)).Interior.ColorIndex = 1
Next i
End Sub
I get this error message:
Compile error: Object required
The editor highlights rownum = , as if this object hadn't been initialized with Dim rownum As String.
You've got a couple issues here, indicated below alongside the fix:
Sub tryrow()
Dim Years() As String 'Best practice is to dim all variables with types. This makes catching errors early much easier
Dim rownum As Range 'Find function returns a range, not a string
Years = Array("2007", "2008", "2009") ' short example
For i = 0 To UBound(Years)
Set rownum = Range("A:A").Find(Years(i) & " Total", LookIn:=xlValues) 'Return the actual range, not just the address of the range (which is a string)
If Not rownum Is Nothing Then 'Make sure an actual value was found
rownum.EntireRow.Interior.ColorIndex = 15 'Instead of trying to build row range, just use the built-in EntireRow function. Also, ColorIndex for gray is 15 (1 is black, which makes it unreadable)
End If
Next i
End Sub
You can avoid loop by using autofilter which will work much faster. The code assumes that table starts from A1 cell:
Sub HighlightRows()
Dim rng As Range, rngData As Range, rngVisible As Range
'//All table
Set rng = Range("A1").CurrentRegion
'//Table without header
With rng
Set rngData = .Offset(1).Resize(.Rows.Count - 1)
End With
rng.AutoFilter Field:=1, Criteria1:="*Total*"
'// Need error handling 'cause if there are no values, error will occur
On Error Resume Next
Set rngVisible = rngData.SpecialCells(xlCellTypeVisible)
If Err = 0 Then
rngVisible.EntireRow.Interior.ColorIndex = 1
End If
On Error GoTo 0
End Sub
I've been following the example from this solution
Excel vba - Compare two ranges and find non matches and tried to combine it with this solution http://dailydoseofexcel.com/archives/2004/05/18/listing-unique-items-with-collections/
However, when I run the code included below, I get a run-time error '1004': Unable to get the Vlookup property of the WoorkSheetClass. How do I get past that error, so that I can remove the rows that does not exist in my cUniqueInput?
Sub RemoveYdelser()
'Get Range From Datark To get range of Ydelses Group
Set YdelsesStart = Worksheets("DATAARK").Range("O8")
Set YdelsesSlut = Worksheets("DATAARK").Range("P8")
Dim LastRow As Long
LastRow = Worksheets("INPUT_MASTERDATA").Range("L" & Rows.Count).End(xlUp).Row
'Variable to get ydelser from Input_masterdata
Dim cUniqueInput As Collection
Dim Rng As Range
Dim Cell As Range
Dim shInput As Worksheet
Dim vNum As Variant
Set shInput = Worksheets("INPUT_MASTERDATA")
Set Rng = shInput.Range("L2:L" & LastRow)
Set cUniqueInput = New Collection
'Get unique values from Input_masterdata, column L
On Error Resume Next
For Each Cell In Rng.Cells
cUniqueInput.Add Cell.Value, CStr(Cell.Value)
Next Cell
On Error GoTo 0
'Add exceptions for master ydelser not listed in column L
cUniqueInput.Add "x", CStr("x")
cUniqueInput.Add "R63011209", CStr("R63011209")
cUniqueInput.Add "R63011206", CStr("R63011206")
'Variable to get ydelser from Summary
Dim cUniqueYdelser As Collection
Dim RngYdelser As Range
Dim CellYdelser As Range
Dim shOpsumering As Worksheet
Dim xNum As Variant
Set shOpsumering = Worksheets("Summary")
Set RngYdelser = shOpsumering.Range("C" & YdelsesStart, "C" & YdelsesSlut)
Set cUniqueYdelser = New Collection
'Get unique values from Summary, column C
On Error Resume Next
For Each CellYdelser In RngYdelser.Cells
cUniqueYdelser.Add CellYdelser.Value, CStr(CellYdelser.Value)
Next CellYdelser
On Error GoTo 0
'Does the values from cUniqueInput exist in cUniqueYdelser
For y = 1 To cUniqueYdelser.Count
'If the value of a row in cUniqueYdelser does not exist in cUniqueInput, then remove entire row
'This is where I get the runtime error
If Application.WorksheetFunction.VLookup(cUniqueYdelser(y), cUniqueInput, 1, False) = "#N/A" Then
With Worksheets("OPSUMMERINGSARK").Cells
.AutoFilter Field:=3, Criteria1:=cUniqueYdelser(y)
.Range("C" & YdelsesStart, "C" & YdelsesSlut).EntireRow.Delete
End With
End If
Next y
Try changing:
If Application.WorksheetFunction.VLookup(cUniqueYdelser(y), cUniqueInput, 1, False) = "#N/A" Then
To
With Application
If .ISNA(.VLookup(cUniqueYdelser(y), cUniqueInput, 1, False) Then
...whatever
End with
Untested in your context of the Collection
To determine if an item exists in a collection, try to retrieve the item by Key, and see if there is an error (#9 - subscript out of range).
You could also set a reference to Microsoft Scripting Runtime and use a Dictionary instead of a collection. You can then use the Exists method to see if a key exists.
I found the solution. The problem was that I was trying to use Vlookup on a collection. Instead I looped through the collections like this
'Does the values from cUniqueInput exist in cUniqueYdelser
For Each ydelse In cUniqueYdelser
Dim itemFoundYdelse As Boolean
itemFoundYdelse = False
For Each inputYdelse In cUniqueInput
If ydelse = inputYdelse Then
itemFoundYdelse = True
Else
'Item not found
End If
Next inputYdelse
If itemFoundYdelse = False Then
With Worksheets("Summary").Cells
.AutoFilter Field:=3, Criteria1:=ydelse
.Range("C" & YdelsesStart, "C" & YdelsesSlut).EntireRow.Delete
End With
End If
Next ydelse