Setting value to a not-existing array item? - vb.net

Please take a look at the following code:
Try
Dim Reader As New System.IO.StreamReader(PositionsFileName)
Do While Reader.Peek() <> -1
Dim Positions() As String = Reader.ReadLine().Split("|")
If (Positions(0) Is Nothing) Or (Positions(1) Is Nothing) Or (Positions(2) Is Nothing) Then
' something
End If
Loop
Catch ex As Exception
ex.Source = Nothing
End Try
I am reading a file and expecting format something|something1|something2. I am trying to make it set "Nothing" to the array index which does not exists (the file format is broken), so that the If statement goes smoothly, but it seem I am doing it wrong. Can you give me some hints?

If you do Split("|") and there are only 2 items (for example, something|something1), Positions(2) will not be Nothing, it will just not be there. So your code will raise an exception, something about index out of bounds of the array.
If you need Positions(2) contain Nothing in this case, you code can look like this:
Dim Positions(2) As String
Dim tmpArray() As String = Reader.ReadLine().Split("|")
For i = 0 To UBound(Positions)
If i <= UBound(tmpArray) Then
Positions(i) = tmpArray(i)
Else
Positions(i) = Nothing
End If
Next

I assume that you only have three "Somethings" per valid line. If so, try writing your Positions() assignment like this:
Dim Positions() As String = Reader _
.ReadLine() _
.Split("|") _
.Concat(Enumerable.Repeat("Nothing", 3)) _
.Take(3) _
.ToArray()
This will ensure that you have three items every time. No need to check for nothings.

Just check positions.length after the split. Also, if you want to check for cases like "||Something2|Something3", the first position will be "" not Nothing. The orelse is a shortcircuit that will keep the latter condtitions from being evaulated if an earlier condition is met.
If Positions.length < 3 OrElse Positions(0) = "" OrElse Positions(1) = "" OrElse Positions(2) = "" Then
' something
End If

Related

Find and replace text string with only parts of the existing string

I have a .txt file that is formatted like this:
----------------------------------------------------------------------------------------------------------------------------------------------------------
|Order Number|PegReqOrNo |Loc |Product Number |OrdSrtTime|OrdEndTime|Prod. Time|Reqmt Time|OrdSrtDate|OrdEndDate|Comp. Date|Reqmt Date| Date Var.|
----------------------------------------------------------------------------------------------------------------------------------------------------------
|000105812778| |0002|10000347 |10:03:50 |19:37:43 |19:37:43 |00:00:00 |08/02/2016|02/16/2022|02/16/2022| | 0/00:00:00|
|000106805034|4200252838 |0002|H827080082GAAZ |13:43:25 |08:30:04 |08:30:04 |15:00:00 |02/18/2020|09/02/2020|09/02/2020|08/24/2020| 8/17:30:04-|
I am looking to change the strings with pattern ##:##:## in the date var. column, the last on the right. I want to retain the value in front of the / and the - at the end, if there is one.
The two examples from the data above are 0/00:00:00 and 8/17:30:04-, thus I want to retain 0 and -8 respectively.
For your strange request you can use as follows. I hope I well understood
Private Sub AdjustMyData()
Try
Dim allLines As IEnumerable(Of String) = IO.File.ReadAllLines("C:\Users\YourUser\Desktop\test\test.txt")
Dim data As List(Of String) = (From elemet In allLines).Select(Function(linea As String)
If linea.Contains("|") Then
Dim parts = linea.Split(CChar("|"))
Dim newLinea = (From part In parts
Where part IsNot Nothing
Where part Like "*#/##:##:##*").Select(
Function(s As String) As String
If s Is Nothing OrElse s.Length = 0 Then Return ""
linea = linea.Replace(s, "~")
Dim numerisS As String = CStr(IIf(s.Contains("-"), "-", "")) & Trim(s.Remove(s.IndexOf("/")))
Return Space(s.Length - numerisS.Length) & numerisS
End Function).ToList.FirstOrDefault
Return Strings.Replace(linea, "~", newLinea)
End If
Return linea
End Function).ToList
IO.File.WriteAllLines("C:\Users\YourUser\Desktop\test\testFinal.txt", data.ToArray)
Catch ex As Exception
Console.WriteLine(ex.ToString)
End Try
End Sub

Is it possible to create an extension method that can be used in an sql query?

