Remove Duplicate Objects from list in VB.Net - vb.net

I am trying to remove duplicate object from a list based on the companyID.
How do I integrate through a list and remove the object based on a companyID.
While reader.Read()
companys.Add(New CompanySearch)
companys(companys.Count - 1).StartDate = reader("StartDate").ToString & " (" & count & ")"
companys(companys.Count - 1).CompanyID = reader("company").ToString
companys(companys.Count - 1).Origin = reader("Origin").ToString
companys(companys.Count - 1).OriginName = reader("OriginName").ToString
companys(companys.Count - 1).Status = reader("status").ToString
companys(companys.Count - 1).StatusName = reader("statusname").ToString
companys(companys.Count - 1).Status = reader("status").ToString
companys(companys.Count - 1).FullLegalBusinessName = reader("fullLegalBusinessName")
companys(companys.Count - 1).AmountRequestedText = reader("amountRequestedText")
companys(companys.Count - 1).HowSoonNeededText = reader("howSoonNeededText")
companys(companys.Count - 1).QueueID = reader("QueueID")
companys(companys.Count - 1).Company = reader("Company")
End While
For counter As Integer = 0 To companys.Count
counter += 1
If i <> CInt(companys(companys.Count - 1).CompanyID) Then
i = CInt(companys(companys.Count - 1).CompanyID)
Else
companys.Remove()
End If
Next

Don't add them in the first place. Use either aDictionary (if you will look them up by ID later) or a HashSet (if you won't) to check before adding to the results. Here's the HashSet example:
Dim companyIDs As New HashSet(Of String)()
While reader.Read()
If Not companyIDs.Contains(reader("company").ToString()) Then
companys.Add(New CompanySearch() With {
.StartDate = reader("StartDate").ToString() & " (" & count & ")",
.CompanyID = reader("company").ToString(),
.Origin = reader("Origin").ToString(),
.OriginName = reader("OriginName").ToString(),
.Status = reader("status").ToString(),
.StatusName = reader("statusname").ToString(),
.Status = reader("status").ToString(),
.FullLegalBusinessName = reader("fullLegalBusinessName"),
.AmountRequestedText = reader("amountRequestedText"),
.HowSoonNeededText = reader("howSoonNeededText"),
.QueueID = reader("QueueID"),
.Company = reader("Company"),
})
End If
companyIDs.Add(reader("company").ToString())
End While
I also noticed that both the .Company and .CompanyID properties in this object are populated from the company column in the reader. Is this intentional, or do you mean to look at a different column for .CompanyID?
Additionally, while I understand your existing search SQL already considers these company rows as distinct, you should probably go back to the drawing board there and rethink the SQL, so that you truly do get distinct records. Perhaps use a nested query or CTE to first find a projection of CompanyID values that match your query, and then join back to your company table to get the details for each company with an ID included in those initial results. If that's not possible, you should consider what it is that makes the rows different, because I promise you that some column IS different, and if you just cull one record or the other you're potentially showing the user bad data from the wrong row.

Use this :
Dim distinctCompanys = companys.GroupBy(Function(x) x.CompanyID).Select(Function(y) y.First())

You can easily filter the collection with LINQ:
Dim companies = companys.Distinct(Function(c) c.CompanyID).ToList
Or use Dictionary(Of String, CompanySearch) instead, for Example:
Dim companies As Dictionary(Of String, CompanySearch)
While reader.Read()
Dim companyID = reader("company").ToString
companies(companyID) = New CompanySearch() With {
.StartDate = reader("StartDate").ToString & " (" & count & ")",
.CompanyID = companyID,
.Origin = reader("Origin").ToString,
.OriginName = reader("OriginName").ToString,
.Status = reader("status").ToString,
.StatusName = reader("statusname").ToString,
.Status = reader("status").ToString,
.FullLegalBusinessName = reader("fullLegalBusinessName"),
.AmountRequestedText = reader("amountRequestedText"),
.HowSoonNeededText = reader("howSoonNeededText"),
.QueueID = reader("QueueID"),
.Company = reader("Company")
}
End While
But I recommend grouping instead, so that you can check for duplicates after:
Dim companiesLookup = companys.ToLookup(Function(c) c.CompanyID)
Dim duplicates = companiesLookup.Where(Function(c) c.Count > 1).ToList

Related

How to update a single column in single row in a datatable using LinQ

