Sorting Linq results ascending using subselect and then descending - vb.net

I am trying to work out a LINQ query to pull items from a List, the List contains a child-nested List - which I need to query for an item, then use the resulting item in my final sort of all records returned the query.
The parent is List(Of MediaItems) - the class structure as follows:
MediaItems
.ID (int)
.Src (string)
.Advert (bool)
.AdOptions As List(Of AdvertOptions)
.Counter (int)
AdvertOptions class consists of:
.Age (int)
.Gender (int)
.Priority (int)
I want to query for any MediaItems that meet the following criteria:
.Advert = true
.Age = x (paramter in calling function)
.Gender = y (paramter in calling function)
To do this, I am using the following LINQ query: (Age and Gender are the function parameters)
Where(Function(s) s.Advert And s.AdOptions.Any(Function(a) a.Gender = Gender And a.Age = Age))
I now need to sort the results based on two sorting levels:
AdOptions.Priority (in descending order), then sort by Counter in ascending order
This is my failing attempt:
Where(Function(s) s.Advert And s.AdOptions.Any(Function(a) a.Gender = Gender And a.Age = Age)).OrderBy(Function(a) From p1 In a.AdOptions Where p1.Gender = Gender And p1.Age = Age Select p1.Priority).ThenByDescending(Function(s) s.Counter)
My attempt is not working, I am getting the following error:
at least on object must implement IComparable
Can anyone see what needs to be done to achieve my goal?
Ben

On reading your code a bit closer, I spotted that you order by AdOptions
I think this is what you really want
Sub Demo(ByVal gender As Integer, ByVal age As Integer)
Dim items = New List(Of MediaItem)()
Dim matching = Function(a) a.Gender = gender And a.Age = age
Dim ads = items.Where(Function(s) s.Advert And s.AdOptions.Any(matching))
Dim sorted = ads _
.OrderBy(Function(a) a.AdOptions.Where(matching).Max(Function(ao) ao.Priority)) _
.ThenByDescending(Function(s) s.Counter)
' if you really wanted a sorted list of AdOptions, you'd write
Dim optionlist = ads.OrderByDescending(Function(s) s.Counter) _
.SelectMany(Function(a) a.AdOptions.Where(matching).OrderBy(Function(ao) ao.Priority))
' to combine the two previous options
Dim adsWithOptions = ads.OrderByDescending(Function(s) s.Counter) _
.Select(Function(a) New With { _
.Ad = a, _
.Options = a.AdOptions.Where(matching) _
.OrderBy(Function(ao) ao.Priority) _
})
End Sub

Related

LinQ DataTable Group By

I have a datatable in vb.net application. the data table is looking like this:
State Level City
AL T Arlton
AL T Brton
AL T Rurton
CO T Dver
CO T Crodo
WA T Blain
WA T Bwelham
I would like to group the state and do a loop
I tried the following but is not working as expected.
please help
what I tried so far:
Dim result = From r In dt Group r By state = r(0) Into Group Select Group
For Each r In result
Console.WriteLine(r("State"))
Next
The output from the loop should be
AL
CO
WA
Thank you
You cannot call GroupBy on a datatable. So you may first convert the datatable to a collection using the AsEnumerable() method and then apply your group by
Dim itms = dt.AsEnumerable().[Select](Function(x) New With {
Key .City = x.Field(Of String)("City"),
Key .State = x.Field(Of String)("State"),
Key .Level = x.Field(Of String)("Level")
})
Dim uniqueStatesgroupedStates = itms.GroupBy(Function(s) s.State, Function(p) p,
Function(k, v) New With {
Key .State = k,
Key .Item = v
})
' IF you want group records based in State
For Each r In uniqueStatesgroupedStates
Console.WriteLine(r.State)
Next
' IF you want only Unique states from your data table
Dim uniqueStates = itms.[Select](Function(s) s.State).Distinct().ToList()
For Each r In uniqueStates
Console.WriteLine(r)
Next
Your expected output looks like you want only the unique State names, In that case you simply need to do a Select on State property and call Distinct() on that.
I guess you need distinct instead of a group by, If I understood your question correctly following method will work for you.
Dim resultdt As DataTable = dt.DefaultView.ToTable(True, "state")
For Each row In newdt.Rows
Console.WriteLine(row(0).ToString)
Next
DataView.ToTable Method (): Creates and returns a new DataTable based on rows in an existing DataView

Substitution in LINQ OrderBy