I have an application that draws elements from a database when a search button is clicked. I need to update it so that I can find elements that have a specific Substring exactly 6 places into the string. For example, I need to find 111-2233-44-555 by looking for 33 at the 6th and 7th places. My first instinct was to create an extension method for the string class so that I could say something like this:
Dim example As String = "111-2233-44-555"
If example.HasYear(33) Then
'Do Something'
End If
And this worked perfectly. Here's the method I made:
Public Module StringExtensionMethods
''' <summary>
''' Finds the year in a competition number of format XXX-XXXX-XX-XXX
''' </summary>
''' <param name="pstrCompNum">The competition number to find the year in</param>
<Extension()>
Public Function HasYear(ByVal pstrCompNum As String, ByVal pstrCompYear As String) As Boolean
Try
Dim testString As String = pstrCompNum
Debug.Print(testString)
Dim testSubstring As String = testString.Substring(6, 2)
If testSubstring.Equals(pstrCompYear) Then
Return True
End If
Return False
Catch ex As Exception
Throw ex
End Try
End Function
End Module
But a problem arises when I try to use this method in an SQL query. Rightfully so, since HasYear() isn't related to SQL in any way whatsoever. Here's the query I want to execute:
Dim o = From c In myContext.Competitions.Include("CodeJusticeBranches").Include("CodeJusticeLocations").Include("CodeCompetitionTypes").Include("CodePositionTypes").Include("CompetitionPositions") _
Where (pstrCompNum Is Nothing OrElse c.comp_number = pstrCompNum) _
And (pstrCompYear Is Nothing OrElse c.comp_number.HasYear(strYear) = True) _
And (pstrCompTypeId Is Nothing OrElse c.CodeCompetitionTypes.code_ct_id = CInt(pstrCompTypeId)) _
And (pstrBranchId Is Nothing OrElse c.CodeJusticeBranches.code_branch_id = CInt(pstrBranchId)) _
And (pstrPosTypeId Is Nothing OrElse c.CodePositionTypes.code_pos_type_id = CInt(pstrPosTypeId)) _
Order By c.comp_number _
Select c
I get the error LINQ to Entities does not recognize the method 'Boolean HasYear (System.String, System.String)' method, and this method cannot be translated into a store expression.
So what I'm looking for is essentially a way to make an extension method that can be used in an SQL query. Any ideas?
Don't include custom functions in your LINQ. It must be convertable to SQL so your extension method doesn't qualify. Just execute your query with supported where clauses, then tack on your custom queries after.
Dim o = (
From c In myContext.Competitions.Include("CodeJusticeBranches").Include("CodeJusticeLocations").Include("CodeCompetitionTypes").Include("CodePositionTypes").Include("CompetitionPositions")
Where (pstrCompNum Is Nothing OrElse c.comp_number = pstrCompNum) _
And (pstrCompTypeId Is Nothing OrElse c.CodeCompetitionTypes.code_ct_id = CInt(pstrCompTypeId)) _
And (pstrBranchId Is Nothing OrElse c.CodeJusticeBranches.code_branch_id = CInt(pstrBranchId)) _
And (pstrPosTypeId Is Nothing OrElse c.CodePositionTypes.code_pos_type_id = CInt(pstrPosTypeId))
Order By c.comp_number Select c).ToList().
Where(Function(c) c.pstrCompYear Is Nothing OrElse c.comp_number.HasYear(strYear))
I like what #djv posted, but I've decided to try out Andrew Morton's suggestion, to just omit the HasYear() altogether. I ended up using this:
Dim k = From c In myContext.Competitions.Include("CodeJusticeBranches").Include("CodeJusticeLocations").Include("CodeCompetitionTypes").Include("CodePositionTypes").Include("CompetitionPositions") _
Where (pstrCompNum Is Nothing OrElse c.comp_number = pstrCompNum) _
And (pstrCompYear Is Nothing OrElse c.comp_number.Substring(6, 2) = pstrCompYear) _
And (pstrCompTypeId Is Nothing OrElse c.CodeCompetitionTypes.code_ct_id = CInt(pstrCompTypeId)) _
And (pstrBranchId Is Nothing OrElse c.CodeJusticeBranches.code_branch_id = CInt(pstrBranchId)) _
And (pstrPosTypeId Is Nothing OrElse c.CodePositionTypes.code_pos_type_id = CInt(pstrPosTypeId)) _
Order By c.comp_number _
Select c
Notice the line And (pstrCompYear Is Nothing OrElse c.comp_number.Substring(6, 2) = pstrCompYear)
This solved my problem very simply.

For Loop: changing the loop condition while it is looping

