Linq grouping with list - c#

I have an entity like this
Product Room
long productid long roomid
List<Room> rooms string roomname
String name
Now I have a list of Product entities which has duplicate date apart from list of rooms.
Eq.
Product
Id 1 Id2 Id3
Rooms = 1,A Rooms = 1,B Rooms =1,C
ABC ABC ABC
In this case I have the same property repeated 3 times because it got 3 rooms.
What I am trying to do is add all the rooms to one Product entity which will have one product entity and list of rooms (list of 3 in this case).
I tried something like this but it doesn’t work.
here I am passing the property id
var result = from t in Product
where t.ProductId == propId
group t by t.Rooms
into g
select g;
Any help or idea please

How about this?
var product = Product.First(p => p.ProductId == propId);
product.Rooms = from p in Product
from r in p.Rooms
select r;

If you just need to get the list of rooms that are associated with any of the products, you could try this:
var query = from t in products
select t;
var rooms = query.Aggregate(new List<Room> { }.AsEnumerable(), (i, p) => i.Union(p.rooms));
Note that the aggregation will NOT be executed as an sql query (if you are pulling data out of a db) - rather, it will be performed in memory.
I'm not sure if this is the result you wanted, but it will get you a list of rooms that are in the "rooms" property of all of the products combined. For example, if you have one product with rooms "A" and "B", and another product with "C", the resulting list of rooms will contain "A", "B", and "C".

Related

Select Multiple Values from a Collection using Lambda Expression

How do I select two or more values from a collection into a list using a single lambda expression? Here is what I am trying:
List<Prodcut> pds=GetProducts();
List<Product> pdl = new List<Product>();
foreach (Product item in pds)
{
pdl.Add(new Product
{
desc = item.Description,
prodId = Convert.ToInt16(item.pId)
});
}
GetProducts() returns a list of Products that have many (about 21) attributes. The above code does the job but I am trying to create a subset of the product list by extracting just two product attributes (description and productId) using a single lambda expression. How do I accomplish this?
What you want to do is called projection, you want to project each item and turn them into something else.
So you can use a Select:
var pdl = pds.Select(p => new Product
{
desc = p.Description,
prodId = Convert.ToInt16(p.pId)
}).ToList();

select multiple objects from list based on values from another list

I have two lists. One list is of type Cascade (named, cascadeList) & the other list if of type PriceDetails (named priceList), both classes are shown below. I have also given a simple example of what I'm trying to achieve below the classes.
So the priceList contains a list of PriceDetail objects where they can be multiple (up to three) PriceDetail objects with the same ISIN. When there are multiple PriceDetails with the same ISIN I want to select just one based on the Source field.
This is where the cascadeList comes in. So if there were 3 PriceDetails with the same ISIN I would want to select the one where the source has the highest rank in the cascade list (1 is the highest). Hopefully the example below helps.
Reason for the question
I do have some code that is doing this for me however its not very efficient due to my lack of skill.
In a nutshell it first creates a unique list of ISIN's from the priceList. It then loops through this list for each unique ISIN to get a list of the PriceDetails with the same ISIN then uses some if statements to determine which object I want. So hoping and pretty sure there is a better way to do this.
My Classes
class Cascade
{
int Rank;
string Source;
}
class PriceDetails
{
string ISIN;
string Sedol;
double Price;
string Source;
}
Example
PriceList Cascade
ISIN Source Price Source Rank
BN1 XYZ 100 ABC 1
MGH PLJ 102 XYZ 2
BN1 PLJ 99.5 PLJ 3
BN1 ABC 98
MGH XYZ 102
Result I'm looking for
PriceList
ISIN Source Price
BN1 ABC 98
MGH XYZ 102
For getting the desired result we must do these steps:
Join two lists based on Source property.
Group the last result by ISIN property.
After grouping we must get the minimum rank for
each ISIN.
Then we will use the minRank variable to compare it
against the rank of an elements with the same ISIN and then select
the first element.
We can write this query either with query or method syntax.
With query syntax:
var result = from pr in pricesList
join cas in cascadesList on pr.Source equals cas.Source
select new { pr, cas } into s
group s by new { s.pr.ISIN } into prcd
let minRank = prcd.Min(x => x.cas.Rank)
select prcd.First(y => y.cas.Rank == minRank).pr;
With method syntax:
var result = pricesList.Join(cascadesList,
pr => pr.Source,
cas => cas.Source,
(pr, cas) => new { pr, cas })
.GroupBy(j => j.pr.ISIN)
.Select(g => new { g, MinRank = g.Min(x => x.cas.Rank) })
.Select(r => r.g.First(x => x.cas.Rank == r.MinRank).pr);
Result will be same with both ways:
PriceList
ISIN Source Price
BN1 ABC 98
MGH XYZ 102
P.S: I have assumed that list's name is as following: pricesList and cascadesList
from pr in priceList
join c in cascadeList on pr.Source = c.Source
order by c.Rank
select new {Isin = pr.Isin, Source = pr.Source, Price = pr.Price}
See if this works for you
priceList.GroupBy(p => p.ISIN).OrderByDescending(p =>
cascadeList.FirstOrDefault(c => c.Source == p.Source).Rank).First();

