VBA deleting name throws object required - vba

I just want to delete a few names that get created every time a querytable gets created. They are all in 3 sheets starting with 0048,0114,0715, so I would just delete all names that start with any of them. However, I get the rejection "object required" in the if clause when I use rName.Delete. Without this, the code runs fine and prints all the names. Also, if I do range(rName).delete it would delete the ranges in the workbook (not what I want, though).
Sub delNames()
Dim strStartString(0 To 2) As String
strStartString(0) = "'0048'!mta"
strStartString(1) = "'0114'!mta"
strStartString(2) = "'0715!'mta"
For Each rName In ActiveWorkbook.Names
For Each ss In strStartString
If rName.Name Like ss & "*" Then
Debug.Print rName.Name
rName.Delete
End If
Next ss
Next rName
End Sub
Any idea what I am doing wrong here?

Posting Tim's comment as solution
Modifying a collection while looping through it can cause problems. Try using a for next loop counting back through the names in reverse - For x = ActiveWorkbook.Names.Count to 1 Step -1 : ActiveWorkbook.Names(x).Delete – Tim Williams 49 mins ago

Related

Count Rows Until Finding Specific String

I am trying to use Excel VBA to count the number of rows until I hit a specific string and set a variable equal to it.
Something like:
Dim i as Integer
i = Worksheets("Scope Phase Document").Range("A1", Range("A1").End(xlDown)).Find("FACTS - What are we measuring?").Count
I know this isn't the correct syntax and I'm probably missing other stuff, but just using the different functions I currently know, this is what I would hope would do the trick. I get
Run-time error '91' saying "Object variable or With block variable not
set
I have tried a few different ways of doing it, but can't figure out a way that doesn't result in an error.
So I want to start at A1 and count all the rows down until I reach the specific string "FACTS - What are we measuring?".
Any help would be greatly appreciated!
I prefer MATCH, but if the match is not found it throws an error. So we need to test for that:
Dim i As Long
i = 0
On Error Resume Next
i = Application.WorksheetFunction.Match("FACTS - What are we measuring?", ActiveSheet.Range("A:A"), 0)
On Error GoTo 0
If i > 0 Then
' do you stuff with i
End If
So you basically want MATCH():
=MATCH("FACTS - What are we measuring?",A:A,0)
It returns the row number of matched string.
Your code is fine except you should use the Row property. The Count property as you have used it will return 1 because the Find method returns one cell (the first cell where a match is found).
Dim i as Integer
i = Worksheets("Scope Phase Document").Range("A1", Range("A1").End(xlDown)).Find("FACTS - What are we measuring?").Row
Like Scott mentioned, if your text is not found, Excel will throw an error.
I'd do this:
Sub rowcounter()
Dim i As Integer
Range("A1").Select
i = 0
Do
Selection.Offset(1, 0).Select
i = i + 1
Loop Until Selection.Value = "FACTS - What are we measuring?"
MsgBox "rows count is " & i
End Sub

vba - Macro producing incorrect results when run, but when stepping into results are correct

