VB.NET LINQ - Matching Hierarchical Data to Flat Data - vb.net

I have 2 representations of similar data from 2 different systems and I need to match each entity in one system with the entities in the other.
System A is Hierarchical, represented as a Dictionary(Of String, List(Of CategoryA)) looking something like:
- "Organization 1"
- { Name = "Cat1", Id = 1}
- { Name = "Cat2", Id = 2}
- { Name = "Cat3", Id = 3}
- "Organization 2"
- { Name = "Cat1", Id = 4}
- { Name = "Cat3", Id = 5}
- { Name = "Cat4", Id = 6}
- "Organization 3"
- { Name = "Cat1", Id = 7}
- { Name = "Cat2", Id = 8}
- { Name = "Cat3", Id = 9}
- { Name = "Cat4", Id = 10}
System B is Flattened, represented as a List(Of CategoryB) looking something like:
- { Org = "Organization 1", Name = "Cat1", Id = 100 }
- { Org = "Organization 1", Name = "Cat2", Id = 101 }
- { Org = "Organization 1", Name = "Cat3", Id = 102 }
- { Org = "Organization 2", Name = "Cat1", Id = 103 }
- { Org = "Organization 2", Name = "Cat2", Id = 104 }
- { Org = "Organization 2", Name = "Cat4", Id = 105 }
- { Org = "Organization 4", Name = "Cat1", Id = 106 }
- { Org = "Organization 4", Name = "Cat2", Id = 107 }
- { Org = "Organization 4", Name = "Cat3", Id = 108 }
- { Org = "Organization 4", Name = "Cat4", Id = 109 }
And basically what I need to do is to outer join the hierarchical data to the flattened data on Organization name (Dictionary.Key to CategoryB.Org) and Category Name (CategoryA.Name to CategoryB.Name), leaving me with a Dictionary(Of String, IEnumerable(Of Tuple(Of CategoryA, CategoryB))) or something that looks something like:
- "Organization 1"
- { Name = "Cat1", Id = 1}, { Org = "Organization 1", Name = "Cat1", Id = 100 }
- { Name = "Cat2", Id = 2}, { Org = "Organization 1", Name = "Cat2", Id = 101 }
- { Name = "Cat3", Id = 3}, { Org = "Organization 1", Name = "Cat3", Id = 102 }
- "Organization 2"
- { Name = "Cat1", Id = 4}, { Org = "Organization 2", Name = "Cat1", Id = 103 }
- { Name = "Cat3", Id = 5}, null
- { Name = "Cat4", Id = 6}, { Org = "Organization 2", Name = "Cat4", Id = 105 }
- "Organization 3"
- { Name = "Cat1", Id = 7}, null
- { Name = "Cat2", Id = 8}, null
- { Name = "Cat3", Id = 9}, null
- { Name = "Cat4", Id = 10}, null
I don't have access to the CategoryA object to be able to apply an Organization property to it, or I would do it and make this easier on myself. I just can't figure out how to join on the Dictionary key and a property of one of the items in its value, and end up with anything useful. The most successful implementation I've created involves a For Each loop first, and a LINQ query inside:
Given:
catA = Dictionary(Of String, List(Of CategoryA))
catB = List(Of CategoryB)
Dim result As New Dictionary(Of String, List(Of Tuple(Of CategoryA, CategoryB)))
For Each kvp As KeyValuePair(Of String, List(Of CategoryA)) In catA
Dim orgName As String = kvp.Key
If Not result.ContainsKey(orgName) Then
result.Add(orgName, New List(Of Tuple(Of CategoryA, CategoryB)))
End If
Dim orgCategories As IEnumerable(Of CategoryB) =
From cat In catB Where cat.Org = orgName
Dim tmpResult As IEnumerable(Of Tuple(Of CategoryA, CategoryB)) =
From cat_a In kvp.Value
Group Join cat_b In orgCategories
On cat_a.Name Equals cat_b.Name
Into matchedCats = Group
From cat In matchedCats.DefaultIfEmpty
Select matches = Tuple.Create(cat_a, cat)
result(orgName).AddRange(tmpResult)
Next
It works alright, but I'd like it to be in the same statement.

