I am trying to code a part of a software where I try to show the results that match search criteria.
I have a textbox where I can type one or more words I want to search and a listview that contains 4 different columns and a dozen rows. The idea is that each listview row contains lots of words and I want to see only the rows that contain all the words I have typed in the textbox. I have finished the code that searches for one term only. The problem I am having is that I don't fully understand how to do the same, but using multiple terms instead of one term only.
In the textbox, I write the words I want to search separated by a space. I have a variable where I keep the whole content of the listview row separated by : (example => col1row1content:col1row2content:col1row3content,etc). Summarizing, I want to check if a string (the full content of a row) contains all other strings (each word I have typped in the textbox).
This is the code I have implemented:
Dim textboxFullContentArray As String() = textboxSearch.Split(New Char() {" "c})
Dim Content As String
Dim containsAll As Boolean = False
Dim wholeRowContent(listviewMain.Items.Count - 1) As String ' each index of the array keeps the entire row content (one array contains all 4 cells of the row)
' wholeRowContent contains in one index the entire content of a row. That means,
' the index contains the 4 cells that represent an entire row.
' The format is like "rowData1:rowData2:rowData3:rowData4" (omitted for simplicity)
For Q As Integer = 0 To listviewMain.Items.Count - 1
For Each Content In textboxFullContentArray
If wholeRowContent(Q).ToLower.Contains(Content) Then
containsAll = True
' rest of the code...
ElseIf Not wholeRowContent(Q).ToLower.Contains(Content) Then
containsAll = False
Exit For
End If
Next
Next
But of course, this code is showing false positives and I think it's not a good solution. I think it must be much easier and I am overcomplicating the concept.
I am using VB.Net 2013
You can determine whether a String contains all of a list of substrings with a single line of code:
If substrings.All(Function(s) str.IndexOf(s, StringComparison.OrdinalIgnoreCase) >= 0) Then
Notice that I have actually implemented a case-insensitive comparison, rather than using ToLower or ToUpper.
It may not seem as neat to call IndexOf rather than Contains but guess what: Contains actually calls IndexOf internally anyway:
public bool Contains(string value)
{
return this.IndexOf(value, StringComparison.Ordinal) >= 0;
}
You can write your own extension methods if you want a case-insensitive Contains method:
<Extension>
Public Function Contains(source As String,
value As String,
comparisonType As StringComparison) As Boolean
Return source.IndexOf(value, comparisonType) >= 0
End Function
Your If/Else looks like it could be simplified. I would set your containsAll value to true outside the nested loops, and only if you encounter a "Content" in "textboxFullContentArray" that is not contained in wholeRowContent(Q) you set containsAll to false, otherwise do nothing.
Also, one way to see what's going on is to print statements with the values that are being compared throughout your function, which you can read through and see what is happening at runtime when the false positives occur.
After some hours looking for a simple and effective solution (and trying different codes), I have finally found this solution that I adapted from: Bad word filter - stackoverflow
For Q As Integer = 0 To listviewMain.Items.Count - 1
If textboxFullContentArray.All(Function(b) wholeRowContent(q).ToLower().Contains(b.ToLower())) Then
' my code
End If
Next
Related
I have been working on a project, and am attempting to make a new array for data. I have tried making an empty array with Dim Name() As String = {}. I am using a ListView, and the way I have done it there are blank spots where I have gotten rid of data. This is my current code:
Sub English(ByVal Country() As String, ByVal Language() As String)
rbDisplayallData.Checked = False
lstResults.Visible = True
lstResults.Items.Clear()
lstResults.Columns.Clear()
With lstResults
.View = View.Details
.Columns.Add("English Speaking Countries", 200, HorizontalAlignment.Left)
End With
For i = 0 To 181
Dim EnglishSpeakingCountries(i) As String
If Language(i) = "English" Then
EnglishSpeakingCountries(i) = Country(i)
End If
lstResults.Items.Add(New ListViewItem({EnglishSpeakingCountries(i)}))
Next
End Sub
I am trying to get rid of these spaces.
I Was thinking if I were to compact the array or make a new one with the same data going into a new array it would fix the issue.
If you have a solution please let me know.
There are two things that could be considered an empty array
An array with no elements, i.e. a Length of zero.
An array where every element is Nothing.
All arrays are fixed-length. Once you create an array with a particular number of elements, it always has that number of elements. You can use ReDim Preserve or Array.Resize but, in both those cases, what actually happens is that a new array is created and the elements copied from the old array. The new array is assigned to the same variable but anywhere the old array is referenced, it will still have that same number of elements. Try running this code to see that in action:
Dim a1 As String() = {}
Dim a2 As String() = {"First", "Second", "Third"}
Dim b1 = a1
Dim b2 = a2
Console.WriteLine(a1.Length)
Console.WriteLine(a2.Length)
Console.WriteLine(b1.Length)
Console.WriteLine(b2.Length)
Console.WriteLine()
ReDim Preserve a1(2)
Array.Resize(a2, 6)
Console.WriteLine(a1.Length)
Console.WriteLine(a2.Length)
Console.WriteLine(b1.Length)
Console.WriteLine(b2.Length)
Console.ReadLine()
Output:
0
3
0
3
3
6
0
3
As you'll be able to see, a1 and a2 end up referring to new arrays with the specified lengths but the original arrays with the original lengths still exist and are still accessible via b1 and b2.
If you start with an array with no elements then you can use ReDim Preserve or Array.Resize to give the appearance of resizing the array but that's not really what's happening and that should generally be avoided. If you know how many elements you'll end up with then you could create an array of that size and then set each element in turn. You'd need to keep track of the next element index though, so that's still a bit tedious.
Generally speaking, if you want an array-like data structure but you want it to be able to grow and shrink as required, you should use a collection. The most common collection is the List(Of T), where T is any type you care to specify in your code. If you want to store String objects then use a List(Of String). You can call Add to append a new item to the end of the list, as well as Insert, Remove and RemoveAt methods. You can also get or set an item by index, just as you can do for array elements.
Note that a List(Of T) actually uses an array internally and uses the aforementioned method of "resizing" that array. It optimises the process somewhat though, which makes the code easier for you to write and large collections more efficient to use.
It's worth noting that, in your own code, the Columns and Items properties of your ListView are both collections, although they are slightly different to the List(Of T) class.
Looking at your original code, this:
For i = 0 To 181
Dim EnglishSpeakingCountries(i) As String
If Language(i) = "English" Then
EnglishSpeakingCountries(i) = Country(i)
End If
lstResults.Items.Add(New ListViewItem({EnglishSpeakingCountries(i)}))
Next
could be changed to this:
Dim englishSpeakingCountries As New List(Of String)
For i = 0 To 181
If Language(i) = "English" Then
englishSpeakingCountries.Add(Country(i))
lstResults.Items.Add(Countries(i))
End If
Next
Note that you're just adding items to two collections. I guess the question is whether you actually need this extra collection at all. If you do want to use it later then you need to assign it to a member variable rather than a local variable. If you don't need it later then don't create it at all. As I said, you're already adding items to a collection in the ListView. Maybe that's all you need, but you haven't provided enough info for us to know.
I have file with tags and targets, this is example:
TAG1|TARGET1,TARGET2
TAG2|TARGET3,TARGET4
I start by creating String Array using File.ReadAllLines
Dim MAIN As String() = File.ReadAllLines("")
At some point I have one of targets and I need to know what was the tag index (which array line is it), so for example if I have TARGET3 I want to know it's in second line so it's in MAIN(1) and then I can grab TAG = TAG2.
I can't get it working, I tried few methods:
Array.IndexOf(MAIN,"TARGET3")
always returned -1, it worked with full string tho,
Array.IndexOf(MAIN,"TAG2|TARGET3,TARGET4")
returned 1. I tried with Array.FindIndex, was the same.
So my question is: how to get index of partial array item. Thank you for any help.
You can use Linq to search your array in this way
Dim search = "TARGET3"
Dim line = MAIN.FirstOrDefault(Function(x) x.Contains(search))
This will return directly the line with the matching word
Sorry I'am new on Ruby (just a Java programmer), I have two string arrays:
Array with file paths.
Array with patterns (can be a path or a file)
I need to check each patter over each "file path". I do with this way:
#flag = false
["aa/bb/cc/file1.txt","aa/bb/cc/file2.txt","aa/bb/dd/file3.txt"].each do |source|
["bb/cc/","zz/xx/ee"].each do |to_check|
if source.include?(to_check)
#flag = true
end
end
end
puts #flag
This code is ok, prints "true" because "bb/cc" is in source.
I have seen several posts but can not find a better way. I'm sure there should be functions that allow me to do this in fewer lines.
Is this is possible?
As mentioned by #dodecaphonic use Enumerable#any?. Something like this:
paths.any? { |s| patterns.any? { |p| s[p] } }
where paths and patterns are arrays as defined by the OP.
While that will work, that's going to have geometric scaling problems, that is it has to do N*M tests for a list of N files versus M patterns. You can optimize this a little:
files = ["aa/bb/cc/file1.txt","aa/bb/cc/file2.txt","aa/bb/dd/file3.txt"]
# Create a pattern that matches all desired substrings
pattern = Regexp.union(["bb/cc/","zz/xx/ee"])
# Test until one of them hits, returns true if any matches, false otherwise
files.any? do |file|
file.match(pattern)
end
You can wrap that up in a method if you want. Keep in mind that if the pattern list doesn't change you might want to create that once and keep it around instead of constantly re-generating it.
while migrating an Excel-VBA project to Visual Basic 2010, I came across a problem when populating arrays.
In Excel-VBA I would do something like
Function mtxCorrel() As Variant
mtxCorrel = wsCorr.UsedRange
End Function
to read an m*n-matrix (in this case n*n), that is conveniently stored in a worksheet, into an array for further use.
In VB2010 I obviously won't use an Excel-Worksheet as storage. csv-Files (see below) seem like a decent alternative.
I want to populate an 2d-array with the csv-contents without looping n*n-times. Let's assume I already know n=4 for demonstration purposes.
This suggests that what I want to do cant be done.
Nevertheless I still hope something like the following could work:
Function mtxCorrel() As Object
Dim array1(4, 4) As String
Using ioReader As New Microsoft.VisualBasic.FileIO.TextFieldParser("C:\cm_KoMa.csv")
With ioReader
.TextFieldType = FileIO.FieldType.Delimited
.SetDelimiters(";")
' Here I want to...
' A) ...either populate the whole 2d-array with something like
array1 = .ReadToEnd()
' B) ... or populate the array by looping its 1d-"rows"
While Not .EndOfData
array1(.LineNumber, 0)= .ReadFields()
End While
End With
End Using
return array1
End Function
Notes:
I'm mainly interested in populating the array.
I'm less interested in potential errors with determining which csv-line belongs into which 1d-"row", and also not interested in checking n.
Appendix: sample csv-File:
1;0.5;0.9;0.3
0.5;1;0.6;0.2
0.9;0.6;1;0.1
0.3;0.2;0.1;1
I have a text-file(customers.txt) from which I'm trying to extract certain pieces of data from to place in a list-box(lstReportsUnresolved). I'm using commas to split up lines in the text-box and at a certain split, if the text is equal to "No" then the previous split is added to the listbox.
Code:
Private Sub btnUnresolved_Click(sender As Object, e As EventArgs) Handles btnUnresolved.Click
For Each line As String In File.ReadLines("customers.txt")
Dim data As String() = line.Split(",")
If data(18) = "No" Then
lstReportsUnresolved.Items.Add(data(17))
End If
If data(20) = "No" Then
lstReportsUnresolved.Items.Add(data(19))
End If
If data(22) = "No" Then
lstReportsUnresolved.Items.Add(data(21))
End If
If data(24) = "No" Then
lstReportsUnresolved.Items.Add(data(23))
End If
Next
End Sub
It was working fine earlier, but I came back and now try to run it and it stops and highlights the line If data(18)="No" Thenwith the error "An unhandled exception of type 'System.IndexOutOfRangeException' occurred in Semester Two Project.exe" with additional info saying "Index was outside the bounds of the array."
Here is an example of a line from the text file
Mark,Barry,07/04/1996,Redbrook,Sandymount,Dublin,Ireland,094-378231,085-0983623,markbarry96#gmail.com,Basic Cable,VB Fibre Broadband,VB Sports,VB Movies,Chat Chat Phone,VB Anywhere,VB Data,Unavailable Services,Yes,Incorrect Bill,Yes,Poor Tv Reception,No, ,No
Thank you for your help and time
It could be due to File.ReadLines(). The method only reads some (or maybe even one) lines at a time, meaning it could have read less (if it's split into lines) than required to make your split return an array of at least 19 elements. You should use File.ReadAllLines() instead.
Just replace File.ReadLines() with File.ReadAllLines():
For Each line As String In File.ReadAllLines("customers.txt")
If this is a big file and you don't want to use File.ReadAllLines(), you could instead check the end of the array (if the "No" always appears there), instead of having a static index.
EDIT:
Actually, either if you use ReadLines or ReadAllLines you should probably stick with checking from the end:
If data(data.Length - 1) = "No" Then
lstReportsUnresolved.Items.Add(data(data.Length - 2))
End If