I have a macro that inserts a VLOOKUP into a column. The macro has to take a number stored as text and convert it to a number, before looking up that number in another sheet.
The macro always produces the same results, such as reaching row 43 before starting to produce erroneous results however when using F8 to step through the code, these incorrect results are not produced.
The erroneous results are that the value placed into col 13 is not equal to the number stored as text. Mostly it seems as though values from rows above and below, sometimes 2 rows below are being inserted to col 13. Almost seems to me as if 2 different threads are running at 2 different speeds or something?
If anyone could have a look at the loop causing the errors I would be grateful, thanks.
For counter = 2 To NumRowsList
checker = CInt(Sheets("Sheet2").Cells(counter, 3)
Sheets("Sheet2").Cells(counter, 13).Value = checker
'Call WaitFor(0.5)
If checker < 4000 Then
Sheets("Sheet2").Cells(counter, 14) = "=VLOOKUP(M" & counter & ",Sheet4!E2:F126,2,FALSE)"
Else
Sheets("Sheet2").Cells(counter, 14) = "=VLOOKUP(M" & counter & ",Sheet5!B2:C200,2,FALSE)"
End If
Next counter
I have tried a few similar variations of this code, such as using the value stored in col 13 directly rather than using the cell reference in the VLOOKUP, always producing the same results.
I even used the waitfor function to try and create a delay hoping it may synchronise the operations, but it did not help and using a delay of more than 0.5 would cause the run time of the macro to be too big.
UPDATE:
I did not find a perfect solution, only a long hand work around. I simply combined the Vlookups onto a single sheet, and converted the numbers stored as text to numbers outside of the vba routine. This took the error away from the number calculation (just col C * 1), and then the vlookups were looking up the correct values. Thank you for the help, regardless.
you can avoid looping, checker and all those If-Then-Else, like follows
edited to account for VlookUp range depending on VlookUp value
With Worksheets("Sheet2")
.Range("N2", .Cells(NumRowsList, 14)).FormulaR1C1 = "=VLOOKUP(Value(RC3),IF(Value(RC3)<4000,Sheet4!R2C5:R126C6,Sheet4!R2C2:R200C3),2,FALSE)"
End With
The following works for me with my test data, but you'll need to see if it works for you... (also are you turning off calculation or events? I don't know if this might have an issue?)
I find it preferable to set a reference to the sheet you want to use rather than access it directly, and this may help?
Dim ws As Worksheet: Set ws = ThisWorkbook.Sheets("Sheet2")
Dim VLURange As String, checker As Long
For counter = 2 To 200 ' NumRowsList
checker = CLng(ws.Cells(counter, 3).Value)
ws.Cells(counter, 13) = checker
VLURange = IIf(checker < 4000, "Sheet4!E2:F126", "Sheet5!B2:C200")
ws.Cells(counter, 14) = "=VLOOKUP(M" & counter & ", " & VLURange & ", 2, FALSE)"
Next counter

Foreach over Excel cells only returns the first row in run, but works properly when stepping through the method

I'm using an Excel spreadsheet as a data table for time period history. The structure is this:
ID Person Start End
1 Alan 5/1 5/3
2 Bobbi 5/3 5/4
3 Chuck 5/1 5/2
5 Eugenia 5/3 5/6
6 Chuck 5/10 5/12
Start and End are formatted as Date fields.
I wrote a method in a VBA module to query this table and return all rows for a given person, such as Chuck. In SQL, this is easy enough (select fields from History where Person = something). I am currently using a foreach loop and testing the value of Person. My loop reads as follows:
Public Sub UpdatePeriod(currentPeriod as timePeriod)
Dim indexCell As Range
Dim colPeriods As New Collection
Dim priorPeriod As timePeriod
For Each indexCell in ThisWorkbook.Worksheets("History").Range("A2:A" & Range("A" & Rows.Count).End(xlUp).Row).Cells
If Not (indexCell Is Nothing) And IsNumeric(indexCell) = True Then
Set priorPeriod = GetPeriodObject(indexCell)
If (priorPeriod.Person = currentPeriod.Person) Then
colPeriods.Add priorPeriod
End If
End If
Next
'Do stuff with the entries in colPeriods....
End Sub
I have set up the spreadsheet so that a certain sheet's Worksheet_Change event handler will pass a timePeriod object to this method. So far, everything works properly (though there's probably a better way).
When I test the method without breaking before the For Each, the loop only goes over row 2 (as row 1 contains the headers). But when I do break before the loop, the loop properly goes over all rows.
How do I improve this method to return all rows of interest?
(Note: The project is using VBA as a prototype, and eventually will be a proper application with a proper database. I aim to make the data interface look exactly the same as the application implementation.)
The most likely cause is that you haven't qualified the second Range call (or Rows). Use:
For Each indexCell in ThisWorkbook.Worksheets("History").Range("A2:A" & ThisWorkbook.Worksheets("History").Range("A" & ThisWorkbook.Worksheets("History").Rows.Count).End(xlUp).Row).Cells
Using a variable for the worksheet would simplify things!
With ThisWorkbook.Worksheets("History")
For Each indexCell in .Range("A2:A" & .Range("A" & Rows.Count).End(xlUp).Row).Cells
....
Next
End With