Well, this kinda works but id stick with your for loop!
Public Class CategoryA
Public Property Name As String
Public Property Id As Integer
End Class
Public Class CategoryB
Public Property Org As String
Public Property Name As String
Public Property Id As Integer
End Class
Private SystemA As New Dictionary(Of String, List(Of CategoryA))
Private SystemB As New List(Of CategoryB)
Sub Main()
SystemA.Add("Org1", New List(Of CategoryA) From {New CategoryA() With {.Id = 1, .Name = "Cat1"},
New CategoryA() With {.Id = 2, .Name = "Cat2"},
New CategoryA() With {.Id = 3, .Name = "Cat3"}})
SystemA.Add("Org2", New List(Of CategoryA) From {New CategoryA() With {.Id = 4, .Name = "Cat1"},
New CategoryA() With {.Id = 5, .Name = "Cat2"},
New CategoryA() With {.Id = 6, .Name = "Cat3"}})
SystemA.Add("Org3", New List(Of CategoryA) From {New CategoryA() With {.Id = 7, .Name = "Cat1"},
New CategoryA() With {.Id = 8, .Name = "Cat2"},
New CategoryA() With {.Id = 9, .Name = "Cat3"},
New CategoryA() With {.Id = 10, .Name = "Cat4"}})
SystemB.Add(New CategoryB() With {.Org = "Org1", .Name = "Cat1", .Id = 100})
SystemB.Add(New CategoryB() With {.Org = "Org1", .Name = "Cat2", .Id = 101})
SystemB.Add(New CategoryB() With {.Org = "Org1", .Name = "Cat3", .Id = 102})
SystemB.Add(New CategoryB() With {.Org = "Org2", .Name = "Cat1", .Id = 103})
SystemB.Add(New CategoryB() With {.Org = "Org2", .Name = "Cat2", .Id = 104})
SystemB.Add(New CategoryB() With {.Org = "Org2", .Name = "Cat4", .Id = 105})
SystemB.Add(New CategoryB() With {.Org = "Org4", .Name = "Cat1", .Id = 106})
SystemB.Add(New CategoryB() With {.Org = "Org4", .Name = "Cat2", .Id = 107})
SystemB.Add(New CategoryB() With {.Org = "Org4", .Name = "Cat3", .Id = 108})
SystemB.Add(New CategoryB() With {.Org = "Org4", .Name = "Cat4", .Id = 109})
Dim AllOrgs = SystemA.Keys.Union(SystemB.Select(Function(b) b.Org).Distinct)
Dim BothCats2 = From org In AllOrgs
Let CatAList = If(Not SystemA.ContainsKey(org), New List(Of CategoryA), From cat In SystemA(org))
Let CatBList = (From cat In SystemB Where cat.Org = org).ToList
Let AllCatNames = (From cat In CatAList Select cat.Name Distinct).Union(From cat In CatBList Select cat.Name Distinct)
Let BothCats = (From cat In AllCatNames
From A In CatAList.Where(Function(CatA) CatA.Name = cat).DefaultIfEmpty
From B In CatBList.Where(Function(CatB) CatB.Name = cat).DefaultIfEmpty)
Select org, BothCats
End Sub

Related

Status Bar in for loop