What I want to do is replace all 'A' in a string with "Bb". but it will only loop with the original string not on the new string.
for example:
AAA
BbAA
BbBbA
and it stops there because the original string only has a length of 3. it reads only up to the 3rd index and not the rest.
Dim txt As String
txt = output_text.Text
Dim a As String = a_equi.Text
Dim index As Integer = txt.Length - 1
Dim output As String = ""
For i = 0 To index
If (txt(i) = TextBox1.Text) Then
output = txt.Remove(i, 1).Insert(i, a)
txt = output
TextBox2.Text += txt + Environment.NewLine
End If
Next
End Sub
I think this leaves us looking for a String.ReplaceFirst function. Since there isn't one, we can just write that function. Then the code that calls it becomes much more readable because it's quickly apparent what it's doing (from the name of the function.)
Public Function ReplaceFirst(searched As String, target As String, replacement As String) As String
'This input validation is just for completeness.
'It's not strictly necessary.
'If the searched string is "null", throw an exception.
If (searched Is Nothing) Then Throw New ArgumentNullException("searched")
'If the target string is "null", throw an exception.
If (target Is Nothing) Then Throw New ArgumentNullException("target")
'If the searched string doesn't contain the target string at all
'then just return it - were done.
Dim foundIndex As Integer = searched.IndexOf(target)
If (foundIndex = -1) Then Return searched
'Build a new string that replaces the target with the replacement.
Return String.Concat(searched.Substring(0, foundIndex), replacement, _
searched.Substring(foundIndex + target.Length, searched.Length - (foundIndex + target.Length)))
End Function
Notice how when you read the code below, you don't even have to spend a moment trying to figure out what it's doing. It's readable. While the input string contains "A", replace the first "A" with "Bb".
Dim input as string = "AAA"
While input.IndexOf("A") > -1
input = input.ReplaceFirst(input,"A","Bb")
'If you need to capture individual values of "input" as it changes
'add them to a list.
End While
You could optimize or completely replace the function. What matters is that your code is readable, someone can tell what it's doing, and the ReplaceFirst function is testable.
Then, let's say you wanted another function that gave you all of the "versions" of your input string as the target string is replaced:
Public Function GetIterativeReplacements(searched As String, target As String, replacement As String) As List(of string)
Dim output As New List(Of String)
While searched.IndexOf(target) > -1
searched = ReplaceFirst(searched, target, replacement)
output.Add(searched)
End While
Return output
End Function
If you call
dim output as List(of string) = GetIterativeReplacments("AAAA","A","Bb")
It's going to return a list of strings containing
BbAAA, BbBbAA, BbBbBbA, BbBbBbBb
It's almost always good to keep methods short. If they start to get too long, just break them into smaller methods with clear names. That way you're not trying to read and follow and test one big, long function. That's difficult whether or not you're a new programmer. The trick isn't being able to create long, complex functions that we understand because we wrote them - it's creating small, simpler functions that anyone can understand.
Check your comments for a better solution, but for future reference you should use a while loop instead of a for loop if your condition will be changing and you're wanting to take that change into account.
I've made a simple example below to help you understand. If you tried the same with a for loop, you'd only get "one" "two" and "three" printed because the for loop doesn't 'see' that vals was changed
Dim vals As New List(Of String)
vals.Add("one")
vals.Add("two")
vals.Add("three")
Dim i As Integer = 0
While i < vals.Count
Console.WriteLine(vals(i))
If vals(i) = "two" Then
vals.Add("four")
vals.Add("five")
End If
i += 1
End While
If you do want to replace one by one instead of using the Replace function, you could use a while loop to look for the index of your search character/string, and then replace/insert at that index.
Sub Main()
Dim a As String = String.Empty
Dim b As String = String.Empty
Dim c As String = String.Empty
Dim d As Int32 = -1
Console.Write("Whole string: ")
a = Console.ReadLine()
Console.Write("Replace: ")
b = Console.ReadLine()
Console.Write("Replace with: ")
c = Console.ReadLine()
d = a.IndexOf(b)
While d > -1
a = a.Remove(d, b.Length)
a = a.Insert(d, c)
d = a.LastIndexOf(b)
End While
Console.WriteLine("Finished string: " & a)
Console.ReadLine()
End Sub
Output would look like this:
Whole string: This is A string for replAcing chArActers.
Replace: A
Replace with: Bb
Finished string: This is Bb string for replBbcing chBbrBbcters.
I was going to write a while loop to answer your question, but realized (with assistance from others) that you could just .replace(x,y)
Output.Text = Input.Text.Replace("A", "Bb")
'Input = N A T O
'Output = N Bb T O
Edit: There is probably a better alternative, but i quickly jotted this loop down, hope it helps.
You've said your new and don't fully understand while loops. So if you don't understand functions either or how to pass arguments to them, I'd suggest looking that up too.
This is your Event, It can be a Button click or Textbox text change.
'Cut & Paste into an Event (Change textboxes to whatever you have input/output)
Dim Input As String = textbox1.Text
Do While Input.Contains("A")
Input = ChangeString(Input, "A", "Bb")
' Do whatever you like with each return of ChangeString() here
Loop
textbox2.Text = Input
This is your Function, with 3 Arguments and a Return Value that can be called in your code
' Cut & Paste into Code somewhere (not inside another sub/Function)
Private Function ChangeString(Input As String, LookFor As Char, ReplaceWith As String)
Dim Output As String = Nothing
Dim cFlag As Boolean = False
For i As Integer = 0 To Input.Length - 1
Dim c As Char = Input(i)
If (c = LookFor) AndAlso (cFlag = False) Then
Output += ReplaceWith
cFlag = True
Else
Output += c
End If
Next
Console.WriteLine("Output: " & Output)
Return Output
End Function