Getting only the highest value in the database

public ActionResult List_of_Winners(int id=0)
{
var winners = (from cat in db.Events_Category_tbl
join can in db.Candidates_Info_tbl
on cat.events_category_id equals can.events_category_id
where cat.events_info_id == id
select new Candidates
{
events_category_name = cat.events_category_name,
candidates_fullname = can.candidates_fullname,
candidates_info_id = can.candidates_info_id,
events_category_id = cat.events_category_id,
no_of_votes = can.no_of_votes.Value
}).OrderBy(x => x.no_of_votes).Distinct();
return PartialView(winners);
}
I have 2 tables, the Events_Category_tbl & Candidates_Info_tbl then in one category there are many candidates registered. Then, what I want to do is that I need to get only the highest votes in the category. And this serve as the winner of the category.
My Candidates table looks like this:
candidates_info_id,
candidates_fullname,
events_category_id,
no_of_votes
My Category table looks this way:
events_category_id,
events_category_name
Then, I want a result in my query that in one category it has one winner of the candidates the one got the highest votes.
How am I gonna do that?
Above is my code.

Referencing a row from another table c# linq

I want to use an id from one table to list a title from another table in wpf, so i did this:
var q = from a in context.associations
select a;
associations = q.ToList();
associationViewSource.Source = associations;
foreach (var item in q)
{
var qTitles = from b in context.textbooks
where b.Id == item.book_id
select b.Title;
assocListView.ItemsSource = qTitles.ToList();
}
in the first portion of the code i am making the main body of the information, it lists all the information from associations table, after that i want to list the relevant titles from textbooks table, thats where i add items to the assocListview, but it of course fails and the data isn't displayed, no errors are thrown either. i hope i was clear enough.
Please help
You can get a list of Titles that have associations with a join...
var qTitles = context.textbooks
.Join(context.associations,
b => b.Id,
a => a.book_id,
b => b.Title)
.ToList();
The intention of the code isn't clear though because assocListView.ItemsSource is assigned to in each iteration of the loop. Is that a bug?
Since you are setting assocListView.ItemsSource each pass through the loop, the resultant list will have the results of the last query.
If you want a list of all associated titles you can get it with a single query:
var titles =
from a in context.associations
join b in context.textbooks on a.book_id equals b.Id
select b.Title;
assocListView.ItemsSource = titles.ToList();
This will return every title linked to any record in your associations table, in no particular order, with duplicates, etc. Normally it makes sense to extract a little more information to make it more usable. For instance, define a structure to hold an associated title:
public struct AssocTitle
{
public int AssocID;
public int BookID;
public string Title;
}
Then query it like:
var titles =
from a in context.associations
join b in context.textbooks on a.book_id equals b.Id
select new AssocTitle { AssocID = a.Id, BookID = b.Id, Title = b.Title };
Then when you click on things in the list view you can find out which book and which association, even if you have lots of associations with the same titles.

Get list of child records

I have a database that looks like this:
tbl_Seminar
ID
isActive
tbl_SeminarFees
ID
seminar_id -- foreign key
fee_text
I want to get all seminars that are active (isActive ==1) and a list of the fees associated with that seminar. Each Seminar may have n records in tbl_SeminarFees that are its fees. I am able to return a linq structure that returns me a list of objects that look like this {seminar, SeminarFee} but I wanted to create a nested structure that looks like this:
{seminar, List<SeminarFee>}
What should my linq query look like?
here is my linq currently:
var results = from s in context.Seminar
join p in context.SeminarFees on
s.ID equals p.SeminarID
where s.IsActive == 1
select new
{
Seminar = s,
Fees = p
};
How do I change this to get a list of these: {seminar, List<SeminarFee>}
Thanks
UPDATE
#lazyberezovsky gave me a good idea to use a group join and into another variable. But then how do I loop through the result set. Here is what I have now:
foreach (var seminarAndItsFeesObject in results)
{
//do something with the seminar object
//do something with the list of fees
}
This however gives me the following error:
Argument type 'SeminarFees' does not match the
corresponding member type
'System.Collections.Generic.IEnumerable`1[SeminarFees]'
What am I doing wrong?
Thanks
You can use group join which groups inner sequence items based on keys equality (a.k.a. join..into) to get all fees related to seminar:
var results = from s in context.Seminar
join f in context.SeminarFees on
s.ID equals f.SeminarID into fees // here
where s.IsActive == 1
select new
{
Seminar = s,
Fees = fees
};
You can't call ToList() on server side. But you can map results on client later.
BTW You can define navigation property Fees on Seminar object:
public virtual ICollection<SeminarFee> Fees { get; set; }
In this case you will be able load seminars with fees:
var results = context.Seminar.Include(s => s.Fees) // eager loading
.Where(s => s.IsActive == 1);
var results = from s in context.Seminar
join p in context.SeminarFees on s.ID equals p.SeminarID
where s.IsActive == 1
group p by s into grouped
select new {
Seminar = grouped.Key,
Fees = grouped.ToList()
};

Categories