(VB.NET) Find all duplicates in a list of objects based on multiple properties - vb.net

I have a list of CommissionStatement objects which i created. I need to create a new list which only holds the duplicates found in this list based on 3 properties: Firm; Provider; and Total (ie each of these 3 have to be the same in 2 or more objects for it to be recognized as a duplicate)
Object is a simple object of strings at the moment.
Private Class CommissionStatement
Property Provider As String
Property Firm As String
Property Source As String
Property Media As String
Property Total As String
Property Received As String
End Class
I have a list of all CommissionStatments as follows:
Dim fileLocation As String = importText.Text
Dim csvText As String = My.Computer.FileSystem.ReadAllText(fileLocation).Replace(", ", " ")
Dim providerString As String = ""
Dim allStatements = New List(Of CommissionStatement)
Dim countIndex As Integer = 0, maxIndex As Integer = csvText.Split(vbLf).Length
For Each line As String In csvText.Split(vbLf)
'' Remove the top row
countIndex += 1
If countIndex = 1 Then
Continue For
End If
statementProgress.Value = ((countIndex / maxIndex) * 100)
'' Build the New commissionStatement object and add it to the allStatements list
If Not line = "" Then
Dim commissionStatement = New CommissionStatement
With commissionStatement
.Provider = line.Split(",")(0)
.Firm = line.Split(",")(1)
.Source = line.Split(",")(2)
.Media = line.Split(",")(3)
.Total = line.Split(",")(4)
End With
providerString &= commissionStatement.Provider & ","
allStatements.Add(commissionStatement)
End If
Next
First post on StackOverflow so sorry if its not very clear! The duplicate list needs to also be a list of CommissionStatements which contain the duplicates from the allStatements list based on Firm Provider and Total

Your best bet is to use a lambda expression. The function below should do what you ask.
Private Function GetDuplicateCommisionStatements(tempStatement As CommissionStatement) As List(Of CommissionStatement)
Return allStatements.FindAll(Function(x) x.Firm = tempStatement.Firm And x.Provider = tempStatement.Provider And x.Total = tempStatement.Total)
End Function
And use it like this..
duplicatelist = GetDuplicateCommisionStatements(testCommisionStatement)
using your own object names of course.
Incidentally, you could shorten the sub using the With statement like below
Private Function GetDuplicateCommisionStatements(tempStatement As CommissionStatement) As List(Of CommissionStatement)
With tempStatement
Return allStatements.FindAll(Function(x) x.Firm = .Firm And x.Provider = .Provider And x.Total = .Total)
End With
End Function

Related

Split a string array problems vb.net