Syntax Out Of Range Unhandled

I'm a bit of a newbie so any advice would be great. I have a program
that opens a CSV, and then saves it as a csv with a different name. there will be a set of rules to change fields but haven't got that far yet.
when I run this on a small csv file (about 4 columns and rows) it works fine, but with a larger file, it fails with the error above. i'm sure its something daft but I I'm at a loss.
Thanks,
Dean
Dim FileName = tbOpen.Text
Dim fileout = tbSave.Text
Dim lines = File.ReadAllLines(FileName)
Dim output As New List(Of String)
For Each line In lines
Dim fields = line.Split(","c)
If fields(1) = "" Then 'This is where the error is triggered
fields(1) = "Norman"
End If
If fields(3) = "" Then
fields(3) = "Blue Leather"
End If
If fields(4) = "" Then
fields(3) = "Interlined"
End If
output.Add(String.Join(","c, fields))
Next
File.WriteAllLines(fileout, output)
Try
Dim a As String = My.Computer.FileSystem.ReadAllText(tbSave.Text)
Dim b As String() = a.Split(vbNewLine)
ListBox2.Items.AddRange(b)
Catch ex As Exception
MsgBox("error")
End Try
Keep in mind that arrays start a index 0. VB is notorious for stretching this concept. Normally, when you declare an array with 10 elements the indexes would be from 0 - 9. With VB, on the other hand, the indexes will be from 0 - 10 which will actually give you 11 elements.

Validating multiple textboxes with multiple checks

I have multiple textboxes in a groupbox, and can successfully cycle through them all. However the checkNumbers sub fails to recognise blank/null entries, and also non-numeric characters. The correctValidation boolean should return true if all the criteria are met (no blanks/nulls, and must be a number between 1-20). Any thoughts on how to solve this would be appreciated.
Private Sub checkNumbers()
Try
For Each txt As TextBox In Me.gbTechnical.Controls.OfType(Of TextBox)()
If txt.Text <> "" And IsNumeric(txt.Text) And (Integer.Parse(txt.Text) >= 1 And Integer.Parse(txt.Text) <= 20) Then
correctValidation = True
Else
correctValidation = False
MsgBox("Please ensure all numbers are between 1 and 20")
Exit Sub
End If
Next
Catch ex As Exception
MessageBox.Show("General: Please ensure all numbers are between 1 and 20")
End Try
End Sub
I would use Integer.TryParse and then >= 1 AndAlso <= 20. You could use this LINQ query:
Dim number As Int32
Dim invalidTextBoxes =
From txt In gbTechnical.Controls.OfType(Of TextBox)()
Where Not Integer.TryParse(txt.Text, number) OrElse number < 1 OrElse number > 20
Dim correctValidation = Not invalidTextBoxes.Any()
Note that you should almost always use AndAlso instead of And and OrElse instead of Or since those operators are Is short-circuiting boolean operators. This can be more efficient and - more important - can prevent errors. Consider this:
Dim text = ""
If txt IsNot Nothing And txt.Text.Length <> 0 Then text = txt.Text
This fails if txt is nothing since the second condition is evaluated even if the first already was evaluated to false which causes a NullReferenceException at txt.Text.
if you only want a number value, why don't you try to use NumericUpDown. You can also set the Minimum and Maximum in property or use
NumericUpDown1.Maximum = 20
so, there won't be a need to do checkNumbers.
Or is there any reason that you have to use textbox??