Finding closest coordinates with the help of VBA - vba

My goal is to find the closest point to a certain point. I have a list of coordinates to different points and I want to find the closest to a certain point (Point1) that is not in that list.
With the MIN function I can find the closest distance between Point1 and the closest point from the list.
The problem is that I can't find the coordinates(or at least one coordinate) of that point. For this I think I need to use VBA.
I would use VBA to find one coordinate of the point I'm looking for (the point with the closest distance) in the column of one coordinate (for example x). I've written an easy code that should give me what I want if I add my if condition but it doesn't work. The if condition I will be using in the code would be:
If columnx = (SQRT(Abs((distance) ^ 2 - ((columny - pt1y) ^ 2))) + pt1x) Then
pt2x = columnx
pt2x would be the function name, pt1y-y coordinate of Point1, pt1x - x coordinate of Point1, the rest you should understand.
The code I have written is:
Function K23S(l As Range, s As Range) As Variant
Dim K() As Variant
K = Range("l").Value
Dim M() As Variant
M = Range("s").Value
Dim i As Long
Dim j As Long
For i = LBound(K) To UBound(K)
For j = LBound(M) To UBound(M)
If K(i) = M(j) Then
p = K(i)
End If
Next j
Next i
p = K23S
End Function
It compares two columns (for example A1:A32 and B1:B32) and should give one's value if the values in the same row match (if they start on the same row). At the moment it gives #VALUE! error. I have tried the For Each as well but the result is the same. Values in the cells are in the right format.
What am I doing wrong? Or maybe you can offer a different solution?
I've used this link, maybe it helps to undestand: http://excelmacromastery.com/Blog/index.php/the-complete-guide-to-using-arrays-in-excel-vba/
*The Excel formulas found on the internet are not giving the right answers in some cases so I would like to try a different approach.

