Check if any element in an array matches my condition - arrays

I've got an array with 2 values in it. I want to use an If statement to see whether any of the values in the array equals the SelectedValue of my DropDown.
So far, everything I've tried only gives negative results (only the Else condition is reached):
Dim twoValues() As String = {"one", "two"}
If Me.ddlDropdown.SelectedValue.ToLower.Equals(twoValues.Any) Then
'do something
Else
'do something else
End If

You can invert the comparison and use LINQ's Any() method to compare elements of the array to the provided value, until a matching value is found.
Since the comparison needs to be case-insensitive, you can use the overload of String.Equals() that accepts a StringComparison argument.
Better avoid ToLower() without a CultureInfo selector when a case-insensitive comparison is needed.
Also, the InvariantCulture may apply or not, depending on the languages involved in the comparison.
If the current language is the local language and cannot be directly associated with the InvariantCulture, use StringComparison.CurrentCultureIgnoreCase instead. Or build a custom comparer.
Dim twoValues As String() = {"one", "two"}
Dim value = Me.ddlDropdown.SelectedValue.ToString()
If twoValues.Any(Function(s) s.Equals(value, StringComparison.InvariantCultureIgnoreCase)) Then
'do something
Else
'do something else
End If

Related

VBA, How to pass in a string argument, convert to single character array and resize according to non-numerical values?

