I have a worksheet containing a column for the velocity of a vehicle, and I want to take specific ranges from that column (ranges where a turn occurs) and calculate the average speed of all turns combined.
I already have the turn ranges in two arrays, one for the turn starts and one for the turn ends. The code I wrote for this purpose looks like this:
Dim strTurnAvg As String
Dim k As Long
strTurnAvg = ""
For k = LBound(TurnStarts) To UBound(TurnStarts)
If TurnStarts(k) <> TurnEnds(k) Then 'the code for detecting turns rarely messes up and takes the same cell as the start and end of a turn. This is used to ignore such instances
If strTurnAvg = "" Then strTurnAvg = "Range(" & TurnStarts(k) & ":" & TurnEnds(k) & ").Value" Else strTurnAvg = strTurnAvg & ", Range(" & TurnStarts(k) & ":" & TurnEnds(k) & ").Value"
End If
Next
Dim result As Double
result = Application.WorksheetFunction.Average(strTurnAvg) '<- This results in a Run-time error '1004', Unable to get the Average property of the WorksheetFunction class
I tried manually putting the strTurnAvg result into an Average command, and everything worked perfectly fine, but when I try to pass it using the variable, it gives me the runtime error.
Is there a way to achieve this process in another way, or am I doing something wrong?
Try the following:
Dim TurnAvg As Range
Dim k As Long
For k = LBound(TurnStarts) To UBound(TurnStarts)
If TurnStarts(k) <> TurnEnds(k) Then 'the code for detecting turns rarely messes up and takes the same cell as the start and end of a turn. This is used to ignore such instances
If TurnAvg Is Nothing Then
Set TurnAvg = Range(TurnStarts(k), TurnEnds(k))
Else
Set TurnAvg = Union(TurnAvg, Range(TurnStarts(k), TurnEnds(k)))
End If
End If
Next
Dim Result As Double
Result = Application.WorksheetFunction.Average(TurnAvg)
It collects all the ranges using Union into TurnAvg. So you work with the actual ranges and the data in them. You cannot work with strings because you can only calculate with numbers but not with text. The Average functions needs numbers to calculate.
You might want to specify in which workbook/worksheet your ranges are like
ThisWorkbook.Worksheets("Sheet1").Range(TurnStarts(k), TurnEnds(k))
Otherwise VBA might pick the wrong workbook or wrong worksheet.
Related
the ones who read this topic, this is my first question at StackOverflow if any mistakes were made by me please forgive me, I would appreciate it if someone helps.
I have a table which is declared 'genTable' as ListObject. In order to insert and get data from each column of the table. I just set ranges like as:
Dim genTable As ListObject
Set genTable = test.ListObjects("hourly_data")
Set u1_NetLoad = genTable.ListColumns("U1 Net Load").DataBodyRange
I used the following code and helped me setting ranges however it did not satisfied me. I want to learn more dynamic method for my knowledge of programming.
For Each word In genTable.HeaderRowRange
i = 1 + i
test.Cells(22 + i, 2).value = "Set " & Replace(CStr(word), " ", "") & _
" = genTable.ListColumns(" & Chr(34) & word & Chr(34) & ").DataBodyRange"
Next
the output of this code is copied to VBA module to set ranges.
Actually, my scripts work pretty well, but I just want to know If can I set ranges more easily and depending on variables. For my case, I typed every single ranges. I tried the 'for each' loop like this but it did not work.
For Each word In genTable.HeaderRowRange
range_name = Replace(CStr(word), " ", "") & "_"
Set range_name = genTable.ListColumns(word).DataBodyRange
Next
The code above does not work, is there anyway to make it works?
Thanks for reading.
I had the same question last week, I did some digging and came up with this.
Fist I define array then reDim with the the lower and upper limits of the array.
Then using the loop, populated each array entry with the sheet and range needed, noting I needed to use the sheet number, not the name, then I could populate the whole array, then proceed with the code:
...
Dim Nbr_Students As Integer
Dim Counter As Integer
Dim i As Integer
Dim Student_Sheets As Variant
Nbr_Students = Sheets("Master Lists").Range("M3").Value - 1
Counter = 15
i = 0
ReDim RngArray(0 To Nbr_Students)
Do While i <= Nbr_Students
Set RngArray(i) = Worksheets(Counter).Range("C141:C157")
Counter = Counter + 1
i = i + 1
Loop
...
Original Post: Link
I have an excel file with four columns: name, surname, address, area.
There are a lot of rows.
Is there a way to concatenate all the values of every single row in a variable, using vba?
I need a variable that should contain something like this:
(name1, surname1, address1, area1); (name2, surname2, address2, area2); (name3, surname3, address3, area3)...
If you have the following data in your worksheet
Then the following code will read the data into an array …
Option Explicit
Public Sub Example()
Dim RangeData() As Variant ' declare an array
RangeData = Range("A1:D5").Value2 ' read data into array
End Sub
… with the following structure:
Alternatively you can do something like
Public Sub Example()
Dim DataRange As Range
Set DataRange = Range("A2:D5")
Dim RetVal As String
Dim Row As Range
For Each Row In DataRange.Rows
RetVal = RetVal & "(" & Join(Application.Transpose(Application.Transpose(Row.Value2)), ",") & "); "
Next Row
Debug.Print RetVal
End Sub
To get this output:
(name1, surname1, address1, area1); (name2, surname2, address2, area2); (name3, surname3, address3, area3); (name4, surname4, address4, area4);
.. is there a way to write the result like a sort of list that shows all the values of the cells of the range?
Yes, there is. In addition to PEH's valid answers and disposing of Excel version MS365 you might also use
Dim s as String
s = Evaluate("ArrayToText(A2:D5, 1)") ' arg. value 1 representing strict format
resulting in the following output string:
{"name1","surname1","address1","area1";"name2","surname2","address2","area2";"name3","surname3","address3","area3";"name4","surname4","address4","area4"}
Syntax
ARRAYTOTEXT(array, [format])
The ARRAYTOTEXT function returns an array of text values from any specified range. It passes text values unchanged, and converts non-text values to text.
The format argument has two values, 0 (concise default format) and 1 (strict format to be used here to distinguish different rows, too):
Strict format, i.e. value 1 includes escape characters and row delimiters. Generates a string that can be parsed when entered into the formula bar. Encapsulates returned strings in quotes except for Booleans, Numbers and Errors.
Thank you for your answers, suggestions, ideas and hints. I am sorry if my question was not so clear, all the solutions you added were perfect and extremely elegant.
In the end I found a way - a dumber way in comparison to all the things you wrote - and I solved with a for statement.
I did like this:
totRow = ActiveSheet.UsedRange.Rows.Count
For i = 1 To totRow
name = Cells(i, 1)
surname = Cells(i, 2)
address = Cells(i, 3)
area = Cells(i, 4)
Example = Example & "(" & name & ", " & surname & ", " & address & ", " & area & "); "
Next i
Range("E1").Value = Example
It works (it does what I wanted to do), but I noticed a little limit: if the rows are a lot I can't keep the whole text in the variable.
I am a quite new to Excel VBA, and I come from a more... traditional programming background (Java, C). I am having issues with passing a Range Object as a parameter in my User-defined function (see below). The idea of my function is to take several parameters to complete a VLOOKUP of a filtered range.
I may have several syntax issues (I am unsure of my return type and my usage of VLOOKUP), and I would appreciate some guidance on this. See results, more information in my code:
Public Function GETVALUE(screen As String, strEvent As String, dataRange As Range, strDate As String) As String
'ASSUMPTION: dataRange has three columns; first column contains lookup values; Second
' column contains dates for filtering; Third column contains return values
Dim result As String
'remove irrelevant dates in dataRange; apply filter
'ASSUMPTION: This process should return a Range that is removes all Rows that does
'not have strDate in the second column
Dim newRange As Range
'RESULT: Returns #VALUE!. I know this is not the typical := syntax I see in many
'examples but this one apparently compiles, so I use it. I comment this line out
'and try to make the other lines below work with dummy parameters or fixed ranges
newRange = dataRange.AutoFilter(2, strDate)
'Now I try to use the newly filtered, "newRange" and use that in my VLOOKUP
'and return it.
result = [VLOOKUP("*" & screen & "/" & strEvent & "*", newRange, 3, False)]
'I can see an Error 2029 here on Result
GETVALUE = result
'RESULT: Returns #VALUE!
End Function
VLOOKUP ignores any filtering of your data. In other words VLOOKUP will also look in the hidden rows.
I would suggest two alternative approaches:
Copy the visible cells of the filtered range to a new sheet and perform the lookup there:
Set newRange = dataRange.AutoFilter(2, strDate).SpecialCells(xlCellTypeVisible)
set ws = worksheets.Add
ws.Range("A1").Resize(newRange.Rows.Count,newRange.Columns.Count).Value = newRange.Value
etc.
Note that this can not be done in a UDF, you would have to do it in a a Sub.
Store the values in dataRange in a variant array and loop to search for the required value:
Dim arr() as Variant
arr = dataRange.Value
For i = LBound(arr,1) to UBound(arr,1)
If (arr(i,2) = strDate) And (arr(i,1) LIKE "*" & screen & "/" & strEvent & "*"( Then
GETVALUE = arr(i,3)
Exit Function
End If
Next
This I think causes your problem:
result = [VLOOKUP("*" & screen & "/" & strEvent & "*", newRange, 3, False)]
Replace it with this instead:
result = Evaluate("VLOOKUP(*" & screen & "/" & strEvent _
& "*, " & newRange.Address & ", 3, False)")
[] which is shortcut for Evaluate doesn't work on variables.
If it is a direct VLOOKUP like below:
result = [VLOOKUP(D1,Sheet1!$A:$C,3,FALSE)]
it will work. But if you are working with variables as in your example, you have to explicitly state it.
And take note that Evaluate accepts Name argument in a form of string.
So you simply have to concatenate all your strings and then explicitly use Evaluate.
Edit1: Additional Inputs
This will not work as well: newRange = dataRange.AutoFilter(2, strDate).
To pass Objects to a Variable you need to use Set like this.
Set newrange = dataRange.AutoFilter(2, strDate)
On the other hand, AutoFilter method although returning a Range Object fails.
I'm not entirely sure if this can't really be done.
Moving forward, to make your code work, I guess you have to write it this way:
Edit2: Function procedures only returns values, not execute methods
Public Function GETVALUE(screen As String, strEvent As String, rng As Range)
GETVALUE = Evaluate("VLOOKUP(*" & screen & "/" & strEvent & "*, " _
& rng.Address & ", 3, False)")
End Function
To get what you want, use above function in a Sub Procedure.
Sub Test()
Dim dataRange As Range, strDate As String, myresult As String
Set dataRange = Sheet2.Range("A2:E65") 'Assuming Sheet2 as property name.
strDate = "WhateverDateString"
dataRange.AutoFilter 2, strDate
myresult = GETVALUE("String1", "String2", dataRange)
End Sub
Btw, for a faster and less complex way of doing this, try what Portland posted.
Basically you must write :
Getvalue = Application.VLookup( StringVar, RangeVar, ColumnNumberVar)
Vlookup needs your data to be previously ordered in alphabetical order, or it doesn't work.
Excel Developers's approach is a good one too, using a VBA Array.
I can also point the VBA functions FIND, and MATCH, wich will get you the row of searched data, and then you can pull what you need from the 3rd column of that row.
Wichever is faster depends of the size of your range.
Could someone help me create a function that will handle an unlimited number of diverse ranges? I have tried "Paramarray variables() as Variant" and "variable as Range" in my arguments list but neither of them provide the versatility that I am looking for.
The key is that I want my function to be able to simultaneously handle things like "MyFunc(A1:A10, B1)" or "MyFunc(A1, B1:10, C11)". The problem I'm finding is that "ParamArray" can only handle comma separated inputs while "variable as Range" can only handle non-comma separated inputs.
Basically, I want to have the same functionality that the SUM() function has. Where SUM can handle an infinite (sort of) number of inputs regardless if they are separated by commas or are in a range.
As requested, here is my code:
Function COMMA_DELIMITER(inputs as Range)
' this function basically concatenates a consecutive set of cells and places commas between values
For Each j in Inputs
stringy = stringy & j.value & chr(44)
Next
stringy = Left(stringy, Len(stringy) - 1)
COMMA_DELIMITER = stringy
End Function
or
Function COMMA_DELIMITER_A(ParamArray others())
'this is the same function, only the cells don't have to be consecutive
For i = 1 to UBound(others) + 1
stringy = stringy & others(i-1) & chr(44)
Next
COMMA_DELIMIERTER_A = Left(stringy, Len(stringy) - 1)
End Function
I pretty much want to create a function that has the flexibility to handle both consecutive cells and/or non-consecutive cells. The inputs would look like this, "=MyFunc(A1, B1:B10, C11, D12:D44)".
Could someone help me create a function that can handle something like this, "MyFunc(A1, B1:B10, C11, D12:D44)"?
Thanks,
Elias
Actually it is possible to do that, and code from chris neilsen is almost there.
Function MyFunc1(ParamArray r()) As Variant
Dim rng As Range
Dim i As Long
Dim j As Variant
Dim s As String
For i = LBound(r) To UBound(r)
For each j in r(i)
s = s & " " & j.Address
Next
Next
MyFunc1 = s
End Function
See? You only have to put one more loop, so if you have a range like [A1:A4] it will loop for into that too. One loop will return another ParamArray, so you have to loop for twice. And if you have just [A1] that's not a problem either, the double looping will cause no problem.
I think there are two issues with your approach
, and (comma and space) are the Union and Intersect operators for ranges
To pass a multi area range into a single parameter, you need to enclose it in ( )
To demonstrate
ParamArray version
Loop through the array variable to access to individual ranges
Function MyFunc1(ParamArray r()) As Variant
Dim rng As Range
Dim i As Long
Dim s As String
For i = LBound(r) To UBound(r)
s = s & " " & r(i).Address
Next
MyFunc1 = s
End Function
Range version
Iterate the range Areas collection to access individual ranges
Function MyFunc2(r As Range) As Variant
Dim rng As Range
Dim i As Long
Dim s As String
For Each rng In r.Areas
s = s & " " & rng.Address
Next
MyFunc2 = s
End Function
Both
=MyFunc1(B5:D5 C4:C6,B10:E10,D13:D16)
and
=MyFunc2((B5:D5 C4:C6,B10:E10,D13:D16))
will return
$C$5 $B$10:$E$10 $D$13:$D$16
(note that the intersection of B5:D5 and C4:C6 is C5)
I'm trying to write a function that merges multiple rows of text in a column into a single cell based on a pre determined count. My goal is to generate a flexible function to aid in compiling / interperting large quantaties of data. The code I've written returns #NAME? and I cant figure out where the error is. My code is as follows:
Function vmrg(countref As Integer, datref As Integer) As String
If IsEmpty(ActiveCell.Offset(0, -countref)) Then % check if cell containing count is blank
vertmerge = "N/A" % if blank, state N/A
Else
Dim datlst(0 To ActiveCell.Offset(0, -countref).Value - 1) As String
Dim i As Integer
For i = 0 To ActiveCell.Offset(0, -countref).Value - 1
datlst(i) = ActiveCell.Offset(i, -datref).Text %fill array with data
End
vertmerge = datlst(0)
For i = 1 To ActiveCell.Offset(0, -countref).Value - 1 % merge array to a single string
vertmerge = vertmerge & ", " & datlst(i)
End
End
End Function
I have matlab and some C++ experience but this is the first time I've used VBA so my syntax is probably odd in some areas and wrong in others. Ideally I would like to reference the cells where the data and count info are stored, but for now I'm hoping to correct my syntax and set a jumping off point for further development of this function. Any reccomendations are appreciated.
Code Rev_1: I still have an output of #NAME? but I think I've corrected(?) some of the issues
Function vertmerge(countref As Range, datref As Integer) As String
If IsEmpty(countref) = True Then
vertmerge = "NA"
Else
Dim datlst(0 To countref.Value - 1) As String
Dim i As Integer
For i = 0 To countref.Value - 1
datlst(i) = countref.Offset(i, datref).Text
Next i
vertmerge = datlst(0)
For i = 1 To countref.Value - 1
vertmerge = vertmerge & ", " & datlst(i)
Next i
End
End Function
You are doing some dangerous things here!
First - you are referencing "ActiveCell" from inside a function; but you have NO IDEA what cell will be active when the function runs! Instead, pass the target cell as a parameter:
=vmrg("B6", 5, 6)
and change your function prototype to
Function vmrg(r as Range, countref as Integer, datref as Integer)
Now you can reference things relative to r with
r.Offset(1,2)
etc.
Next - you are never assigning anything to vmrg. In VBA, the way a function returns a value is with (in this case)
vmrg = 23
You are assigning things to a variable called vertmerge - but that is not the name of your function. At least add
vmrg = vertmerge
Just before returning. That might do it. Without a full sample of your spreadsheet I can't help you more.