I was trying to create a status bar in my for loop, but the status remains at 0%. I'm not sure what the issue could be.
n <- length(unique(mu_ut$AnimalID))
pb <- txtProgressBar(min = 0, max = n, style = 3)
for(i in unique(mu_ut$AnimalID)){
AnID <- paste("AnID", i, sep = ".")
assign(AnID, mu_ut[mu_ut$AnimalID == i,])
dbWriteTable(conn = db, name = AnID, value = mu_ut[mu_ut$AnimalID == i,],
field.types = c(DateAndTime = "DATETIME",
AnimalID = "CHAR",
Species = "CHAR",
Sex = "CHAR",
CurrentCohort = "CHAR",
BirthYear = "DATE",
CaptureUnit = "CHAR",
CaptureSubunit = "CHAR",
CaptureArea = "CHAR"), overwrite = TRUE)
Sys.sleep(1)
setTxtProgressBar(pb, i)
}
Is it a problem with the "i" argument in the setTxtProgressBar function?
Thank you in advance.
The issue is that setTxtProgressBar needs its value to be in the sequence min=0 to max=n, as set in your txtProgressBar. Since i is really one of the unique values within mu_ut$AnimalID, even if it is an integer, it is not clear that it is also in the range 0:n.
Try this.
ids <- unique(mu_ut$AnimalID)
pb <- txtProgressBar(min = 0, max = length(ids), style = 3)
for (ind in seq_along(ids)) {
id <- ids[ind]
AnID <- paste("AnID", id, sep = ".")
assign(AnID, mu_ut[mu_ut$AnimalID == id,])
dbWriteTable(conn = db, name = AnID, value = mu_ut[mu_ut$AnimalID == id,],
field.types = c(DateAndTime = "DATETIME",
AnimalID = "CHAR",
Species = "CHAR",
Sex = "CHAR",
CurrentCohort = "CHAR",
BirthYear = "DATE",
CaptureUnit = "CHAR",
CaptureSubunit = "CHAR",
CaptureArea = "CHAR"), overwrite = TRUE)
Sys.sleep(1)
setTxtProgressBar(pb, ind)
}
(This is very similar to #CareyCaginalp's comment.)

Find rank of user by LINQ

enter image description here
How can I get Rank of a user in table by LINQ ? I use VB.NET CODE
if we have these fields in our table
[id , name , score]
after order by score of users how we can get the ranking ( row number ) one of them in our query ?
Thank you man . I used the bellow code and it's work fine for me:
Dim query = (From P In DB.tblUserScores Order By P.Score
Descending Select P).AsEnumerable()
Dim Rank As Integer = 1
For Each userObj As tblUserScore In query
If userObj.DeviceId = DeviceID Then Exit For
Rank += 1
Next
Here's how:
Dim records = _
{ _
New With { Key .ID = 1, Key .Name = "A1", Key .Score = 7 }, _
New With { Key .ID = 2, Key .Name = "A2", Key .Score = 9 }, _
New With { Key .ID = 3, Key .Name = "A3", Key .Score = 2 }, _
New With { Key .ID = 4, Key .Name = "A4", Key .Score = 1 }, _
New With { Key .ID = 5, Key .Name = "A5", Key .Score = 6 }, _
New With { Key .ID = 6, Key .Name = "A6", Key .Score = 4 }, _
New With { Key .ID = 7, Key .Name = "A7", Key .Score = 7 }, _
New With { Key .ID = 8, Key .Name = "A8", Key .Score = 3 }, _
New With { Key .ID = 9, Key .Name = "A9", Key .Score = 5 }, _
New With { Key .ID = 10, Key .Name = "A10", Key .Score = 8 } _
}
Dim query = _
From r In Records _
Order By r.Name Ascending
Order By r.Score Descending
Select r
query.Dump()
Dim rank = _
query _
.Select(Function (x, n) New With { Key .Record = x, Key .Rank = n + 1 }) _
.ToDictionary(Function (x) x.Record.Name, Function (x) x.Rank)
Dim name = "A9"
Console.WriteLine("User " & name & "'s Rank is " & rank(name))
That gives me:
User A9's Rank is 6

How to count group of records using LINQ Query?

I have a database table that have a structure like this:
ID Integer Auto Incresement
Name VarChar
Status VarChar
Sample records:
ID Name Status
1 record 1 Outstanding
2 record 2 Outstanding
3 record 3 Aging
4 record 4 Outstanding
5 record 5 Aging
6 record 6 Outstanding
In the table, there are two main status: "Outstanding" and "Aging". I want to count how many records with status of "Outstanding" and how many records with status of "Aging" available in the table.
This is a sample LINQ Query:
Using DC = DataClassesDataContext.Create()
Dim dataTable = From Count(Outstanding), Count(Aging) In DC.MyTable _
Where item.Status = "Outstanding" OrElse item.Status = "Aging" _
Group By item.Status _
Select item
End Using
The expected result should be:
Outstanding Aging
4 2
Can you help me to design a LINQ to achive the result?
Try this:
Dim MyTable = { _
New With {.ID = 1, .Name = "record 1", .Status = "Outstanding"},
New With {.ID = 2, .Name = "record 2", .Status = "Outstanding"},
New With {.ID = 3, .Name = "record 3", .Status = "Aging"},
New With {.ID = 4, .Name = "record 4", .Status = "Outstanding"},
New With {.ID = 5, .Name = "record 5", .Status = "Aging"},
New With {.ID = 6, .Name = "record 6", .Status = "Outstanding"}
}
Dim dataTable = _
From item In MyTable
Group By Key = item.Status Into Xs = Group
Select New With {.Status = Key, .Count = Xs.Count()}
I get this result:
Try this :-
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public static void Main()
{
var test = new List<Sample>();
test.Add(new Sample{ID=1,Name="record 1",Status="Outstanding"});
test.Add(new Sample{ID=2,Name="record 2",Status="Outstanding"});
test.Add(new Sample{ID=3,Name="record 3",Status="Aging"});
test.Add(new Sample{ID=4,Name="record 4",Status="Outstanding"});
test.Add(new Sample{ID=5,Name="record 5",Status="Outstanding"});
test.Add(new Sample{ID=6,Name="record 6",Status="Aging"});
var result = from row in test
group row by "Count" into g
where g.FirstOrDefault() != null
select new
{
//Status = g.Key,
Outstanding = g.Where(C => C.Status == "Outstanding").Count(),
Aging = g.Where(C => C.Status == "Aging").Count()
};
Console.WriteLine("Outstanding"+" "+"Aging");
foreach(var item in result)
{
Console.WriteLine(" "+item.Outstanding+" "+item.Aging);
}
}
}
public class Sample
{
public int ID {get;set;}
public string Name {get;set;}
public string Status {get; set;}
}
result :-
you can run above sample code using following link - https://dotnetfiddle.net/xC2NXm
suggest improvement :)