I am new to VB.NET and would like to split a string into an array.
I have a string like:
613,710,200,127,127,'{\"js\":{\"\":\"16\",\"43451\":\"16\",\"65815\":\"16\",\"43452\":\"16\",\"41147\":\"16\",\"43449\":\"16\",\"43467\":\"16\",\"1249\":\"16\",\"43462\":\"16\",\"43468\":\"48\",\"43438\":\"64\",\"43439\":\"80\"}}','rca',95,2048000,3,1,'AABBCCDDEEFFGGHHIIJJKKLL=','xx.xx.xx.xx',NULL
I want to split this into a array at ",".
I tried:
Dim variable() As String
Dim stext As String
stext = "mystringhere"
variable = Split(stext, ",")
My problem is the part of
'{\"js\":{\"\":\"16\",\"43451\":\"16\",\"65815\":\"16\",\"43452\":\"16\",\"41147\":\"16\",\"43449\":\"16\",\"43467\":\"16\",\"1249\":\"16\",\"43462\":\"16\",\"43468\":\"48\",\"43438\":\"64\",\"43439\":\"80\"}}',
is split too. I want this to get all together in variable(5). Is this posible?
thank you for help
What you need is a CSV parser in which you can set the field quote character. Unfortunately the TexFieldParser which comes with VB.NET doesn't have that facility. Fortunately, other ones do - here I have used the LumenWorksCsvReader, which is available as a NuGet package *.
Option Strict On
Option Infer On
Imports System.IO
Imports LumenWorks.Framework.IO.Csv
Module Module1
Sub Main()
Dim s = "613,710,200,127,127,'{\""js\"":{\""\"":\""16\"",\""43451\"":\""16\"",\""65815\"":\""16\"",\""43452\"":\""16\"",\""41147\"":\""16\"",\""43449\"":\""16\"",\""43467\"":\""16\"",\""1249\"":\""16\"",\""43462\"":\""16\"",\""43468\"":\""48\"",\""43438\"":\""64\"",\""43439\"":\""80\""}}','rca',95,2048000,3,1,'AABBCCDDEEFFGGHHIIJJKKLL=','xx.xx.xx.xx',NULL"
Using sr As New StringReader(s)
Using csvReader = New CsvReader(sr, delimiter:=","c, quote:="'"c, escape:="\"c, hasHeaders:=False)
Dim nFields = csvReader.FieldCount
While csvReader.ReadNextRecord()
For i = 0 To nFields - 1
Console.WriteLine(csvReader(i))
Next
End While
End Using
End Using
Console.ReadLine()
End Sub
End Module
which outputs
613
710
200
127
127
{"js":{"":"16","43451":"16","65815":"16","43452":"16","41147":"16","43449":"16","43467":"16","1249":"16","43462":"16","43468":"48","43438":"64","43439":"80"}}
rca
95
2048000
3
1
AABBCCDDEEFFGGHHIIJJKKLL=
xx.xx.xx.xx
NULL
Note that the double-quotes are doubled up in the literal string as that is the way to enter a single double-quote in VB.
If you really want the backslashes to remain, remove the escape:="\"c parameter.
If you are reading from a file then use the appropriate StreamReader instead of the StringReader.
Using the above, perhaps you have a Windows Forms program where you wanted to populate a RichTextBox with the data from, say, a text file named "C:\temp\CsvFile.txt" with the content
613,710,200,127,127,'{\""js\"":{\""\"":\""16\"",\""43451\"":\""16\"",\""65815\"":\""16\"",\""43452\"":\""16\"",\""41147\"":\""16\"",\""43449\"":\""16\"",\""43467\"":\""16\"",\""1249\"":\""16\"",\""43462\"":\""16\"",\""43468\"":\""48\"",\""43438\"":\""64\"",\""43439\"":\""80\""}}','rca',95,2048000,3,1,'AABBCCDDEEFFGGHHIIJJKKLL=','xx.xx.xx.xx',NULL
614,710,200,127,127,'{\""js\"":{\""\"":\""16\"",\""43451\"":\""16\"",\""65815\"":\""16\"",\""43452\"":\""16\"",\""41147\"":\""16\"",\""43449\"":\""16\"",\""43467\"":\""16\"",\""1249\"":\""16\"",\""43462\"":\""16\"",\""43468\"":\""48\"",\""43438\"":\""64\"",\""43439\"":\""80\""}}','din',95,2048000,3,1,'AABBCCDDEEFFGGHHIIJJKKLL=','yy.yy.yy.yy',NULL
615,710,200,127,127,'{\""js\"":{\""\"":\""16\"",\""43451\"":\""16\"",\""65815\"":\""16\"",\""43452\"":\""16\"",\""41147\"":\""16\"",\""43449\"":\""16\"",\""43467\"":\""16\"",\""1249\"":\""16\"",\""43462\"":\""16\"",\""43468\"":\""48\"",\""43438\"":\""64\"",\""43439\"":\""80\""}}','jst',95,2048000,3,1,'AABBCCDDEEFFGGHHIIJJKKLL=','zz.zz.zz.zz',NULL
you could use the above to come up with
Imports System.IO
Imports LumenWorks.Framework.IO.Csv
Public Class Form1
Public Class Datum
Property A As Integer
Property B As Integer
Property C As Integer
Property D As Integer
Property E As Integer
Property JsonData As String
Property SocketType As String
Property F As Integer
Property G As Integer
Property H As Integer
Property I As Integer
Property Base64Data As String
Property IpAddy As String
Property J As String
Public Overrides Function ToString() As String
Return $"{A}, {SocketType}, {IpAddy}, {B} ,{C}, {D}, {E}, {F}, {G}, {H}, {I}, {JsonData}, {Base64Data}, {J}"
End Function
End Class
Public Function GetData(filename As String) As List(Of Datum)
Dim data As New List(Of Datum)
Using sr As New StreamReader(filename)
Using csvReader = New CsvReader(sr, hasHeaders:=False, delimiter:=","c, quote:="'"c, escape:="\"c, comment:=Nothing, trimmingOptions:=ValueTrimmingOptions.UnquotedOnly)
Dim nFields = csvReader.FieldCount
If nFields <> 14 Then
Throw New MalformedCsvException("Did not find 14 fields in the file " & filename)
End If
While csvReader.ReadNextRecord()
Dim d As New Datum()
d.A = Integer.Parse(csvReader(0))
d.B = Integer.Parse(csvReader(1))
d.C = Integer.Parse(csvReader(2))
d.D = Integer.Parse(csvReader(3))
d.E = Integer.Parse(csvReader(4))
d.JsonData = csvReader(5)
d.SocketType = csvReader(6)
d.F = Integer.Parse(csvReader(7))
d.G = Integer.Parse(csvReader(8))
d.H = Integer.Parse(csvReader(9))
d.I = Integer.Parse(csvReader(10))
d.Base64Data = csvReader(11)
d.IpAddy = csvReader(12)
d.J = csvReader(13)
data.Add(d)
End While
End Using
End Using
Return data
End Function
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim srcFile = "C:\temp\CsvData.txt"
Dim dat = GetData(srcFile)
For Each d In dat
RichTextBox1.AppendText(d.ToString() & vbCrLf)
Next
End Sub
End Class
It might be necessary to perform more checks on the data when trying to parse it. Note that I made a function for the .ToString() method of the Datum class and put the properties in a different order just to demonstrate its use.
* Tools -> NuGet Package Manager -> Manage NuGet Packages for Solution... Choose the "Browse" tab -> type in LumenWorksCsvReader -> select the one by Sébastien Lorion et al., -> tick your project name in the pane to the right -> click Install.
I am new to VB.NET and would like to split a string into an array.
...
variable = Split(stext,",")
Instead of
variable = Split(stext,",")
use
variable = stext.split(",")
If you want to get a bit more complicated on your split you would create an array of char data as such
dim data(3) as char
data(0) = ","c
data(1) = vbcrlf
data(2) = chr(34)
data(3) = vbtab
... and so on
variable = stext.split(data)

Split() doesn't work properly

well I'm doing a computing assessment and well I've ran into an issue with splitting a string. For some reason when the string splits the array stores the whole thing in Variable(0). The error that occurs is when it tries to assign TicketID(Index) a value, it says that the array is out of bound.
Here's the code:
Private Sub ReadInformation(ByRef TicketID() As String, CustomerID() As String, PurchaseMethod() As Char, NumberOfTickets() As Integer, FileName As String)
Dim Line, TextArray(3) As String
Dim Index As Integer
FileOpen(1, FileName, OpenMode.Input)
For Index = 0 To 499
Input(1, Line)
TextArray = Line.Split(",")
CustomerID(Index) = TextArray(0)
TicketID(Index) = TextArray(1)
NumberOfTickets(Index) = TextArray(2)
PurchaseMethod(Index) = TextArray(3)
MessageBox.Show(CustomerID(Index))
Next
FileClose()
End Sub
Here's the first 10 lines of the TextFile I'm trying to read:
C001,F3,10,S
C002,F3,2,O
C003,F3,3,S
C004,W2,9,S
C005,T3,10,S
C006,F3,2,S
C007,W1,3,O
C008,W3,1,O
C009,T2,2,S
C010,F2,9,O
Here's the Error Message I receive:
Error Message
I would use some Lists instead of arrays. In this way you don't have to worry about length of the arrays or if there are fewer lines than 500. Of course, using the more advanced NET Framework methods of the File.IO namespace is a must
Private Sub ReadInformation(TicketID As List(Of String), _
CustomerID As List(Of String), _
PurchaseMethod As List(Of Char), _
NumberOfTickets As List(Of Integer), _
FileName As String)
for each line in File.ReadLines(FileName)
Dim TextArray = Line.Split(","c)
if TextArray.Length > 3 Then
CustomerID.Add(TextArray(0))
TicketID.Add(TextArray(1))
' This line works just because you have Option Strict Off
' It should be changed as soon as possible
NumberOfTickets.Add(TextArray(2))
PurchaseMethod.Add(TextArray(3))
End If
Next
End Sub
You can call this version of your code declaring the 4 lists
Dim TicketID = New List(Of String)()
Dim CustomerID = New List(Of String)()
Dim PurchaseMethod = New List(Of Char)()
Dim NumberOfTickets = New List(Of Integer)()
ReadInformation(TicketID, CustomerID, PurchaseMethod, NumberOfTickets, FileName)
Another approach more Object Oriented is to create a class that represent a line of your data. Inside the loop you create instances of that class and add the instance to a single List
Public Class CustomerData
Public Property TicketID As String
Public Property CustomerID As String
Public Property NumberOfTickets As Integer
Public Property PurchaseMethod As Char
End Class
Now the loop becomes
Private Function ReadInformation(FileName As String) as List(Of CustomerData)
Dim custData = New List(Of CustomerData)()
For Each line in File.ReadLines(FileName)
Dim TextArray = Line.Split(","c)
if TextArray.Length > 3 Then
Dim data = new CustomerData()
data.CustomerID = TextArray(0)
data.TicketID = TextArray(1)
data.NumberOfTickets = TextArray(2)
data.PurchaseMethod = TextArray(3)
custData.Add(data)
End If
Next
return custData
End Function
This version requires the declaration of just one list
You can call this version of your code passing just the filename and receiving the result fo the function
Dim customers = ReadInformation(FileName)
For Each cust in customers
Console.WriteLine(cust.CustomerID)
...
Next
Or use it as an array
Dim theFirstCustomer = customers[0]
Console.WriteLine(theFirstCustomer.CustomerID)

Length of 2D String list does not return the right value

I have a problem when I use .count in my 2D String list. This is the code:
If File.Exists(fullPath) = True Then
Dim readText() As String = File.ReadAllLines(fullPath)
Dim s As String
accountCounter = 0
For Each s In readText
accountList.Add(New List(Of String))
accountList.Add(New List(Of String))
accountList.Add(New List(Of String))
accountList(accountCounter).Add(s.Split(",")(0))
accountList(accountCounter).Add(s.Split(",")(1))
accountList(accountCounter).Add(s.Split(",")(2))
accountCounter += 1
Next
print_logs(accountList.count)
End If
The result is this:
{{name,email,password},{name2,email2,password2},{name3,email3,password3},{name4,email4,password4}}
beacuse in the file there are the following lines:
name,email,password
name2,email2,password2
name3,email3,password3
name4,email4,password4
But data is not the problem, the real problem is the Count method, it returns (12). I think that it returns 4 * 3 result, because if I add this in the code:
print_logs(accountList(0).Count)
it correctly returns 3.
So, how can I just return 4?
In this code you create three new rows everytime you do an iteration... If there are four lines in your text files then you will create twelve...
Do this instead :
If File.Exists(fullPath) = True Then
Dim readText() As String = File.ReadAllLines(fullPath)
Dim s As String
accountCounter = 0
For Each s In readText
accountList.Add(New List(Of String))
accountList(accountCounter).Add(s.Split(",")(0))
accountList(accountCounter).Add(s.Split(",")(1))
accountList(accountCounter).Add(s.Split(",")(2))
accountCounter += 1
Next
print_logs(accountList.count)
End If
And if you want to make it even better :
If File.Exists(fullPath) = True Then
Dim readText() As String = File.ReadAllLines(fullPath)
For Each s As String In readText
Dim newList = New List(Of String)
newList.Add(s.Split(",")(0))
newList.Add(s.Split(",")(1))
newList.Add(s.Split(",")(2))
accountList.Add(newList)
Next
print_logs(accountList.count)
End If

Splitting file into parts for dictionary use

I have an uploaded file, displaying
Han 33.3
Han 5.66
Han 8.3
Chewbacca 99.4
Chewbacca 100.3
Chewbacca 98.1
I need to make an average for each han and Chewbacca using a dictionary, but first I have to split the list in order to do that. How do I split them for this purpose.
I'm making the assumption that the whitespace between the name and quantity is a single space. I'm also calling your values "scores" and Han and Chewbacca "players" for my example solution. The result splits the line into individual key-value pairs; each key represents the list of values found; it:
Imports System.IO
Dim pathToYourFile As String = "Path to your file with file name"
Dim f As StreamReader = File.OpenText(pathToYourFile)
Dim fileText As String = f.ReadToEnd
f.Close()
Dim singleSpace As String = " " 'single space between quotes
Dim playerScores As New SortedDictionary(Of String, List(Of Double))
Dim lines() As String = fileText.Split(vbCrLf.ToCharArray())
For i As Integer = 0 To lines.Length - 1
Dim scoreLine() As String = lines(i).Split(" ".ToCharArray())
Dim playerName As String = scoreLine(0)
Dim score As Double = Double.Parse(scoreLine(1))
If playerScores.ContainsKey(playerName) Then
playerScores(playerName).Add(score)
Else
Dim list As New List(Of Double)
list.Add(score)
playerScores.Add(playerName, list)
End If
Next i
Try this:
Public Class DataClass
Public Property Name() As String
Public Property Value() As Decimal
End Class
' ...
Public Shared Function GetAverageDict(lines As IEnumerable(Of String)) _
As Dictionary(Of String, Decimal)
Dim lst As New List(Of DataClass)()
For Each line As String In lines
Dim lastSpaceIndex = line.LastIndexOf(" ")
If (lastSpaceIndex >= 0) Then
Dim name = line.Substring(0, lastSpaceIndex).Trim()
Dim value = Decimal.Parse(line.Substring(lastSpaceIndex + 1), _
System.Globalization.CultureInfo.InvariantCulture)
lst.Add(New DataClass() With { .Name = name, .Value = value })
End If
Next
Dim averages = From g In From x In lst _
Group x By key = x.Name _
Select New With { .Name = key, _
.Average = Group.Average(Function(y) y.Value) }
Return averages.ToDictionary(Function(x) x.Name, Function(y) y.Average)
End Function
The function first gets the parts from each line. It assumes that the last space is the separator between the parts. This way, the name can also contain space characters.
The data is collected in a list that is afterwards grouped by the name. By calling ToDictionary the result of the grouping operation is converted in a dictionary.
Call the function like this:
Dim dict = GetAverageDict(System.IO.File.GetLines(filePath))

VB.net Dictionary loop

Hey all i am in need of some help looping thru my Dictionary list. I can not seem to find the correct syntax in order to do so.
Here is my code:
Dim all = New Dictionary(Of String, Object)()
Dim info = New Dictionary(Of String, Object)()
Dim theShows As String = String.Empty
info!Logo = channel.SelectSingleNode(".//img").Attributes("src").Value
info!Channel = .SelectSingleNode("channel.//span[#class='channel']").ChildNodes(1).ChildNodes(0).InnerText
info!Station = .SelectSingleNode("channel.//span[#class='channel']").ChildNodes(1).ChildNodes(2).InnerText
info!Shows = From tag In channel.SelectNodes(".//a[#class='thickbox']")
Select New With {channel.Show = tag.Attributes("title").Value, channel.Link = tag.Attributes("href").Value}
all.Add(info!Station, info.Item("Shows"))
theShows = all.Item("Shows") '<--Doesnt work...
I just want to extract whatever is in "Shows" from the all dictionary.
Your code,
all.Add(info!Station, info.Item("Shows"))
theShows = all.Item("Shows")
The value of info!Station is being used as the KEY value in the all dictionary. Then you attempt to access the value using the constant string "Shows". I'm not sure what your intention was but
theShows = all.Item(info!Station)
should return the value of Shows that was stored using the Key info!Station.
If you want the list of shows as a string, you can do this,
Dim Shows as String = ""
For Each item in theShows
Shows &= item.Show & vbNewLine
Next
You can loop like this
For Each pair As KeyValuePair(Of String, String) In dict
MsgBox(pair.Key & " - " & pair.Value)
Next
source : VB.Net Dictionary
Winston