grouping in LINQ query - vb.net

Given a table of thousands of rows of data as shown in the sample below
Id Date SymbolId NumOccs HighProjection LowProjection ProjectionTypeId
1 2014-04-09 28 45 1.0765 1.0519 1
2 2014-04-10 5 44 60.23 58.03 1
3 2014-04-11 29 77 1.026 1.0153 1
and a Class defined as
Public Class ProjectionPerformance
Public symbolId As Integer
Public Name as String
Public Date as String
Public ActualRange as Decimal
End Class
I am trying to return the following for each symbolId;
The symbolId (from this table)
The symbol Name (from the symbols table)
The Actual Range (High Projection - Low Projection)
Can this be done in one query since i am essentially in need of a Dictionary(Of Integer, List(Of ProjectionPerformance)) where the integer is the symbolId and the List is generated from the query?
Updated:
So as to be a little clearer, Here is what I'm doing so far but contains two LINQ iterations
Public Shared Function GetRangeProjectionPerformance(Optional daysToRetrieve As Integer = 100) As Dictionary(Of Integer, List(Of ProjectionPerformance))
Dim todaysDate As Date = DateTime.Now.Date
Dim lookbackDate As Date = todaysDate.AddDays(daysToRetrieve * -1)
Dim temp As New Dictionary(Of Integer, List(Of ProjectionPerformance))
Using ctx As New ProjectionsEntities()
Dim query = (From d In ctx.projections
Where d.SymbolId <= 42 AndAlso d.Date >= lookbackDate
Join t In ctx.symbols On d.SymbolId Equals t.Id
Let actualRange = d.HighProjection - d.LowProjection
Select New With {
d.Date,
d.SymbolId,
t.Name,
actualRange}).GroupBy(Function(o) o.SymbolId).ToDictionary(Function(p) p.Key)
For Each itm In query
Dim rpp As New ProjectionPerformance
Dim rppList As New List(Of ProjectionPerformance)
If itm.Value.Count > 0 Then
For x As Integer = 0 To itm.Value.Count - 1
Dim bb As Integer = Convert.ToInt32(itm.Value(x).SymbolId)
With rpp
.SymbolId = bb
.ProjectionDate = itm.Value(x).Date.ToString()
.Name = itm.Value(x).Name
.ProjectedRange = itm.Value(x).actualRange
End With
rppList.Add(rpp)
Next
End If
temp.Add(itm.Key, rppList)
Next
End Using
Return temp
End Function

