WorksheetFunction.Match not working - vba

Here is an excerpt from my data:
------+------+------+------+------+
| A | B | C | D |
------+------+------+------+------+
1 | 10 20 25 30
2 | 152 181 195 210
and my code:
Dim xrng as range, yrng as range, offset as integer
set xrng = Sheets("Sheet1").Range("A1:D1")
set yrng = Sheets("Sheet1").Range("A2:D2")
offset = WorksheetFunction.Match(23, xrng , 1) - 1
Why does running this result in a 1004 error: Unable to get the match property of the worksheetfunction class? How can I fix it?
EDIT: Detailed problem
Okay, I have written a function that does interpolation:
Public Function interpolate(intvalue_X As Double, xrange As range, yrange As range) As Double
....this is just an excerpt:
Dim offst As Integer
offst = WorksheetFunction.Match(intvalue_X, xrange, 1) - 1 'find the offset of the nearest value
---
End Function
With the following data and call, it works fine and returns the correct answer:
(don't mind the variables' who's declarations aren't shown - they have been declared at this point, it's just not copied)
Set intXrng = Sheets("Tables").range("B32:G32")
If beltWidth >= 46 And beltWidth <= 122 And conveyerCenter >= 7.6 And conveyerCenter <= 152.4 Then 'dan kan jy die tabel gebruik
m = interpolate(beltWidth, intXrng, Sheets("Tables").range("B44:G44"))
c = interpolate(beltWidth, intXrng, Sheets("Tables").range("B45:G45"))
powerX = m * conveyerCenter + c
Else
MsgBox "Unable to use the power x-factor table.", vbCritical
End If
Now, when I use the same function, but with this data and call, it gives the error:
Set intXrng = Sheets("Tables").range("F4:I4")
angleSurcharge = 23
capacityTable = interpolate(angleSurcharge, intXrng, Sheets("Tables").range("F7:I7"))

Your values are not stored as strings because they are in a table header. Table headers are always read as strings regardless of their format.
You can convert all values to the Doubles before passing it to Worksheet.Match to fix the bug.
Dim offst As Integer
Dim arry As Variant
ReDim arry(1 To 1, 1 To xrange.Columns.Count)
For i = 1 To xrange.Columns.Count
arry(1, i) = CDbl(xrange.Cells(1, i).Value)
Next
offst = WorksheetFunction.Match(intvalue_X, arry, 1) - 1 'find the offset of the nearest value

Related

Label a set of objects with (A->Z,AA->ZZ, AAA->ZZZ) in VBA

I have a set which has an unknown number of objects. I want to associate a label to each one of these objects. Instead of labeling each object with a number I want to label them with letters.
For example the first object would be labeled A the second B and so on.
When I get to Z, the next object would be labeled AA
AZ? then BA, BB, BC.
ZZ? then AAA, AAB, AAC and so on.
I'm working using Mapbasic (similar to VBA), but I can't seem to wrap my head around a dynamic solution. My solution assumes that there will be a max number of objects that the set may or may not exceed.
label = pos1 & pos2
Once pos2 reaches ASCII "Z" then pos1 will be "A" and pos2 will be "A". However, if there is another object after "ZZ" this will fail.
How do I overcome this static solution?
Basically what I needed was a Base 26 Counter. The function takes a parameter like "A" or "AAA" and determines the next letter in the sequence.
Function IncrementAlpha(ByVal alpha As String) As String
Dim N As Integer
Dim num As Integer
Dim str As String
Do While Len(alpha)
num = num * 26 + (Asc(alpha) - Asc("A") + 1)
alpha = Mid$(alpha, 2,1)
Loop
N = num + 1
Do While N > 0
str = Chr$(Asc("A") + (N - 1) Mod 26) & str
N = (N - 1) \ 26
Loop
IncrementAlpha = str
End Function
If we need to convert numbers to a "letter format" where:
1 = A
26 = Z
27 = AA
702 = ZZ
703 = AAA etc
...and it needs to be in Excel VBA, then we're in luck. Excel's columns are "numbered" the same way!
Function numToLetters(num As Integer) As String
numToLetters = Split(Cells(1, num).Address(, 0), "$")(0)
End Function
Pass this function a number between 1 and 16384 and it will return a string between A and XFD.
Edit:
I guess I misread; you're not using Excel. If you're using VBA you should still be able to do this will the help of an reference to an Excel Object Library.
This should get you going in terms of the logic. Haven't tested it completely, but you should be able to work from here.
Public Function GenerateLabel(ByVal Number As Long) As String
Const TOKENS As String = "ZABCDEFGHIJKLMNOPQRSTUVWXY"
Dim i As Long
Dim j As Long
Dim Prev As String
j = 1
Prev = ""
Do While Number > 0
i = (Number Mod 26) + 1
GenerateLabel = Prev & Mid(TOKENS, i, 1)
Number = Number - 26
If j > 0 Then Prev = Mid(TOKENS, j + 1, 1)
j = j + Abs(Number Mod 26 = 0)
Loop
End Function

Excel VBA: "Next Without For" Error

I am getting the "next without for" error. I checked other questions on this and looked for any open if statements or loops in my code, but could find none. I'm need an extra set of eyes to catch my error here.
I am trying to loop through this code and advance the torque value 3 times each times it gets to the 30th i.
'This is Holzer's method for finding the torsional natural frequency
Option Explicit
Sub TorsionalVibrationAnalysis_()
Dim n As Integer 'position along stucture
Dim m As Integer
Dim i As Long 'frequency to be used
Dim j As Variant 'moment of inertia
Dim k As Variant 'stiffness
Dim theta As Long 'angular displacement
Dim torque As ListRow 'torque
Dim lambda As Long 'ListRow 'omega^2
Dim w As Variant
Dim s As Long
'equations relating the displacement and torque
n = 1
Set j = Range("d2:f2").Value 'Range("d2:f2").Value
Set k = Range("d3:f3").Value
'initial value
Set w = Range("B1:B30").Value
For i = 1 To 30
'start at 40 and increment frequency by 20
w = 40 + (i - 1) * 20
lambda = w ^ 2
theta = 1
s = 1
Do While i = 30 & s <= 3
torque = lambda * j(1, s)
s = s + 1
End
m = n + 1
theta = theta - torque(i, n) / k(n)
torque(i, m) = torque(i, n) + lambda * j(m) * theta
If m = 4 & i < 30 Then
w(i) = 40 + (i - 1) * 20
lambda = w(i) ^ 2
ElseIf m = 4 & i >= 30 Then
Cells([d], [5+i]).display (i)
Cells([e], [5+i]).display (theta)
Cells([f], [5+i]).display (torque)
Else
End If
If m <> 4 Then
n = n + 1
End If
Next i
End Sub
You are trying to terminate your While with an End instead of Loop
Try changing your End to Loop in your Do While loop. I think you are terming the loop when you hit that End
Proper indentation makes the problem rather apparent.
You have:
For i = 1 To 30
'...
Do While i = 30 & s <= 3
'...
End
'...
If m = 4 & i < 30 Then
'...
ElseIf m = 4 & i >= 30 Then
'...
Else
End If
If m <> 4 Then
'...
End If
Next i
But run it through Rubberduck's Smart Indenter and you get:
For i = 1 To 30
'...
Do While i = 30 & s <= 3
'...
End
'...
If m = 4 & i < 30 Then
'...
ElseIf m = 4 & i >= 30 Then
'...
Else
End If
If m <> 4 Then
'...
End If
Next i
End Sub
Notice how the End other answers are pointing out, is clearly not delimiting the Do While loop.
The Next i is inside the Do While block, which isn't terminated - when the VBA compiler encounters that Next i, it doesn't know how it could possibly relate to any previously encountered For statement, and thus issues a "Next without For" compile error.
Use an indenter.

sorting data by date with excel

I have raw data I'm trying to sort out by date, the data is in this form:
month:april-2014
offer | value
ofr x | 2132
ofr y | 135
.
.
.
month:mai-2014
offer | value
ofr x | 5115
ofr z | 513
ofr y | 651
and it goes on, there are offers that apear every month and others that dissapear.
I wanted it to look like this :
offer | april-2014 |mai 14 | june ....
ofr x 123 5 6
ofr y 5 1 6
ofr z
ofr a
.
.
any help would be appreciated, thank you
Try to restructure the data like this and use pivot tables?
Date | offer | value
may-2014 |ofr x | 5115
may-2014 |ofr z | 513
may-2014 |ofr y | 651
This first chunk of code is going through and rearranging things for you. The other important thing it does is only sends one column from your selected range to the function. Some important things to remember are you may need to write the search criteria if you key word for "month" is not in the same spot in the text, the word offer is not by itself with no spaces in the following row. Another point of note, is this is treating everything as is. That means if the source cell was text, then the destination cell will be text. To convert from date as text to date as Excel serial that is a separate issue and there are plenty of ways to achieve that as well.
Option Explicit
Sub SortOffer(OfferList As Range)
Dim CounterX As Long, CounterY As Long, jCounter As Long, icounter As Long, MonthCount As Long, UniqueOffers As Long
Dim inlist As Boolean
Dim unsorted() As Variant
Dim sorted() As Variant
MonthCount = WorksheetFunction.CountIf(OfferList, "month*")
UniqueOffers = CountUnique(OfferList.Columns(1).Cells) - MonthCount - 1
ReDim sorted(1 To UniqueOffers + 1, 1 To MonthCount + 1) As Variant
unsorted = OfferList
CounterX = 1
jCounter = 1
sorted(1, 1) = "offer"
For CounterY = LBound(unsorted, 1) To UBound(unsorted, 1)
If Left(unsorted(CounterY, 1), 5) = "month" Then
CounterX = CounterX + 1
sorted(1, CounterX) = Right(unsorted(CounterY, 1), Len(unsorted(CounterY, 1)) - 6)
Else
inlist = False
For icounter = 2 To jCounter
If unsorted(CounterY, 1) = sorted(icounter, 1) Then
sorted(icounter, CounterX) = unsorted(CounterY, 2)
inlist = True
End If
Next icounter
If Not inlist And unsorted(CounterY, 1) <> "offer" And unsorted(CounterY, 1) <> "" Then
jCounter = jCounter + 1
sorted(jCounter, 1) = unsorted(CounterY, 1)
sorted(jCounter, CounterX) = unsorted(CounterY, 2)
End If
End If
Next CounterY
Range("F1").Resize(UBound(sorted, 1), UBound(sorted, 2)).Value = sorted
End Sub
This next function counts the number of unique entries in a range and does not count spaces. I stumbled across this code on this web page. If you subtract the number of months from this count, you will know how many offers are in your table. This is important because it will tell you how to size your array(alt link) that you will later write back as your results
Function CountUnique(ByVal MyRange As Range) As Integer
Dim Cell As Range
Dim J As Integer
Dim iNumCells As Integer
Dim iUVals As Integer
Dim sUCells() As String
iNumCells = MyRange.Count
ReDim sUCells(iNumCells) As String
iUVals = 0
For Each Cell In MyRange
If Cell.Text > "" Then
For J = 1 To iUVals
If sUCells(J) = Cell.Text Then
Exit For
End If
Next J
If J > iUVals Then
iUVals = iUVals + 1
sUCells(iUVals) = Cell.Text
End If
End If
Next Cell
CountUnique = iUVals
End Function
Now just in case the links don't cover it, this answer which was a learning lesson for me was taught in various parts to me by #JNevill, #Ralph, #findwindow, #Gary'sStudent and #ScottCraner. Appologies if I missed someone. I am also sure any of these individuals could do it slicker and take less then 10 hours to write it 8).

VBA Error: "The object invoked has disconnected from its clients"

I am attempting to write a macro that matches up x/y coordinates to ellipses that they fit into. I get the automation error at the "Else" line in my code. I have reviewed a lot of other posts but I can't figure out what is wrong with my code. Any assistance is much appreciated. Thank you!
Private Sub CommandButton1_Click()
Dim XR As Integer
Dim XC As Integer
Dim YR As Integer
Dim YC As Integer
Dim areaR As Integer
Dim areaC As Integer
Dim hR As Integer
Dim hC As Integer
Dim kR As Integer
Dim kC As Integer
Dim aR As Integer
Dim aC As Integer
Dim bR As Integer
Dim bC As Integer
Dim angleR As Integer
Dim angleC As Integer
Dim matchR As Integer
Dim matchC As Integer
XR = 2
XC = 1
YR = 2
YC = 2
Do Until ThisWorkbook.Sheets("Sheet1").Cells(XR, XC).Value = ""
ThisWorkbook.Sheets("Sheet1").Activate
areaR = 2
areaC = 6
hR = 2
hC = 7
kR = 2
kC = 8
aR = 2
aC = 9
bR = 2
bC = 10
angleR = 2
angleC = 11
matchR = XR
matchC = 12
Do Until ThisWorkbook.Sheets("Sheet1").Cells(hR, hC).Value = ""
If (((((ThisWorkbook.Sheets("Sheet1").Cells(XR, XC).Value) _
- (ThisWorkbook.Sheets("Sheet1").Cells(hR, hC).Value)) * _
Cos((ThisWorkbook.Sheets("Sheet1").Cells(angleR, angleC).Value)) _
+ ((ThisWorkbook.Sheets("Sheet1").Cells(YR, YC).Value) - _
(ThisWorkbook.Sheets("Sheet1").Cells(kR, kC).Value)) * Sin _
((ThisWorkbook.Sheets("Sheet1").Cells(angleR, angleC).Value))) ^ 2) _
/ ((Cells(aR, aC).Value) ^ 2)) + (((((Cells(XR, XC).Value) - _
(Cells(hR, hC).Value)) * Sin((ThisWorkbook.Sheets("Sheet1").Cells _
(angleR, angleC).Value)) - ((ThisWorkbook.Sheets("Sheet1").Cells(YR, YC).Value) _
- (ThisWorkbook.Sheets("Sheet1").Cells(kR, kC).Value)) _
* Cos((Cells(angleR, angleC).Value))) ^ 2) / ((Cells(bR, bC).Value) ^ 2)) _
<= 1 Then
ThisWorkbook.Sheets("Sheet1").Cells(matchR, matchC).Value = _
ThisWorkbook.Sheets("Sheet1").Cells(areaR, areaC)
Else
areaR = areaR + 1
hR = hR + 1
kR = kR + 1
aR = aR + 1
bR = bR + 1
angleR = angleR + 1
End If
Loop
XR = XR + 1
YR = YR + 1
Loop
End Sub
That's a lot of code, and doing direct math with cell reference values is very hard to read...probably even for you.
It may not be much of an answer, but if you create variables (yes, more variables), inside your loops, then your code will be easier to read for everyone and very possibly your error will emerge.
So instead of using ThisWorkbook.Sheets("Sheet1").Cells(XR, XC).Value in your calculation, first do this: X = ThisWorkbook.Sheets("Sheet1").Cells(XR, XC).Value
But don't stop there. Include intermediate variable that will self-describe your code and the calculation process.
This will hopefully allow you to see the code for what it really does.
isMatch = cosX <= 1
If isMatch Then
And, by the way, my suspission is that your error is coming from the fact that you are not qualifying all of your Cells methods with a sheets reference.

What could be slowing down my Excel VBA Macro?

This function goes through all integers and picks out binary values with only five ones and writes them to the spreadsheet.
To run this For x = 1 To 134217728 would take 2.5 days!!!! Help!
How could I speed this up?
Function D2B(ByVal n As Long) As String
n = Abs(n)
D2B = ""
Do While n > 0
If n = (n \ 2) * 2 Then
D2B = "0" & D2B
Else
D2B = "1" & D2B
n = n - 1
End If
n = n / 2
Loop
End Function
Sub mixtures()
Dim x As Long
Dim y As Integer
Dim fill As String
Dim mask As String
Dim RowOffset As Integer
Dim t As Date
t = Now
fill = ""
For x = 1 To 134217728
mask = Right(fill & CStr(D2B(x)), Len(fill & CStr(D2B(x))))
Debug.Print mask
If x > 100000 Then Exit For
If Len(mask) - Len(WorksheetFunction.Substitute(mask, "1", "")) = 5 Then _
RowOffset = RowOffset + 1
For y = 1 To Len(mask)
If Len(mask) - Len(WorksheetFunction.Substitute(mask, "1", "")) = 5 Then _
Range("mix").Offset(RowOffset).Cells(y) = Mid(mask, y, 1)
Next
Next
Debug.Print DateDiff("s", Now, t)
End Sub
By first sight guess, I think the problem lies in the fact that you do that cell by cell, which causes many read and write accesses.
You should do it range by range, like
vArr = Range("A1:C1000").Value
' it is array now, do something here effeciently
Range("A1:C1000").Value = vArr
You want find all 28bit numbers with 5 1s
There are 28*27*26*25*24/5/4/3/2=98280 such numbers
The following code took ~10 seconds on my PC:
lineno = 1
For b1 = 0 To 27
For b2 = b1 + 1 To 27
For b3 = b2 + 1 To 27
For b4 = b3 + 1 To 27
For b5 = b4 + 1 To 27
Cells(lineno, 1) = 2 ^ b1 + 2 ^ b2 + 2 ^ b3 + 2 ^ b4 + 2 ^ b5
lineno = lineno + 1
Next
Next
Next
Next
Next
mask = Right(fill & CStr(D2B(x)), Len(fill & CStr(D2B(x))))
The above line of code does the same thing (CStr(D2B(x))) twice.
Store the result of CStr(D2B(x)) in a variable & use that variable in the above line of code.
I've got 2 suggestions:
Get rid of the substitution command by counting the ones/zeroes in D2B and return an empty string if the count does not equal 5
Write these pre-filtered bitstrings to an array first and copy the array directly to the cells when finished.
Something like
ws.Range(ws.cells(1, 1), ws.cells(UBound(dstArr, 1) + 1, UBound(dstArr, 2) + 1)) = dstArr
The array-copy-trick greatly improves performance!