Putting my code into a nested loop

Can anyone help me put this code into a nested loop please. It is a code to calculate the lowest score of candidates who have been ranked in order of preferences with 1 being the first preference and 5 being the highest.
Dim min = 0
If Candidate1total > min Then
min = Candidate1total
LabelWINNER.Text = EnterNames.Cand1Name.Text
End If
If Candidate2total < min Then
min = Candidate2total
LabelWINNER.Text = EnterNames.Cand2Name.Text
End If
If Candidate3total < min Then
min = Candidate3total
LabelWINNER.Text = EnterNames.Cand3Name.Text
End If
If Candidate4total < min Then
min = Candidate4total
LabelWINNER.Text = EnterNames.Cand4Name.Text
End If
If candidate5total < min Then
min = candidate5total
LabelWINNER.Text = EnterNames.Cand5Name.Text
End If
Candidate1Total to Candidate5Total are the total scores for the candidates. This is calculated by working out the number of times a user has voted for them. The name that the candidate was given on the ENTERNAMES form is then placed in the labelWINNER if that candidate is lower then the minuim.
I think it would be something like for 0 in candidate1 total, im not 100% certain, hence asking for help.
This is what I would do:
Dim Candidates = _
{ _
New With { .Candidate = EnterNames.Cand1Name.Text, .Total = Candidate1total }, _
New With { .Candidate = EnterNames.Cand2Name.Text, .Total = Candidate2total }, _
New With { .Candidate = EnterNames.Cand3Name.Text, .Total = Candidate3total }, _
New With { .Candidate = EnterNames.Cand4Name.Text, .Total = Candidate4total }, _
New With { .Candidate = EnterNames.Cand5Name.Text, .Total = Candidate5total } _
}
Dim result = _
Candidates _
.OrderBy(Function (c) c.Total) _
.GroupBy(Function (c) c.Total, Function (c) c.Candidate) _
.Select(Function (c) New With { .Candidate = String.Join(", ", c), .Total = c.Key }) _
.First()
So, to see the result, I used this example data:
Dim Candidates = _
{ _
New With { .Candidate = "Mike", .Total = 5 }, _
New With { .Candidate = "Tony", .Total = 2 }, _
New With { .Candidate = "Bill", .Total = 2 }, _
New With { .Candidate = "Carl", .Total = 3 }, _
New With { .Candidate = "Dick", .Total = 4 } _
}
I got this result:
Of course, if it were just one candidate with the lowest total score then there would only be one name in the Candidate field.

How to increment a variable name by 1 in Lua, and then set that as the variable name?

My current code is:
obj1 = object:new{x = math.random(1,92), y = math.random(1,92), roomx = 0, roomy = 0, symbol = "t", name = "Tree"}
obj2 = object:new{x = math.random(1,92), y = math.random(1,92), roomx = 0, roomy = 0, symbol = "t", name = "Tree"}
obj3 = object:new{x = math.random(1,92), y = math.random(1,92), roomx = 0, roomy = 0, symbol = "t", name = "Tree"}
And so on. Since they are all the same thing, I'd like to be able to generate a variable that I can increment the name by one, and then use a for loop to create a lot of them. Is there a way I can do that in Lua? Thanks!
You can use a table to hold the objects and add them using a for loop.
local objects = {}
--> This will create 20 objects
for i=1, 20 do
objects[i] = object:new{x = math.random(1,92), y = math.random(1,92), roomx = 0, roomy = 0, symbol = "t", name = "Tree"}
end