How to check if one string contains another case insensitively? - vb.net

comp = StringComparison.OrdinalIgnoreCase
Console.WriteLine(" {0:G}: {1}", comp, s.Contains(sub1, comp))
That seems like the way. However, I tried and it doesn't work. It seems that the only altenative of s.Contains take char() as first argument. If I want to insert string as first argument then I cannot have second argument as StringComparison.OrdinalIgnoreCase.
I got the snippet from https://msdn.microsoft.com/en-us/library/dy85x1sa(v=vs.110).aspx

You can use IndexOf function.
Console.WriteLine(s.IndexOf(sub1, 0, StringComparison. OrdinalIgnoreCase) > -1)
Above snippet prints true or false depending on whether s contains sub1.
Alternatively, if you want to use contains, you can use ToUpper or ToLower as shown below...
Console.WriteLine(" {0:G}: {1}", comp, s.ToUpper().Contains(sub1.ToUpper()))
The link you gave in question shows you how to create a custom extension to string. He also gave the code you have to use to create that custom extension and that method uses IndexOf internally if you had observed.
Source

Try this:
s.ToLowerInvariant().Contains(sub1.ToLowerInvariant())
The link you pointed to in your question defines the follow extension method:
Imports System.Runtime.CompilerServices
Module StringExtensions
<Extension()>
Public Function Contains(str As String, substring As String,
comp As StringComparison) As Boolean
If substring Is Nothing Then
Throw New ArgumentNullException("substring",
"substring cannot be null.")
Else If Not [Enum].IsDefined(GetType(StringComparison), comp)
Throw New ArgumentException("comp is not a member of StringComparison",
"comp")
End If
Return str.IndexOf(substring, comp) >= 0
End Function
End Module
Unless you implement this your call to s.Contains(sub1, comp) won't work.

Related

How to create an extension method for any type of Enum array in VB.Net

Below is the code I have but the method is not available on Enum arrays. I can't work out what I'm doing wrong. Note that I'm unable to test the line Array.ConvertAll until I have this method available on Enum arrays.
Public Module EnumExtensions
<Extension()>
Function ValuesToString(Source As [Enum]()) As String()
Dim EnumType = Source.GetType()
If Not EnumType.IsEnum Then Return Nothing
Return Array.ConvertAll(Source, Function(x) x.ToString)
End Function
End Module
As I said in my comment, you could just call Select and ToArray as needed. If you really want an extension though, you would need to make your method generic:
<Extension>
Public Function ToStrings(Of T)(source As T()) As String()
'If Not GetType(T).IsEnum Then
' Return Nothing
'End If
Return Array.ConvertAll(source, Function(e) e.ToString())
End Function
There's no generic constraint that can limit that method to be called on just an array of Enum values so you can use an If statement to either return Nothing or throw an exception. I don't really see the point though, as it really doesn't hurt if you allow the same method to be called on another array of any other type anyway.

Checking is StringBuilder() AppendLine() is an empty string