Ok,
To all those who may come across this question, this is a problem I have been banging my head against for the past two weeks and have made little or no progress, so any help would be extremely welcome.
Here's the set up; then I will follow with an excerpt of the code I have written:
I am writing a function to get a very specific formula for each file name in a given folder. This naturally requires me to write a program which can take string arguments (in this case, excel file names) from a very broad domain of possibilities and yield a very specific output based on some key -and highly unique- parameters. Hence, my function is bijective and the set of arguments and set of products are massive; therefore, I am in the process of writing a sub-process for this function which partitions the string argument, by character, into a corresponding array, remove all the unnecessary characters, concatenate the remaining characters into the output string, and then go through a series of checks to enforce whatever name-formula the file requires. For now, I am just focused on splitting the string into an array, removing all non-numeric characters and combining the remaining characters back into a single string.
Naturally, I have tried the split function, but to my knowledge VBA doesn't support the splitting of a string into single characters. So I have written the following code, which, admittedly, is a bit inelegant, but I think in principle must work. -It does not. Would someone kindly tell me why it doesn't, and make a recommendation for altering it.
Dim arr() As Variant
For i = Len(strArg) To i = 1
If IsNumeric(Mid$(strArg, i, 1)) = True Then
arr(i - 1) = Mid$(strArg, i, 1)
Else: arr(i - 1) = ""
End If
Next
newStr = Join(arr())
arr() always returns empty, so newStr is always "". Yet there are always numeric values in each string argument. -I can't imagine why I am getting this result. If I use ReDim arr(Len(strArg)), I get Len(strArg) number of " " back....
Thanks in advance to whomever may provide help.
Not sure why you need to split it into an array for this. Your description says you only want to have numeric characters returned in a new string variable. A function like this should work for you:
Function GetNumbers(ByVal arg_sText As String) As String
Dim i As Long
Dim sChar As String
Dim sNumbers As String
For i = 1 To Len(arg_sText)
sChar = Mid(arg_sText, i, 1)
If IsNumeric(sChar) Then sNumbers = sNumbers & sChar
Next i
GetNumbers = sNumbers
End Function
Then just call it in your code like this:
newStr = GetNumbers(strArg) 'Example: "ab1c2d" = "12"
Alternatively use a Regular Expression
Function NumOnly(s As String) As String
With CreateObject("VBScript.RegExp")
.Global = True
.MultiLine = False
.IgnoreCase = True
.Pattern = "[^0-9]+"
NumOnly = .Replace(s, "")
End With
End Function
As a further approach to the existing solutions, I'd like to demonstrate how to use the â–ºFilterXML() function and to check valid results.
The proposed function NumsOnly() consists only of three steps:
a) execute an XPath search upon xml content which has been created by getXML()
b) check valid results via procedure check
c) return the function result as new formed string
Coming close to the requirements in OP this includes also a way to
convert a string to a single character array (c.f. #JNevill 's comment to OP) and
to build a well-formed xml string as base for Filter.XML (see function getXML()) .
Main function NumsOnly()
Function NumsOnly(ByVal s As String) As String
'Purp: recognizes only numeric values in comparisons
'Note: no findings and single digit results are handled by proc check
Const xpath As String = "//*[.>0]" ' get only digits out of atomized characters
'a) execute XPath search upon xml content
Dim x: x = Application.FilterXML(getXML(s), xpath)
'b) check valid results
check x
'c) return only-nums string as function result
NumsOnly = x
End Function
Helper function getXML()
Extended udf based on Split string into array of characters?
Function getXML(ByVal s As String)
'Purp: return well-formed xml content string as base for FilterXML function
'1) atomize string elements into array
Dim buff() As String: buff = Split(StrConv(s, vbUnicode), Chr$(0))
ReDim Preserve buff(UBound(buff) - 1)
'2) return valid xml content string
getXML = "<ch><c>" & Join(buff, "</c><c>") & "</c></ch>"
End Function
Procedure check
As FilterXML returns findings of more than one element as a 2-dim array,
non-findings as Error 2015 and a single element as stand-alone value, it is necessary to distinguish between the returned var types:
Sub check(ByRef x, Optional ErrorResult As String = "")
'Purp: provide for correct xml result by checking var types
Select Case VarType(x)
Case vbError ' non-findings (Error 2015)
x = ErrorResult
Case Is >= vbArray ' 2-dim results (if more than 1 element)
x = Join(Application.Transpose(x), vbNullString)
'Case Else ' single element (here: digit, i.e. Double)
End Select
End Sub

How to convert Array of values in VBA to array of other value types, based on user input?

So I have an class returns array of singles. Problem is that new requrement came up where I need to be able provide these values as a single string, which can be done with Join except Join requres that array is made up of strings not singles.
I guess I could write a new method in my class that provides same array but with values as strings. However I was wondering if I could just convert existing method to accept something like Optional ValType as vbVarType as a parameter and accordingly change the type of values in ouputed array.
Is this doable in a relatively DRY way?? I'd love to avoid code that looks like:
Select Case ValType
Case #something1
#omitted code
Case #something2
#omitted codev
Case #something3
#omitted code
........
UPDATE: I guess what I am looking for is a formula like Cstr() except that I'd like it to work on an Array and have it expect parameeter describing value to convert to.
Can you simply have an array of type Variant rather than Single? The following code seems to work ok:
Dim arrInt() As Variant
ReDim arrInt(0 To 3)
arrInt(0) = 0
arrInt(1) = 1
arrInt(2) = 2
arrInt(3) = 3
Debug.Print Join(arrInt, "#")

Check if property is an array

I want to check if a property from a class is an array (only concerned about numerical arrays here, NOT character arrays [i.e. strings]). I then want to iterate through the array (i.e. 'do something' with each element). See my attempt below. Thanks!!
edit:
So, a little more info... neither IsArray nor my method shown has worked thus far to check for an array. MSDN suggestions "typeof(Array).IsAssignableFrom(type)", but I wasn't sure how to make that work with the property info here. But maybe someone else knows how to use them and I just didn't use properly.
Within the "Class3" I define an array but to not dimension it. I use "redim" when I instantiate it in another thread and load it up prior to passing it to this function. When I insert a breakpoint in the code here, I can look at "myobject" and see the array elements and values, but really I'm looking to cleanly use the propertyinfo type to generalize this method. I also need to be able to index into the array once I've determined that it is an array...again using propertyinfo, not "myobject" directly.
Public Class Class2
Private Shared filelock As New Object
Public Shared Sub write2file(ByVal myobject As Class3)
SyncLock filelock
Dim sb As New StringBuilder
Using sw As StreamWriter = New StreamWriter(File.Open(newfilename, FileMode.Append, FileAccess.Write, FileShare.None))
'Dim pinfo() As PropertyInfo = GetType(Class3).GetProperties
Dim pinfo() As PropertyInfo = CType(myobject.GetType.GetRuntimeProperties, PropertyInfo())
sb.Clear()
For Each p As PropertyInfo In pinfo
If Not p.GetIndexParameters.Length > 0 Then 'if property is not an array
sb.Append(p.GetValue(myobject)).Append(",")
Else ' if property is an array
For x As Integer = 0 To p.GetIndexParameters.Length - 1
sb.Append(p.GetValue(myobject, New Object() {x})).Append(",") 'append each value from array to the stringbuilder in .CSV format
Next
End If
Next
sw.WriteLine(sb) 'write string to file
End Using
End SyncLock
End Sub
End Class
Well, this is not the prettiest thing to do (I somehow don't feel comfortable when comparing the type with a string... if anyone knows better please let me know), but I have tested it and it works:
For Each p As PropertyInfo In pinfo
Dim typeString As String = p.PropertyType.Name.ToString
If typeString = "Int32[]" Then 'if property is not an array
sb.Append(p.GetValue(myobject)).Append(",")
Else ' if property is an array
For x As Integer = 0 To p.GetIndexParameters.Length - 1
sb.Append(p.GetValue(myobject, New Object() {x})).Append(",") 'append each value from array to the stringbuilder in .CSV format
Next
End If
Next
Is it what you were looking for?
only concerned about numerical arrays here, NOT character arrays [i.e. strings]
If by "numerical" you mean that it could be any type (not just Integer), and you are 100% sure that the array it is either numerical or string (I mean, no boolean arrays for instance), then you can modify it to this:
If typeString.EndsWith("[]") And typeString <> "String[]" Then
I hope it helps...
Replace the If block with something like this and it seems to work with a trivial Class3 implementation with an Int32() array property:
If p.PropertyType.IsArray Then
' Untested loop code for array case
Else
' Untested code for scalar (non-array) case
End If
No one seems to have recommended the PropertyType property yet, but that's pretty crucial.

check index value of multi-value parameter

I have a multi-value parameter how I check in iif condition that value of idex is null or not
now, I am using this expression
IIF(IsNothing(Parameters!FR.Value(0))," ","Test")
My parameter is hidden and some thing it has 4 values or some time less than 4.
when I check that values of index 3 value(3) and there is no value in this index. I shows error. How I check the index value of parameter.
The first problem is that there is no object at index 3, not that the value of the object is Nothing, so you will get an error trying to access the Value property of an object that doesn't exist.
The second problem is that IIF is a function not a language construct, so all parameters are evaluated before the function is called regardless of the value of the boolean condition. I can't see a way of trying to access the Value(3) property without the IIF function blowing up in your face when it doesn't exist. You could try something convoluted like this:
=IIF(Parameters!FR.Count >= 4, Parameters!FR.Value(IIF(Parameters!FR.Count >= 4, 3, 0)), "")
but that is still going to blow up in your face when there are no parameter values selected at all.
The easiest and safest way to do it would be to create a custom code function to which you pass the multi-value parameter and the index you want:
Function Get_FR_by_Index(ByVal parameter As Parameter, ByVal index As Integer) AS String
Dim Result As String
Result = ""
If parameter.IsMultiValue Then
If parameter.Count >= index+1 Then
Result = CStr(parameter.Value(index))
End If
End If
Return Result
End Function
then use this function to access the result:
=Code.Get_FR_by_Index(Parameters!FR, 3)
Remember that the index is zero-based, so the first Value is index 0.

How to customize sort order of strings

I am completely new to using string arrays. Consider this example:
I have the following values in a text box:
bufallo#2000
lice#20
cell#1
rat#150
cow#10000
When I sort them, they are sorted alphabetically, as in the list above. However, I need them to sort in descending order using the integer value that follows the # symbol. So, for instance, I want the above list to be sorted like this:
cow#10000
bufallo#2000
rat#150
lice#20
cell#1
I have no idea on how to arrange them in descending order like that.
While doing all your logic in a single LINQ expression can prove how clever you are :) sometimes the code is just easier to read and follow if you do it in a more verbose fashion. So, if you don't want to use LINQ, you could create your own IComparer class which contains your custom sorting algorithm:
Public Class MyComparer
Implements IComparer(Of String)
Public Function Compare(ByVal x As String, ByVal y As String) As Integer Implements IComparer(Of String).Compare
Dim xParts() As String = x.Split("#"c)
Dim yParts() As String = y.Split("#"c)
'Get the integer value (after the # symbol) of each parameter
Dim xValue As Integer = 0
Dim yValue As Integer = 0
If xParts.Length = 2 Then
Integer.TryParse(xParts(1), xValue)
End If
If yParts.Length = 2 Then
Integer.TryParse(yParts(1), yValue)
End If
'Compare y-to-x instead of x-to-y because we want descending order
Return yValue.CompareTo(xValue)
End Function
End Class
In this example, IComparer is a standard .NET framework interface, which you are implementing in your MyComparer class. The Compare method (as defined by IComparer) simply takes two parameters and compares them. If x is less than y (i.e. x comes before y in the sort-order), the method will return a negative number (e.g. -1). If x is greater than y, it will return a positive number (e.g. 1). And if x and y are equal, then the method will return 0.
In this case, however, since all we want to do is use the standard integer sorting, we can just call Integer.CompareTo which compares two integers and returns the negative, positive, or zero, as appropriate.
Then, when you call the Array.Sort method, you can give it one of your custom IComparer objects so that it uses your custom sorting algorithm instead of the default behavior:
Dim arrayToSort() As String = New String() {"bufallo#2000", "lice#20", "cell#1", "rat#150", "cow#10000"}
Array.Sort(arrayToSort, New MyComparer())
The Sort method will use the IComparer object that you gave it to perform the sorting. Each time it needs to compare two items in the array to see which should come first, it will call MyComparer.Compare and use the value returned by that method to determine the proper sorting.
You can re-use the same MyComparer class everywhere in your code that you need to sort items using that same algorithm, which is another benefit over the LINQ approach. Implementing your own IComparer classes allows you to make all sorts of very powerful customize-able sort orders.
You want to order by the numeric part of the string? No need for Regex.
You can use String.Split and Enumerable.OrderByDescending:
Dim number As Int32 = Int32.MinValue
Dim orderedLines = From line In TextBox1.Lines
Let parts = line.Split("#"c)
Let numericPart = parts.Last()
Let success = Int32.TryParse(numericPart, number)
Select LineInfo = New With {line, number}
Order By LineInfo.number Descending
Select LineInfo.line
' if you want to reassign it to the TextBox:
TextBox1.Lines = orderedLines.ToArray()

Resources