I have tried the following to modify the sort order in order to have the fields with HeureDebut sorted to the end when it is 00:00 but it does not work
Is there another method to achieve this?
Dim theCompetitions As List(Of competitions) = (From aCompetition In context.competitions
Where aCompetition.concour_id = idConcours
And aCompetition.isPublic = 1
And aCompetition.idMYMaster = 0 _
Order By aCompetition.DateEpreuve, If(aCompetition.HeureDebut = "00:00", "99:99", aCompetition.HeureDebut), aCompetition.NoRef).ToList()

linq using order by, where, and select vb.net

I have a Linq query that I am passing to a list, and then to the view through the viewbag. I am trying to keep that list in a specific order, so that when I iterate through it I have control over the order in which it's displayed.
Here is the query:
ViewBag.attributes = (From row In db.tblCategory_Attributes
Where row.Item_Type_Identifier = itemType
Order By row.Category_Attribute_Identifier
Select CStr(row.Attribute_Name)
Distinct).ToList()
I am successfully passing this list to the view and iterating through it, but no matter what the values are always displayed in alphabetical order. Category_Attribute_Identifier is an integer that aligns with the order I would like these values to be displayed in.
I've played around with the order of my statements quite a bit and I'm not having any luck.
Can you tell me how to distinctly select the Attribute_Name's that correlate with my specific Item_Type_Identifier and order my results by the Category_Attribute_Identifier?
The Distinct is creating its own ordering again (because it shuffles through the result to filter out duplicates). Just do the sorting after the Distinct:
(From row In db.tblCategory_Attributes
Where row.Item_Type_Identifier = itemType
Select row
Distinct)
.OrderBy(Function(row) row.Category_Attribute_Identifier)
.Select(Function(row) CStr(row.Attribute_Name))
Try using Group By instead of Distinct
ViewBag.attributes = (From row In db.tblCategory_Attributes _
Where row.Item_Type_Identifier = itemType _
Order By row.Category_Attribute_Identifier) _
.AsEnumerable() _
.GroupBy(Function(r) r.Attribute_Name) _
.Select(Function(g) g.Key) _
.ToList()
Or use the extension method syntax which gives you the freedom of applying the extension methods in any order:
ViewBag.attributes = db.tblCategory_Attributes _
.Where(Function(row) row.Item_Type_Identifier = itemType) _
.Select(Function(row) New With {row.Attribute_Name, row.Category_Attribute_Identifier}) _
.Distinct() _
.OrderBy(Function(a) a.Category_Attribute_Identifier) _
.Select(Function(a) a.Attribute_Name) _
.ToList()
This simple test demonstrates that GroupBy preserves the order:
Public Shared Sub TestGroupOrder()
Dim a = New Integer() {6, 2, 4, 2, 7, 5, 3, 4}
Dim query = a.GroupBy(Function(i) i).[Select](Function(g) g.Key)
For Each i As Integer In query
Console.Write("{0} ", i)
Next
End Sub
Result in the console:
6 2 4 7 5 3

Enumeration yielded no results

I am trying to insert/update my table using the values in the grid
Given below is the code I'm using to get the productId in the grid:
Dim prodId = From row As DataRow _
In grdSale.Rows _
Where row.Item(0).ToString <> "" _
Select row.Item(0)
I am getting productid correctly. Given below is the code to get the value in QTY column with respect to the productId:
For Each id As Long In prodId
Dim intpdt As Long
intpdt = id
intQty = (From row As DataRow In grdSale.Rows Where _
row.Item(0).Equals(intpdt) _
Select row.Item("QTY")).FirstOrDefault()
Next
In intQty I am getting 0 but it should be 10 or 12 as you can see in the QTY column in the grid (Enumeration yielded no results).
Where am wrong?
Try doing this and see if you get the result you expected:
intQty = _
( _
From row As DataRow In grdSale.Rows Where _
CLng(row.Item(0)) = intpdt _
Select CInt(row.Item("QTY")) _
).FirstOrDefault()
Not sure what causes your issue, but you should use the Field extension method since it is strongly typed and supports nullable types. I also don't understand why you need the additional loop and query to find the quantity of each product. This should do both:
Dim prodQTYs = From row In grdSale.Rows.Cast(Of DataRow)()
Let ProductID = row.Field(Of String)("ProductId")
Let Quantity = row.Field(Of Long)("QTY")
Where Not String.IsNullOrWhiteSpace(ProductID)
Select New With {.ProductID = ProductID, .Quantity = Quantity}
Change the types to the appropriate ones.
Output:
For Each prodInfo In prodQTYs
Console.WriteLine("Product:{0} Quantity:{1}", prodInfo.ProductID, prodInfo.Quantity)
Next

LINQ to SQL Query Syntax