I want to conditionally append a string, derived from a function, to a string builder. The required condition is that the function is not returning a blank string ("").
The purpose of conditionally appending the string is to avoid AppendLine() appending a line when the string (returned from the function) being appended is empty.
My current code (wrapping the function in a condition calling the very same function):
Dim builder As New System.Text.StringBuilder()
builder.Append("Some text...").AppendLine()
If Not IsNothing(someFunction(someParameterAA, someParameterAB)) Then
builder.Append(someFunction(someParameterAA, someParameterAB)).AppendLine()
End If
If Not IsNothing(someFunction(someParameterBA, someParameterBB)) Then
builder.Append(someFunction(someParameterBA, someParameterBB)).AppendLine()
End If
builder.AppendLine().Append("...some text.")
Dim s As String = builder.ToString
MessageBox.Show(s)
It is my hope that a more efficient alternative exists (efficient in terms of the amount of code to be written). Ultimately, I'd like to avoid calling the same function twice however I cannot independently add the builder.Append line of code to my function. Is it instead possible to target builder.Append?
Example of the potential logic:
If `builder.Append()` inside the following brackets is not an empty string then:
(
builder.Append(someFunction(someParameterAA, someParameterAB)).AppendLine()
)
If anyone has a solution on the above, bear in mind the prequisite of concision (=< 2) lines of code additional to the builder.Append() line.
I'm open to any other suggestions.
Create another method to do the appending like so:
CheckBeforeAppend(someFunction(someParameterAA, someParameterAB), builder)
CheckBeforeAppend(someFunction(someParameterBA, someParameterBB), builder)
....
Public Sub CheckBeforeAppend(s As String, sb As StringBuilder)
If Not String.IsNullOrEmpty(s)
sb.Append(s).AppendLine()
End If
End Sub
A simple refactor such as this shortens your original code and means you don't need to duplicate the null or empty check on the return value of your function.
And for the extension method (place this code in a Module):
<Extension()>
Public Sub CheckBeforeAppend(s As String, sb As StringBuilder)
If Not String.IsNullOrEmpty(s)
sb.Append(s).AppendLine()
End If
End Sub
usage:
someFunction(someParameterAA, someParameterAB).CheckBeforeAppend(sb)
or for an extension on StringBuilder:
<Extension()>
Public Sub CheckBeforeAppend(sb As StringBuilder, s As String)
If Not String.IsNullOrEmpty(s)
sb.Append(s).AppendLine()
End If
End Sub
usage:
builder.CheckBeforeAppend(someFunction(someParameterAA, someParameterAB))
You can avoid calling the function twice by storing the result of the function in a variable.
Dim myString As String = someFunction(someParameterAA, someParameterAB)
If myString <> "" Then
builder.Append(myString).AppendLine()
End If
myString = someFunction(someParameterBA, someParameterBB)
If myString <> "" Then
builder.Append(myString).AppendLine()
End If
This way you just call the function once and check your conditions with the variable. Also the code looks a lot smaller and more understandable.

VB.NET equivalent of a nameless variable in C#?

In C#, you can do this:
new MyClass().MyMethod();
The method is executed and the reference is (typically) discarded since no reference to the object is kept.
My question is: Is this possible with VB.NET (.NET v4)?
Edit: I suppose this is a better example:
new Thread((x) => doSomething()).Start();
Is this even possible in VB.NET?
VB.NET has stricter rules about the syntax of a statement. The curly-brace languages allow any expression to also be a statement, simply by terminating it with a semi-colon. That's not the case for VB.NET. You can only use this syntax if the method you call is a Function. Which allows you to use the assignment statement:
Dim result = New Test().Func()
If it is a Sub then you'll have to use the assignment statement to store the object reference. This otherwise has no runtime effect, the reference is optimized away.
In addition to Hans' answer, you could use a With statement:
Sub Main
With New Person("Ahmad")
.PrintName()
.Name = "Mageed"
.PrintName()
End With
End Sub
Public Class Person
Public Property Name As String
Public Sub New(ByVal name As String)
Me.Name = name
End Sub
Public Sub PrintName()
Console.WriteLine("Name: {0} - Len: {1}", Name, Name.Length)
End Sub
End Class
It's not as succinct as C#, but the reference to the object is discarded after End With.
If you're explicitly wanting to call a sub and not a function, you can:
Call (New obj).Func()
Which will anonymously create a new obj, and call its Func() method
You can do lambda functions in VB.NET like this:
Dim test = Function (x)
x.doStuff()
End Function
Which would be semantically equivilent to:
var test = (x) => x.doStuff();
I think the one constraint though is that it must return a result under VB.NET.

VB.NET string manipulation -- caps to only one letter caps