I'm working on a project that uses a Master database (a datatable) to drive downloading of files from an FTP server. During the process, I create a local database (another datatable) for each client in the master database. Here is the code that I use to create a client database:
Console.WriteLine(" Building Client Database")
Clientdatabase = New DataTable
Clientdatabase = DataBaseTable.Clone
Dim RowList = (From row In DataBaseTable.AsEnumerable() Where (row.Field(Of String)("co") = CompanyID))
For Each RowItem In RowList
Clientdatabase.ImportRow(RowItem)
Next
This code is working as expected; the Clientdatabase is accurate in what it contains. My problem is updating a field in the Clientdatabase. From within a loop, I'm getting each record that contains the full path and file name of the target file on the FTP server, then I attempt to update the clientdatabase with the local path and I can't get it to work. From within the loop this code is performing the logic and downloading the file:
Dim FileList = (From row In DataBaseTable.AsEnumerable().Select(Function(x) New With {
Key .co = x.Field(Of String)("co"),
Key .path = x.Field(Of String)("path"),
Key .OriginalFileName = x.Field(Of String)("OriginalFileName"),
Key .DocumentID = x.Field(Of String)("DocumentID")
}).Where(Function(s) s.co = CompanyID).ToList)
Console.Write(" Downloading Files : ")
Dim CursorArray() As String = Split("\,|,/,-", ",")
Dim FileCounter As Integer = 1
For Each CompanyFile In FileList
Dim ThisFile As String = CompanyFile.path.Replace("\", "/")
Dim Results As TransferOperationResult = DownloadFromPath(ThisFile, DestinPath)
Dim TmpParts() As String = Split(ThisFile, "/")
Dim LocalName As String = TmpParts(UBound(TmpParts))
If Results.IsSuccess Then
UpdateCDBPath(CompanyID, CompanyFile.DocumentID, LocalName)
ReportData &= CompanyID & ",Success," & CompanyFile.OriginalFileName & "," & ThisFile & vbCrLf
Else
ReportData &= CompanyID & ",Failed," & CompanyFile.OriginalFileName & "," & ThisFile & vbCrLf
End If
FileCounter += 1
Console.Write(CursorArray(FileCounter Mod 4) & Chr(8))
Next
The call to update (UpdateCDBPath) contains the following code:
Sub UpdateCDBPath(ByVal CompanyID As String, ByVal DocumentID As String, TargetValue As String)
Dim result = (From row In Clientdatabase.AsEnumerable().Select(Function(x) New With {
Key .DocumentID = x.Field(Of String)("DocumentID"),
Key .Co = x.Field(Of String)("co")
}).Where(Function(s) s.DocumentID = DocumentID And s.Co = CompanyID ) Select Clientdatabase)
For Each ItemRow In result
ItemRow.Rows(0).Item("Path") = TargetValue
Next
End Sub
The problem is within this code block. DocumentID is a GUID that is unique within the client that uniquely identifies the document, TargetValue is the replacement path (local path to the file). If I'm understanding the Linq correctly (and I'm not), it should only return the rows I'm interested in (1), thus how I'm setting the new path. I've tried countless examples from various examples and can't make it work. When I check the Clientdatabase, none of the path fields are updated. I've also confirmed that after saving the Clientdatabase locally, the fields are still the same. Can some one please point me in the right direction, tell me where I went wrong or something so I can solve this. I eventually will need to update other fields in the clientdatabase; this is the first one. Thanks in advance for any and all help that might come my way!
The linq for result is returning a EnumerableRowCollection(Of System.Data.DataTable), meaning that it contains a list of rows that are the entire Clientdatabase.
As each row contains the entire DataRowCollection, ItemRow.Rows(0).Item("Path") = TargetValue will update only the first row in Clientdatabase as many times as there are records in results.
Changing the query to return an EnumerableRowCollection(Of System.Data.DataRow) allows each row to be accessed directly in a loop:
Sub UpdateCDBPath(ByVal CompanyID As String, ByVal DocumentID As String, TargetValue As String)
Dim result = From row In Clientdatabase.AsEnumerable()
Where row.Field(Of String)("DocumentID") = DocumentID AndAlso
row.Field(Of String)("co") = CompanyID
Select row
For Each ItemRow In result
ItemRow.Item("Path") = TargetValue
Next
End Sub

Dynamically Create Collection/Array in VBA