I'm going to answer in C#, but I think you'll get the gist of it anyway. Basically, you can group by SymbolId, build your object graph and then use ToDictionary using the Key to create dictionary.
var result = (From d In _projectionEntities.projections
Where d.SymbolId <= 42
group d by d.SymbolId into g
select new {
SymbolId = g.Key,
ProjectionPerformances =
g.Select(gg=>new ProjectionPerformance{
SymbolId = gg.SymbolId,
Name = gg.Symbol.Name,
rpDate = gg.Date.ToString(),
ActualRange = gg.HighProjection - gg.LowProjection
})
.ToDictionary(g=>g.SymbolId);

Try
Dim Result = (From d In _ProjectionEntities.projections
Join t In _projectionEntities.symbols On d.SymbolId Equals t.Id
Where d.SymbolId <= 42
Select New With {.SymbolID = d.SymbolID
.Date = d.Date
.Name = t.Name
.ActualRange = d.HighProjection - d.LowProjection})

Related

Entity framework vb.net error in translating query

I am trying to convert this query:
select sales.TTL_Sales_Net, sales.TTL_Sales_Target, units.TTL_Units_Net
from epos_Anal_Branch_Sales_Day sales
join epos_PULL_PM_SKU_STATUS till on sales.BRANCH_ID = till.BRANCH_ID
join epos_Anal_Branch_Units_Day units on sales.BRANCH_ID = units.BRANCH_ID
where till.BRANCH_ID = 45 and sales.DATEKEY = 20140725 and units.DATEKEY = 20140725
from SQL into an Entity Framework query in VB.net. I am sending in the BRANCH_ID in my function and producing the datekey like so:
Dim MyDate As String = Date.Now.ToString("yyyyMMdd")
This is what I have but it just returns no results and I am at a complete loss as to why! This is my query thus far, if anyone can spot what I have done wrong then I would be grateful for a push in the right direction
Public Class clsStoreSales
Public Property DailyAmount As String
Public Property DailyUnits As String
Public Property SalesTarget As String
Public Property ErrorMessages As String
Public Shared Function GetStoreSales(ByVal BranchID As String) As List(Of clsStoreSales)
Dim live As New RMISEntitiesLive
Dim MyDate As String = Date.Now.ToString("yyyyMMdd")
Dim l As New List(Of clsStoreSales)
Try
Dim r = From till In live.epos_PULL_PM_SKU_STATUS
Join sales In live.epos_Anal_Branch_Sales_Day On till.BRANCH_ID Equals sales.BRANCH_ID
Join units In live.epos_Anal_Branch_Units_Day On till.BRANCH_ID Equals units.DATEKEY
Where till.BRANCH_ID = CDbl(BranchID) AndAlso sales.DATEKEY = CDbl(MyDate) AndAlso units.DATEKEY = CDbl(MyDate)
Select New With {.SalesAmount = sales.TTL_Sales_Net, .SalesTarget = sales.TTL_Sales_Target, .SaleUnits = units.TTL_Units_Net}
For Each t In r
Dim m As New clsStoreSales
m.DailyAmount = CStr(t.SalesAmount)
m.DailyUnits = CStr(t.SaleUnits)
m.SalesTarget = CStr(t.SalesTarget)
l.Add(m)
Next
Return l
Catch ex As Exception
Dim e As New clsStoreSales
e.ErrorMessages = ex.ToString
l.Add(e)
Return l
End Try
End Function
End Class
This is what sqlServer Profiler is showing as the query being run:
exec sp_executesql N'SELECT
[Extent1].[BRANCH_ID] AS [BRANCH_ID],
[Extent2].[TTL_Sales_Net] AS [TTL_Sales_Net],
[Extent2].[TTL_Sales_Target] AS [TTL_Sales_Target],
[Extent3].[TTL_Units_Net] AS [TTL_Units_Net]
FROM [dbo].[epos_PULL_PM_SKU_STATUS] AS [Extent1]
INNER JOIN [dbo].[epos_Anal_Branch_Sales_Day] AS [Extent2] ON [Extent1].[BRANCH_ID] = [Extent2].[BRANCH_ID]
INNER JOIN [dbo].[epos_Anal_Branch_Units_Day] AS [Extent3] ON [Extent1].[BRANCH_ID] = [Extent3].[DATEKEY]
WHERE ( CAST( [Extent1].[BRANCH_ID] AS float) = #p__linq__0) AND ( CAST( [Extent2].[DATEKEY] AS float) = #p__linq__1) AND ( CAST( [Extent3].[DATEKEY] AS float) = #p__linq__2)',N'#p__linq__0 float,#p__linq__1 float,#p__linq__2 float',#p__linq__0=45,#p__linq__1=20140725,#p__linq__2=20140725
This
Join units In live.epos_Anal_Branch_Units_Day On till.BRANCH_ID Equals units.DATEKEY
Should be this!
Join units In live.epos_Anal_Branch_Units_Day On till.BRANCH_ID Equals units.BRANCH_ID

convert query result to dictionary in vb.net

What is the proper syntax for .ToDictionary to return the following as a Dictionary(Of String, String) where ShortDesc is the key and CurrentFrontSymbol is the value
Dim dicQuery As Dictionary(Of String, String) = (From d In toolkitEntities.currentfrontcommoditysymbols
Select d.ShortDesc, d.CurrentFrontSymbol)
Update
And can the following functions query and following For Each loop become one LINQ query?
Public Shared Function GetRangeProjectionPerformance(Optional daysToRetrieve As Integer = 100) As Dictionary(Of Integer, List(Of ProjectionPerformance))
Dim todaysDate As Date = DateTime.Now.Date
Dim lookbackDate As Date = todaysDate.AddDays(daysToRetrieve * -1)
Dim temp As New Dictionary(Of Integer, List(Of ProjectionPerformance))
Using ctx As New ProjectionsEntities()
Dim query = (From d In ctx.projections
Where d.SymbolId <= 42 AndAlso d.Date >= lookbackDate
Join t In ctx.symbols On d.SymbolId Equals t.Id
Let actualRange = d.HighProjection - d.LowProjection
Select New With {
d.Date,
d.SymbolId,
t.Name,
actualRange}).GroupBy(Function(o) o.SymbolId).ToDictionary(Function(p) p.Key)
For Each itm In query
Dim rpp As New ProjectionPerformance
Dim rppList As New List(Of ProjectionPerformance)
If itm.Value.Count > 0 Then
For x As Integer = 0 To itm.Value.Count - 1
Dim bb As Integer = Convert.ToInt32(itm.Value(x).SymbolId)
With rpp
.SymbolId = bb
.ProjectionDate = itm.Value(x).Date.ToString()
.Name = itm.Value(x).Name
.ProjectedRange = itm.Value(x).actualRange
End With
rppList.Add(rpp)
Next
End If
temp.Add(itm.Key, rppList)
Next
End Using
Return temp
End Function
As you do not have a special projection in the select, you can just call ToDictionary on the collection. The first lambda expression retrieves the key, the second one the value.
Dim dicQuery = toolkitEntities.currentfrontcommoditysymbols.ToDictionary( _
Function(x) x.ShortDesc, _
Function(y) y.CurrentFrontSymbol)
As for your update: the following query should get you the desired result:
Dim query = (From d In ctx.projections
Where d.SymbolId <= 42 AndAlso d.Date >= lookbackDate
Join t In ctx.symbols On d.SymbolId Equals t.Id
Let actualRange = d.HighProjection - d.LowProjection
Select New With {
d.Date,
d.SymbolId,
t.Name,
actualRange}).GroupBy(Function(o) o.SymbolId)
.ToDictionary(Function(p) p.Key,
Function(x) x.Select(Function(y) New ProjectionPerformance() With {
.SymbolId = Convert.ToInt32(y.SymbolId),
.ProjectionDate = y.Date.ToString(),
.Name = y.Name,
.ProjectedRange = y.actualRange
}).ToList())

mscorlib error on LINQ

I am getting an InvalidOperation on mscorlib.dll error on the following query and cannot for the life of me figure out why.
Here is the Class I'm populating the List(Of ) with;
Public Class ProjectionPerformance
Public SymbolId As Long
Public Name As String
Public ProjectionDate As String
Public ActualRange As Double
Public ProjectedRange As Double
End Class
Those types match the types in the table except for the date which I convert to a string
Here is the function with the LINQ query
Public Shared Function GetRangeProjectionPerformance(Optional daysToRetrieve As Integer = 100) As Dictionary(Of Long, List(Of ProjectionPerformance))
Dim todaysDate As Date = DateTime.Now.Date
Dim lookbackDate As Date = todaysDate.AddDays(daysToRetrieve * -1)
Using ctx As New ProjectionsEntities
Dim query = (From d In ctx.projections
Where d.SymbolId <= 42 AndAlso d.Date >= lookbackDate
Join t In ctx.symbols On d.SymbolId Equals t.Id
Let actualRange = d.ActualHigh - d.ActualLow
Let projectedRange = d.HighProjection - d.LowProjection
Select New With {
d.Date,
d.SymbolId,
t.Name,
projectedRange,
actualRange}).GroupBy(Function(o) o.SymbolId).ToDictionary(Function(p) p.Key,
Function(x) x.Select(Function(y) New ProjectionPerformance() With {
.SymbolId = y.SymbolId,
.ProjectionDate = y.Date.ToString(),
.Name = y.Name,
.ActualRange = y.actualRange,
.ProjectedRange = y.projectedRange
}).ToList())
Return query
End Using
End Function
I'm getting that error on this part of the LINQ query (Im assuming that is that portion is highlighted in green in VS2013)
Function(x) x.Select(Function(y) New ProjectionPerformance() With {
Do I have to retrieve the actual field values and eliminate the Let statements and do the calcs in the List function of the Dictionary call?
Based on your comments, I suspect that the problem is that you try to assign null values to the double properties in ProjectionPerformance. It should work if you change your code as follows:
Dim query = (From d In ctx.projections
Where d.SymbolId <= 42 AndAlso d.Date >= lookbackDate
Join t In ctx.symbols On d.SymbolId Equals t.Id
Let actualRange = d.ActualHigh - d.ActualLow
Let projectedRange = d.HighProjection - d.LowProjection
Select New With {
d.Date,
d.SymbolId,
t.Name,
projectedRange,
actualRange}).GroupBy(Function(o) o.SymbolId).ToDictionary(Function(p) p.Key,
Function(x) x.Select(Function(y) New ProjectionPerformance() With {
.SymbolId = y.SymbolId,
.ProjectionDate = y.Date.ToString(),
.Name = y.Name,
.ActualRange = If(y.actualRange, 0.0),
.ProjectedRange = y.projectedRange
}).ToList())
In order to spot such errors before running the program, you should set OPTION STRICT to ON (either on project or file level).

Inserting to table in VB.NET, LINQ to SQL

This is the beginnings of a timetabling algorithm. The problem is with inserting a member into a group, but I have included the whole subroutine here for context. The table "membergroup" has 2 headings, MemberID and GroupID. No error code is thrown, but the table does not receive the new record.
I have gone through it line by line, and the values for iMember_Choice.MemberID and groupID_to_insert are correct.
Dim numberofrooms As Byte = rm.Count
Dim possiblerooms(numberofrooms + 1), possibleroomcounter As Int32
For TimeTableNumber = 1 To Val(NumberOfTimetablesToCreate.Text)
Dim memchoic = (dc.ExecuteQuery(Of memberChoice)("SELECT * FROM MemberChoices ORDER BY NEWID()")).ToList 'Orders list randomly
'sort into array for each rank, so highest ranks are allocated first
Dim Member_choices_Table_ordered_by_rank = From q In memchoic Order By q.Rank
For Each Member_Choice In Member_choices_Table_ordered_by_rank
ProgressBar.Value = ProgressBar.Value + 1
Dim iMember_Choice As memberChoice = Member_Choice
'Dim memactpossibleinstance(maxmemchoicernk, n) As memactvpossibleinstance
For Each room In rm
Dim rmid As Int32 = room.RoomID ' finds rooms activities can be in
If Not (From rom In roomact Where rom.ActivityID = iMember_Choice.ActivityID And rom.RoomID = rmid).FirstOrDefault Is Nothing Then 'finds suitable rooms
possiblerooms(possibleroomcounter) = rmid
possibleroomcounter = possibleroomcounter + 1
End If
Next
'find possible times
Dim roomid_to_insert, current_maximum_rank As Integer
Dim period_to_insert As String = "MonAM"
Dim staffact_that_can_do_this_activity = _
(From q In staffact Where q.ActivityID = iMember_Choice.ActivityID)
For roomcount = 0 To possibleroomcounter - 1 'for each room in roomcount, find rank for room
Dim rmid As Int32 = possiblerooms(roomcount)
For Each time In periods
Dim itime As String = time.Period
Dim Rank As Int16 = member_Error_check_period_count(iMember_Choice.MemberID, itime)
If Not (From rav In rmav Where rav.RoomID = rmid And rav.Period = itime) Is Nothing Then 'room is free
Rank = Rank + 12
Else ' room has an activity
Dim GroupID_now = (From q In instnce Where q.Period = itime And q.RoomID = rmid Select q.GroupID).SingleOrDefault
If GroupID_now <> 0 Then
Dim groupActID_now = (From q In grp Where q.GroupID = GroupID_now Select q.ActivityID).SingleOrDefault
'Dim activity_now = (From q In actv Where q.ActivityID = groupActID_now).SingleOrDefault
If groupActID_now = iMember_Choice.ActivityID Then 'Good, this activity is already on in this room
Rank = Rank + 50
If (From q In memgrp Where q.GroupID = GroupID_now).Count > 4 Then
Rank = Rank - 50
End If
End If
End If
End If
If Rank > current_maximum_rank Then
current_maximum_rank = Rank
roomid_to_insert = rmid
period_to_insert = itime
End If
Rank = 0
Next
possibleroomcounter = 0
Next 'for each room possible
Dim groupID_to_insert As Integer
If (From q In instnce Where q.Period = period_to_insert And q.RoomID = roomid_to_insert).FirstOrDefault Is Nothing Then
groupID_to_insert = insert_ins_group(period_to_insert, roomid_to_insert, iMember_Choice.ActivityID)
Else
groupID_to_insert = (From q In instnce Where q.Period = period_to_insert And q.RoomID = roomid_to_insert Select q.GroupID).SingleOrDefault
End If
'PROBLEM PROBPBLY HERE/////////////////////////////////////
memgrp.InsertOnSubmit(New membergroup With {.MemberID = iMember_Choice.MemberID, .GroupID = groupID_to_insert}) 'PROBLEM PROBABLY HERE
dc.SubmitChanges()
current_maximum_rank = 0
Next 'for each memberchoice
dc.SubmitChanges()
Next 'timetabl no
dc.SubmitChanges()
memgrp is initiated as
Dim memgrp As Table(Of membergroup) = dc.GetTable(Of membergroup)()

How to remove all duplicates in a data table in vb.net?

Consider my data table
ID Name
1 AAA
2 BBB
3 CCC
1 AAA
4 DDD
Final Output is
2 BBB
3 CCC
4 DDD
How can i remove the rows in the data table using Vb.Net
Any help is appreciated.
Following works if you only want the distinct rows(skip those with same ID and Name):
Dim distinctRows = From r In tbl
Group By Distinct = New With {Key .ID = CInt(r("ID")), Key .Name = CStr(r("Name"))} Into Group
Where Group.Count = 1
Select Distinct
' Create a new DataTable containing only the unique rows '
Dim tblDistinct = (From r In tbl
Join distinctRow In tblDistinct
On distinctRow.ID Equals CInt(r("ID")) _
And distinctRow.Name Equals CStr(r("Name"))
Select r).CopyToDataTable
If you want to remove the dups from the original table:
Dim tblDups = From r In tbl
Group By Dups = New With {Key .ID = CInt(r("ID")), Key .Name = CStr(r("Name"))} Into Group
Where Group.Count > 1
Select Dups
Dim dupRowList = (From r In tbl
Join dupRow In tblDups
On dupRow.ID Equals CInt(r("ID")) _
And dupRow.Name Equals CStr(r("Name"))
Select r).ToList()
For Each dup In dupRowList
tbl.Rows.Remove(dup)
Next
Here is your sample-data:
Dim tbl As New DataTable
tbl.Columns.Add(New DataColumn("ID", GetType(Int32)))
tbl.Columns.Add(New DataColumn("Name", GetType(String)))
Dim row = tbl.NewRow
row("ID") = 1
row("Name") = "AAA"
tbl.Rows.Add(row)
row = tbl.NewRow
row("ID") = 2
row("Name") = "BBB"
tbl.Rows.Add(row)
row = tbl.NewRow
row("ID") = 3
row("Name") = "CCC"
tbl.Rows.Add(row)
row = tbl.NewRow
row("ID") = 1
row("Name") = "AAA"
tbl.Rows.Add(row)
row = tbl.NewRow
row("ID") = 4
row("Name") = "DDD"
tbl.Rows.Add(row)
You can use the DefaultView.ToTable method of a DataTable to do the filtering like this:
Public Sub RemoveDuplicateRows(ByRef rDataTable As DataTable)
Dim pNewDataTable As DataTable
Dim pCurrentRowCopy As DataRow
Dim pColumnList As New List(Of String)
Dim pColumn As DataColumn
'Build column list
For Each pColumn In rDataTable.Columns
pColumnList.Add(pColumn.ColumnName)
Next
'Filter by all columns
pNewDataTable = rDataTable.DefaultView.ToTable(True, pColumnList.ToArray)
rDataTable = rDataTable.Clone
'Import rows into original table structure
For Each pCurrentRowCopy In pNewDataTable.Rows
rDataTable.ImportRow(pCurrentRowCopy)
Next
End Sub
Assuming you want to check all the columns, this should remove the duplicates from the DataTable (DT):
DT = DT.DefaultView.ToTable(True, Array.ConvertAll((From v In DT.Columns Select v.ColumnName).ToArray(), Function(x) x.ToString()))
Unless I overlooked it, this doesn't seem to be in the documentation (DataView.ToTable Method), but this also appears to do the same thing:
DT = DT.DefaultView.ToTable(True)