p = K23S should be K23S = p.
Add Option Explicit to the top of your module, too, as it should identify compile errors like this.
Also, you are incorrectly doing your range assignments.
Function K23S(l As Range, s As Range) As Variant
Dim K() As Variant
Dim M() As Variant
K = l.Value
M = s.Value
Dim i As Long
Dim j As Long
For i = LBound(K) To UBound(K)
For j = LBound(M) To UBound(M)
If K(i) = M(j) Then
K23S = K(i)
Exit Function
End If
Next j
Next i
K23S = "Default Value"
End Function
I also added a early exit on your function when it finds the match and a default return value (in case it doesn't find anything).

I just solved my problem. The column nr was supposed to be aaded to the arrays. The correct code would be:
Function K23S(l As Range, s As Range) As Variant
Dim K() As Variant
Dim M() As Variant
K = l.Value
M = s.Value
Dim i As Long
Dim j As Long
For i = LBound(K) To UBound(K)
For j = LBound(M) To UBound(M)
If K(i, 1) = M(j, 1) Then
K23S = K(i, 1)
Exit Function
End If
Next j
Next i
K23S = "Default Value"
End Function

Related

Returning an Array/Matrix in Excel VBA from a user defined function

I'm new to Excel VBA and its been 5 years since I've done any VBA at all. I've written a UDF to do a basic regression, but I can't get it to output an array of regressed values. I select the range I want to output so and hit crtl+shift+enter, but it doesn't work. I've tried a few different things, but nothing does the trick. Here is my latest attempt:
Function REGRESSMEDIAN(x As Range, y As Range) As Double
Dim slope As Double, intercept As Double, count As Integer
count = x.count
Dim lny() As Double, regression() As Double
ReDim lny(1 To count), regression(1 To count)
Dim i As Integer
For i = 1 To count
lny(i) = Application.WorksheetFunction.Ln(y(i))
Next i
slope = Application.WorksheetFunction.slope(lny, x)
intercept = Application.WorksheetFunction.intercept(lny, x)
Dim j As Integer
For j = 1 To count
regression(j) = Exp(slope * x(j) + intercept)
Next j
REGRESSMEDIAN = regression
End Function
This test function:
Function tester()
tester = Array("a", "b", "c")
End Function
will work fine as a UDF as long as your 3 output cells are in a row, not in a column. If they're in a column then you'll just see "a" in all 3 cells.
If you're trying to put the output in a column then this will work:
Function tester()
tester = Application.Transpose(Array("a", "b", "c"))
End Function
Try
Function REGRESSMEDIAN(x As Range, y As Range) As Double()
only putting the parentheses in the function declaration.
It will work.

Simple moving average range in Excel-VBA

This code is just to calculate simple moving average. Opened an excel, created dummy array in C row from 1 to 20. I want to create a function for eg: SMA(C7,3) = which should give average of C5:C7.
Coming back to VBA after long time, not able to figure whats the error in the below code.
Function sma1(rng As Range, N As Integer)
Set rng = rng.Resize(-N + 1, 0)
sma1 = Application.WorksheetFunction.average(rng)
End Function
avoid using a cell name as a function
fixed the RESIZE()
used an internal range variable
Function smal(rng As Range, N As Integer) As Variant
Dim rng2 As Range
Set rng2 = rng.Resize(N, 1)
smal = Application.WorksheetFunction.Average(rng2)
End Function
EDIT#1:
Based on Scott's comment:
Function smal(rng As Range, N As Integer) As Variant
Dim rng2 As Range
Set rng2 = rng.Offset(1 - N, 0).Resize(N, 1)
smal = Application.WorksheetFunction.Average(rng2)
End Function
I assume you want the column along side it to give you're SMA (as shown below?):
If so, the below will do it and drag it autocomplete it to the bottom of you column C array:
Sub SMA3()
Range("D7").FormulaR1C1 = "=AVERAGE(R[-2]C[-1]:RC[-1])" 'This is a relative reference (left one cell and up two cells) - This give your three inputs
Range("D7").AutoFill Destination:=Range("D7:D" & Range("C1048576").End(xlUp).Row) 'Autofills the SMA
End Sub
Just an FYI this can be done with existing formula:
=IF(ROW(C1)<$E$1,"",AVERAGE(INDEX(C:C,ROW(C1)-$E$1+1):C1))
E1 contains the number of rows to include.

dB calculator? VBA function writing

So I've got quite the specific task. I want to create a function that will add decibel units together. Currently, you have to input something like
=10*LOG10(10^(A1/10)+10^(A2/10))
and make this even longer if you have like 15 parts you want to add together. Kind of a pain.
Ideally, it would work like the excel function SUM and just take in any input. Can someone help me put this together, or at least show me what SUM would look like if you had to create it from scratch?
Best,
T. Heng
This little UDF() will give you more flexibility:
Public Function decibelle(rng As Range, N As Long) As Double
Dim wf As WorksheetFunction, i As Long, Z As Double
Set wf = Application.WorksheetFunction
For i = 1 To N
Z = Z + 10 ^ (rng(i) / 10)
Next i
decibelle = 10 * wf.Log10(Z)
End Function
where the first argument is the range of inputs and the second argument is the number of inputs:
EDIT#1:
and if you want the UDF() to be more like SUM() consider:
Public Function decibelle2(rng As Range) As Double
Dim wf As WorksheetFunction, r As Range, Z As Double
Set wf = Application.WorksheetFunction
For Each r In rng
Z = Z + 10 ^ (r.Value / 10)
Next r
decibelle2 = 10 * wf.Log10(Z)
End Function
so you could use it like:
=decibelle2(A1:A2)
The below is a very simple example of a User Defined Function (which should be typed into a standard module) that can take any number of parameters and return the sum of these parameters. Your example seems to involve more advanced logic, so you will have to expand upon my example. Let us know if you need more assistance.
Function AddSomeDigits(ParamArray nums()) As Double
Dim vRunningTotal As Variant
vRunningTotal = 0
For i = LBound(nums) To UBound(nums)
vRunningTotal = vRunningTotal + nums(i)
Next i
AddSomeDigits = vRunningTotal
End Function

Custom sort routine for unique string A being place after another string B, C, D, etc if string A is found within them

Situation
I have a UDF that works with a range that it is passed that is of variable height and 2 columns wide. The first row will contain text in column 1 and an empty column2. The remainder of column 1 will contain unsorted text with an associated value in the same row in column 2. I need to sort the data such that if some text in column 1 also appears in some other text in column.
Problem
My VBA skills are all self taught and mimimal at best. I remember a few decades ago in university we did bubble sorts and played with pointers, but I no longer remember how we achieved any of that. I do well reading code but creating is another story.
Objective
I need to generate a sort procedure that will produce unique text towards the bottom of the list. I'll try wording this another way. If text in column1 can be found within other text in column, that the original text need to be placed below the other text it can be found in along with its associated data in column 2. The text is case sensitive. Its not an ascending or descending sort.
I am not sure if its a restriction of the UDF or not, but the list does not need to be written back to excel, it just needs to be available for use in my UDF.
What I have
Public Function myFunk(rng As Range) As Variant
Dim x As Integer
Dim Datarange As Variant
Dim Equation As String
Dim VariablesLength As Integer
Dim Variable As String
Datarange = rng.Value
'insert something around here to get the list "rng or Datarange" sorted
'maybe up or down a line of code depending on how its being done.
Equation = Datarange(1, 1)
For x = 2 To UBound(Datarange, 1)
VariablesLength = Len(Datarange(x, 1)) - 1
Variable = Left$(Datarange(x, 1), VariablesLength)
Equation = Replace$(Equation, Variable, Datarange(x, 2))
Next x
myFunk = rng.Worksheet.Evaluate(Equation)
End Function
Example Data
Any help with this would be much appreciated. In that last example I should point out that the "=" is not part of the sort. I have a routine that strips that off the end of the string.
So in order to achieve what I was looking for I added a SWAP procedure and changed my code to look like this.
Public Function MyFunk(rng As Range) As Variant
Dim x As Integer
Dim y As Integer
Dim z As Integer
Dim datarange As Variant
Dim Equation As String
Dim VariablesLength As Integer
Dim Variable As String
'convert the selected range into an array
datarange = rng.Value
'verify selected range is of right shape/size
If UBound(datarange, 1) < 3 Or UBound(datarange, 2) <> 2 Then
MyFunk = CVErr(xlErrNA)
Exit Function
End If
'strip the equal sign off the end if its there
For x = 2 To UBound(datarange, 1)
If Right$(datarange(x, 1), 1) = "=" Then
datarange(x, 1) = Left$(datarange(x, 1), Len(datarange(x, 1)) - 1)
End If
Next x
'sort the array so that a variable does not get substituted into another variable
'do a top down swap and repeat? Could have sorted by length apparently.
For x = 2 To UBound(datarange, 1) - 1
For y = x + 1 To UBound(datarange, 1)
If InStr(1, datarange(y, 1), datarange(x, 1)) <> 0 Then
For z = LBound(datarange, 2) To UBound(datarange, 2)
Call swap(datarange(y, z), datarange(x, z))
Next z
y = UBound(datarange, 1)
x = x - 1
End If
Next y
Next x
'Set the Equation
Equation = datarange(1, 1)
'Replace the variables in the equation with values
For x = 2 To UBound(datarange, 1)
Equation = Replace$(Equation, datarange(x, 1), datarange(x, 2))
Next x
'rest of function here
End Function
Public Sub swap(A As Variant, B As Variant)
Dim Temp As Variant
Temp = A
A = B
B = Temp
End Sub
I sorted by checking to see if text would substitute into other text in the list. Byron Wall made a good point that I could have sorted based on text length. Since I had completed this before I saw the suggestion it did not get implemented though I think it may have been a simpler approach.

Making Selected Range into an Array and doing stuff with it

I'm trying to teach myself VBA writing some little things. I'm trying to make something that allows you to select some data and then calculates the mean and variance. My code is as follows :
Sub VarianceCalculator()
Dim k As Integer
Dim SelectedData As Range
Dim SelectedDataArray() As Variant
Dim Var As Double
Dim Mu As Double
On Error Resume Next
Set SelectedData = Application.InputBox("Select a range of data to be
calculated", Default:=Selection.Address, Type:=8)
On Error GoTo 0
SelectedDataArray = Range(SelectedData.Address)
k = UBound(SelectedDataArray)
Call VarianceCalculatorWithArray(SelectedDataArray, k)
MsgBox ("The selected data has variance " & Var & " and has mean " & Mu)
End Sub
Sub VarianceCalculatorWithArray(Data() As Variant, k As Integer)
Dim Var As Double
Dim Mu As Double
Dim j As Integer
Dim i As Integer
ReDim Data(k) As Variant
Mu = 0
Var = 0
For j = 0 To k
Mu = Mu + (Data(j)) / (k + 1)
Next j
For i = 0 To k
Var = Var + ((Data(i) - Mu) ^ (2)) / (k + 1)
Next i
End Sub
I think the error is that somehow the data is not getting transferred into the array but I can't find a solution to this.
Thanks!!
There are two major problems:
(1) If you want to pass the variables Var and Mu from one procedure to another you'll have to declare them as public variables before the first procedure. The alternative is to setup VarianceCalculatorWithArray as a function.
(2) The array Data() is 2D as a range consists of rows and columns. So, if you want to use an element from this array, you'll have to address it as Data(1, 1). Also note, that this range array starts with row 1 and column 1. Therefore your for...next statements should start with 1 and not with 0.
Note, that you can always set a breakpoint in order to check if data is transferred into the array.