I'm struggling with this, I'm doing some stuff in Access with VBA and I need to dynamically create N collections/lists/arrays of records and add them to my dictionary.
//Some pseudo code
Dim dict as object
Set dict = CreateObject("Scripting.Dictionary")
for record in myRecordSetObject
if dict.exists(keyfromrecord)
dict(keyfromrecord) = array.add(record)
else
newarray = [record]
dict.add key:="keyfromrecord" item:=array
If it can't be done I might just do a string of primary keys and grow it as needed, then call string split.
Edit
So I have my records and I need to divide them into subgroups based on a few common fields that they may or may not share. If two records have the same pieces of information in these select fields they're in a subgroup. A subgroup may have 1 - N records.
Instead of getting all possible combinations and filtering my query I want to create a dictionary that defines it's key as a string generated from these fields. If a key exists then there's a member of that subgroup, if it doesn't it's a new subgroup.
The value was going to be an array of records.
Afterwards I was going to go through my dictionary and do stuff with these records.
Field1 Field2 Field3 Field4
Fruit Skinned Sliced Baked
Apples True True True
Bananas True True True
Oranges True False False
Using this example above subgroup would be when Field2,3 and 4 have the same value. (Apples, Bananas) and the other would be (Oranges)
I want a dictionary with Key being
dictionary{
"True-True-True": [Apples, Bananas],
"True-False-True": [Oranges]
}
Not sure if this is what you are after, but this puts a recordset of each combination in at each dictionary key.
Based on your table, it gives keys of
FALSE-FALSE-FALSE-,FALSE-FALSE-TRUE-,FALSE-TRUE-FALSE-,FALSE-TRUE-TRUE-,TRUE-FALSE-FALSE-,TRUE-FALSE-TRUE-,TRUE-TRUE-FALSE-,TRUE-TRUE-TRUE-
where ? dicOutput("TRUE-TRUE-TRUE-").recordcount returns 2 records
and GroupTable("0fruits")("TRUE-TRUE-TRUE-").recordcount the same 2
Hope this helps
Function GroupTable(strTableName As String) As Scripting.Dictionary
Dim strKey As String
Dim diccols As New Scripting.Dictionary
Dim dicOutput As Scripting.Dictionary
Dim dicTruth As Scripting.Dictionary
Dim rst As ADODB.Recordset
Dim rcols As ADODB.Recordset
Set rcols = New ADODB.Recordset
Set rcols = CurrentProject.Connection.OpenSchema(adSchemaColumns, Array(Empty, Empty, strTableName, Empty))
While Not rcols.EOF
If rcols.Fields("COLUMN_NAME").Value <> "Fruit" Then
diccols.Add CStr(diccols.Count), rcols.Fields("COLUMN_NAME").Value
End If
rcols.MoveNext
Wend
Set dicTruth = maketruthtable(2 ^ diccols.Count - 1, diccols.Count)
Set dicOutput = New Scripting.Dictionary
For l = 0 To dicTruth.Count - 1
strSQL = "select [fruit] from [" & strTableName & "] where " & Join(diccols.Items(), "&") & "='" & dicTruth.Items()(l) & "'"
Set rst = New ADODB.Recordset
rst.Open strSQL, CurrentProject.Connection, adOpenStatic
dicOutput.Add Replace(Replace(dicTruth.Items()(l), "-1", "TRUE-"), "0", "FALSE-"), rst
Next l
Set GroupTable = dicOutput
End Function
Function maketruthtable(intMax As Integer, intOptions As Integer) As Scripting.Dictionary
Dim d As New Scripting.Dictionary
Dim j As Integer
For j = 0 To intMax
d.Add CStr(j), Replace(Right(DecToBin(j), intOptions), "1", "-1")
Next j
Set maketruthtable = d
End Function
Public Function DecToBin(ByVal lngDec As Long) As String
Const MAXLEN = 5
Dim strBin As String
Dim n As Long
If lngDec < 0 Then
strBin = "1"
Else
strBin = "0"
End If
For n = MAXLEN To 0 Step -1
If lngDec And (2 ^ n) Then
strBin = strBin & "1"
Else
strBin = strBin & "0"
End If
Next
DecToBin = strBin
End Function
EDIT
Another solution would be to use SQL to do it, so if you have a table with just TRUE in 1 row and False in another, called tblLogicOptions for example, like so
Then you can use the following SQL on a table called 0Fruits
Using the following SQL
select LOGICTABLE.*,Data.Fruit FROM (select ((x1.a) & (x2.a) & (x3.a)) as Logic from tblLogicOptions as x1, tblLogicOptions as x2, tblLogicOptions as x3) AS LOGICTABLE
LEFT JOIN
(SELECT F1.Fruit, [skinned] & [sliced] & [baked] AS LogicCompare
FROM 0fruits as F1) AS DATA ON LOGICTABLE.Logic=DATA.LogicCompare
Which gives the results
Looping through this to build the dictionary, or even using the resultant recordset perhaps, would be easier I think.
You could use the Redim keyword to change the array size

Multiple Dynamic Order By in Linq to Entity Framework