I have several tables that center around an organization table that simply holds a unique ID value. Each Organization can then be at a particular location and have a particular name. The tricky part is that the organizations support location and name changes with a specified effective date of each change. For this example I have 4 relevant tables:
Organization: ID (PK, int, identity)
Location: ID (PK, int, identity), Name (varchar), AltLat (float), AltLong (float)
organization_locations: organization_id(FK, int), location (FK, int), eff_date (datetime)
organization_names: organization_id (FK, int), name (ntext), eff_date (datetime), icon (nvarchar(100))
What I need to retrieve is the list of all locations along with all organizations at a given location as of a specific date and project them into my business entities. In other words, I will have a date provided and need to return for each location, the organization related to the organization_location entry with the most recent eff_date that is less than the date provided. Same thing goes for each organization, I'd need the name as of the date.
Here's what I started with but it doesn't seem to work:
Dim query = From loc In dc.Locations _
Where loc.AltLong IsNot Nothing And loc.AltLat IsNot Nothing _
Select New AnnexA.Entities.AnnexALocation With {.ID = loc.ID, .Name = loc.Location, .Y = loc.AltLat, .X = loc.AltLong, _
.Units = From ol In loc.organization_locations Let o = ol.Organization.organization_names.Where(Function(ed) (ol.eff_date < Date.Parse("1/1/2011"))).OrderByDescending(Function(od) (od.eff_date)).First() Select New AnnexA.Entities.AnnexAMillitaryUnit With {.ID = o.ID, .Name = o.name, .IconPath = o.icon}}
I'd prefer VB syntax but if you can only give me a C# query I can work with that. I've tried a few other variations but I end up getting syntax errors about an expected "}" or members not being a part of an entity set no matter what combination of parenthesis I try.
I think I understand what you're trying to do here, and it looks like some simple joins could get rid of all the complex stuff you're doing at the end of your query. (It's in C# but it should be pretty straight forward to get it to VB)
from loc in dc.Locations
join ol in dc.organization_locations on loc.ID equals ol.location
join orn in dc.organization_names on ol.organization_id equals orn.organization_id
where loc.AltLong != null
&& loc.AltLong != null
&& ol.eff_date < Date.Parse("1/1/2011")
orderby ol.eff_date
select new AnnexA.Entities.AnnexAMillitaryUnit
{ ID = loc.ID, Name = orn.name, IconPath = orn.icon}
Let me know if this works or if it needs any fine tuning...
I need the most recent (as of a given date) location for an organization and name of an organization taken into consideration. Not just all prior to the date. For example, take the U.S. President Elect Obama. In this example there are 3 locations: his house, the Hay-Adams Hotel, and the White House. He also has gone by 3 titles in the past couple months: Senator Obama, President Elect Obama, and soon to be President Obama.
If you were to pass in the date "1/1/2008" into this query, you should get back all 3 locations, with "Senator Obama" (the organization_name) falling within his "house" (the organization_location) and the other two locations should have no "organizations" within them using our sample. If you were to pass in the date of "12/1/2008", he was still technically living at his home but he then had the title of "President Elect Obama" so the results would reflect that. If you were to pass in today's date, his name would still be "President Elect Obama" but his location changed to the "Hay-Adams Hotel". If you pass in any date after "1/20/2009", his title will be "President Obama" and his location will be "The Whitehouse".
I hope that made some sense, even if you have no interest in politics or aren't from the U.S.. I need the results to show me where everything was at and what everything was called at a given point in time.
I ended up getting this working using the following code:
Dim query4 = From loc In dc.Locations _
Let curNames = (From ons In dc.organization_names _
Where ons.eff_date <= ssDate _
Order By ons.eff_date Descending _
Group ons By ons.organization_id Into gNames = Group _
Select New With { _
Key .Key = organization_id, _
.Result = gNames.Take(1)}) _
.SelectMany(Function(a) (a.Result)) _
Let curLocs = (From oLocs In dc.organization_locations _
Where oLocs.eff_date <= ssDate _
Order By oLocs.eff_date Descending _
Group oLocs By oLocs.organization_id Into gLocs = Group _
Select New With { _
Key .Key = organization_id, _
.Result = gLocs.Take(1)}) _
.SelectMany(Function(a) (a.Result)) _
Where loc.AltLat IsNot Nothing And loc.AltLong IsNot Nothing _
Select New AnnexA.Entities.AnnexALocation With { _
.ID = loc.ID, .Name = loc.Location, .Y = loc.AltLat, .X = loc.AltLong, _
.Units = From curLoc In curLocs _
Where curLoc.location = loc.ID _
From curName In curNames _
Where curName.organization_id = curLoc.organization_id _
Select New AnnexA.Entities.AnnexAMillitaryUnit With { _
.ID = curLoc.organization_id, _
.Name = curName.name, _
.IconPath = curName.icon}}
Thanks for taking the time to look at this!