Searching for number of values in one table, resizing number of rows in another table

I'm trying to search for the number of times "Commodity" appears in the table column: Securities[Strategy]. I then want to take that number and resize a table (named: Commodity) on another worksheet accordingly. If it appears 6 times in column Securities[Strategy], the Commodity table should resize to 6 rows, and so on for any number.
I'm very new to VBA. When I run the following code nothing happens.
Sub AdjRow()
Dim Count1 As Integer
Count1 = Application.WorksheetFunction.CountIf(Range("Securities[Strategy]"), "Commodity")
Count1 = Count1 + 12
ActiveSheet.ListObjects("Commodity").Resize Range("$A$12:$J$" & Count1)
End Sub
To help with debugging you can either print key values to the immediate window using Debug.Print or to a messagebox using MsgBox. In this case though, I am curious if you get any error messages when you attempt to run the macro. Editing your code and attempting to run it, it runs fine when the result I get from the CountIf is larger than one, but aborts with an error when it is one or less. If the table you attempt to resize don't have headers, I assume the macro will run fine if CountIf is greater than zero.
Here is the the code, sample data, and output I got when attempting to debug your code. I ran the code 3 times, and had return values of 8, 1, and 2 from the CountIf-function. Note how I didn't get the third address for the listobject on the second run-through, this was because the code aborted when it tried to set the ListObject to only its headers (A2:J2).
Code
Option Explicit
Sub AdjRow()
Dim r As Range
Dim i As Long
Dim lo As ListObject
Set r = Sheet2.Range("Securities[Strategy]")
i = Application.WorksheetFunction.CountIf(r, "Test1")
i = i + 1
Set lo = Sheet1.ListObjects("Commodity")
Debug.Print i
Debug.Print r.Address
Debug.Print lo.Range.Address
lo.Resize Range("$A$2:$J$" & i)
Debug.Print lo.Range.Address
End Sub
Sheet1
Sheet2
Output to immediate window
9
$A$2:$A$10
$A$2:$J$9
$A$2:$J$9
2
$A$2:$A$10
$A$2:$J$9
3
$A$2:$A$11
$A$2:$J$9
$A$2:$J$3

Insert Template with Loop x Amount of Times

I'm back with another question that probably has a simple answer. I really fall down when it comes to loops, so this is what I am trying to build.
I am building a form where user will type in a number between 1 and 156 (named range "GenNo") and click a Generate button.
I need a loop that will copy a template built in the "Template" tab of the spreadsheet with the named range also being "Template", and then insert it into the main form page the specified amount of times. This way the rest of the content and other named ranges should be pushed down accordingly.
Probably a very simple answer but I am terrible when it comes to loops and would not know where to start.
Thanks for your help.
EDIT: This attempt only generates one template in the form:
Sub Generate()
' Check if payslips are already generated
If Sheets("Data").Range("GenLogic").Value = 1 Then
MsgBox ("Already Generated! Please clear the form and regenerate.")
Else
Sheets("Data").Range("GenLogic").Value = 1
End If
' Loop code
Do Until Sheets("Data").Range("LoopLogic").Value = Range("GenNo").Value
Sheets("Template").Range("Template").Copy
Sheets("Overpayment Form").Range("Start").Insert
Range("LoopLogic") = Cell.Value + 1
Loop
End Sub
i would give this a shot; note that i removed your updating of your loop variables. Also, i've rewritten your loop to use a for, and shift down on insert.
Sub Generate()
' Check if payslips are already generated
If Sheets("Data").Range("GenLogic").Value = 1 Then
MsgBox ("Already Generated! Please clear the form and regenerate.")
Else
Sheets("Data").Range("GenLogic").Value = 1
End If
' Loop code
Dim iFrom As Long, iTo As Long, i As Long
iFrom = Sheets("Data").Range("LoopLogic").Value
iTo = Range("GenNo").Value
For i = iFrom To iTo
Sheets("Template").Range("Template").Copy
Sheets("Overpayment Form").Range("Start").Insert Shift:=xlDown
Next
End Sub