Dim receipts As IQueryable(Of ReceiptEntity) = db.Receipts
'code to filter removed for brevity
Dim sorts() As String = SortExpression.Split(";")
For Each sort As String In sorts
Dim sortParts() As String = sort.Split(" ")
If sortParts(1).ToLower = "true" Then
receipts = receipts.OrderBy(Of ReceiptEntity)(sortParts(0).ToString(), SortDirection.Ascending)
Else
receipts = receipts.OrderBy(Of ReceiptEntity)(sortParts(0).ToString(), SortDirection.Descending)
End If
Next
SortExpression comes in like "field1 true;field2 false;field3 true"
What I want to happen is for the query to have multiple order by fields, what is happening is that only the last order by is applied. What am I doing wrong here?
Here is what the working result looks like:
Dim receipts As IOrderedQueryable(Of ReceiptEntity) = db.Receipts.Include(Function(r) r.LineItems).Include(Function(r) r.Payments)
Dim sorts() As String = SortExpression.Split(";")
Dim sortParts() As String
sortParts = sorts(0).Split(" ")
If sortParts(1).ToLower = "true" Then
receipts = receipts.OrderBy(sortParts(0).ToString())
Else
receipts = receipts.OrderByDescending(sortParts(0).ToString())
End If
For Each sort As String In sorts.Skip(1)
sortParts = sort.Split(" ")
If sortParts(1).ToLower = "true" Then
receipts = receipts.ThenBy(sortParts(0).ToString())
Else
receipts = receipts.ThenByDescending(sortParts(0).ToString())
End If
Next
You have to use ThenBy instead of OrderBy for the second and all subsequent sort operations.

mongodb query to find field - vb.net

How would I structure a query for mongodB to make a 'stored procedure' or make the request to select an id which is marked active and then delete that field immediately or mark it as inactive; whichever one has the better performance. Here is the collection structure:
db = server.GetDatabase("test")
siteCollection = db("test")
collection = db.GetCollection(Of BsonDocument)("siteids")
Dim book As BsonDocument = New BsonDocument() _
.Add("siteid", BsonValue.Create(BsonType.String)) _
.Add("active", BsonValue.Create(BsonType.String))
collection.Insert(book)
I found the java version and not sure if this will work and what is .net syntax
db.things.find( { x : 4 } , { j : 1 } )
This apparantly finds records where x = 4 but only return where j = 1 so I want one siteid where active = 'N'
Thanks; here is what I have come up with thus far:
' Dim squery = Query.EQ("active", "Y")
Dim squery = Query.EQ("active", "Y")
Dim ssort = SortBy.Null
Dim uupdate = Update.[Set]("active", "N")
Dim result = collection.FindAndModify(squery, ssort, uupdate)
' Dim dresult = collection.FindAs(Of BsonDocument)(squery)
Dim newSiteId As String = dresult.Count
As you can see the first line commented out I thought a simple select would be implemented but that comes back null. Then with the second last statement commented out that too returned value Null.

Append text to existing row in datatable

I'm trying to make a calendar in vb.net and I have come across this problem. I want to append some text into an existing datatable row. When I watch my debugger it says:"In order to evaluate an indexed property, the property must be qualified and the arguments must be explicitly supplied by the user.".
Dim aantalRijen As Integer = 1
For x = 0 To 6
Dim dttopdrachten As New DataTable
dttopdrachten = opdrachtendao.getOpdrachtenByDate(Today.AddDays(x))
If dttopdrachten.Rows.Count > aantalRijen Then
aantalRijen = dttopdrachten.Rows.Count
End If
Next
For z = 0 To aantalRijen - 1
Dim r As DataRow
r = dttAgenda.NewRow()
dttAgenda.Rows.InsertAt(r, z)
Next
For i = 0 To 6
Dim aantalItems As Integer = 0
Dim dttopdrachten As New DataTable
dttopdrachten = opdrachtendao.getOpdrachtenByDate(Today.AddDays(i))
aantalItems = dttopdrachten.Rows.Count
For j = 0 To aantalItems - 1
Dim info As String = dttopdrachten.Rows(j).Item(0).ToString & vbCrLf & dttopdrachten.Rows(j).Item(2).ToString & vbCrLf & dttopdrachten.Rows(j).Item(3).ToString & vbCrLf & dttopdrachten.Rows(j).Item(4).ToString & vbCrLf & dttopdrachten.Rows(j).Item(5).ToString & vbCrLf & dttopdrachten.Rows(j).Item(6).ToString
dttAgenda.Rows(j).Item(i) = info
Next
Next
dgvAgenda.DataSource = dttAgenda
In the code above, I first count how many rows I have to make. Afterwards I add the amount of rows to the datatable (columns are added before). Until here it works, but then when I keep debugging I get the error. I tried googling but nothing could help me so far.
Seem problem has been solved without changing anything. So if someone want to make a calendar. Here's the solution ;)