So I have a string "NEW".
What is the SIMPLEST way to convert that string to "New".
Basically right now I'm doing this:
Case "NEW"
makes = connector.GetMakesByYear(_AuthorizationKey, "NewCar", CDate(Now), Year)
Case "USED"
makes = connector.GetMakesByYear(_AuthorizationKey, "UsedCar", CDate(Now), Year)
And I would prefer not to use a case statement because it's only one parameter that needs to change, and both are appended with "Car".
Using the “old” string functions, you can use this:
result = StrConv("hello world", VbStrConv.ProperCase)
to convert a string to “proper case”. However, in your case this would probably result in (if I read this right) “Usercar”, not “UserCar”.
You may use:
String.Format("{0}{1}", carType.Substring(0, 1).ToUpper(), carType.Substring(1).ToLower())
Regards
If this is something you plan on using often, you might consider creating an extension function for it:
Public Module ObjectExtensions
<System.Runtime.CompilerServices.Extension()>
Public Function firstLetterToUpper(ByVal s As String) As String
Return Char.ToUpper(s.First()) + New String(s.Skip(1).Select(Function(x) Char.ToLower(x)).ToArray())
End Function
End Module
Then you can do something like this:
"USED".firstLetterToUpper()
Which returns "Used"
Obviously you can change the function body with something more efficient like Guilherme's or Konrad's answer, but making an extension function for this can be quite useful if you do plan on doing something like this often, or if you are just a fan of readability.
Here what I have done!
Function InitUpperCase(ByVal str As String) As String
If String.IsNullOrEmpty(str) Then
Return str
End If
Dim charlist() As Char = str.ToCharArray
charlist(0) = Char.ToUpper(charlist(0))
Return New String(charlist)
End Function
to see Output
MessageBox.Show(InitUpperCase("my first letter"))

VB.NET - Adding more than 1 string to .contains

I have an HTMLElementCollection that I'm going through using a For Each Loop to see if the InnerHTML contains certain words. If they do contain any of those keywords it gets saved into a file.
Everything works fine but I was wondering if there is a way to simplify. Here's a sample
For Each Helement As HtmlElement In elements
If Helement.InnerHtml.Contains("keyword1") Or Helement.InnerHtml.Contains("keyword2") Or Helement.InnerHtml.Contains("keyword3") Or Helement.InnerHtml.Contains("keyword4") Or Helement.InnerHtml.Contains("keyword5") = True Then
' THE CODE TO COPY TO FILE
End If
Next Helement
Does anything exist that would work like:
If Helement.InnerHtml.Contains("keyword1", "keyword2", "keyword3", "keyword4", "keyword5")
The way I'm doing it now just seems wasteful, and I'm pretty OCD about it.
1) One approach would be to match the InnerHtml string against a regular expression containing the keywords as a list of alternatives:
Imports System.Text.RegularExpressions
Dim keywords As New Regex("keyword1|keyword2|keyword3")
...
If keywords.IsMatch(HElement.InnerHtml) Then ...
This should work well if you know all your keywords beforehand.
2) An alternative approach would be to build a list of your keywords and then compare the InnerHtml string against each of the list's elements:
Dim keywords = {"keyword1", "keyword2", "keyword3"}
...
For Each keyword As String In keywords
If HElement.InnerHtml.Contains(keyword) Then ...
Next
Edit: The extension method suggested by Rob would result in more elegant code than the above approach #2, IMO.
You could write an Extension Method to string that provides a multi-input option, such as:
Public Module StringExtensionMethods
Private Sub New()
End Sub
<System.Runtime.CompilerServices.Extension> _
Public Function Contains(ByVal str As String, ByVal ParamArray values As String()) As Boolean
For Each value In values
If str.Contains(value) Then
Return True
End If
Next
Return False
End Function
End Module
You could then call that instead, as in your second example :)
Here's another extension method that cleans up the logic a little with LINQ:
<Extension()>
Public Function MultiContains(str As String, ParamArray values() As String) As Boolean
Return values.Any(Function(val) str.Contains(val))
End Function