I have two custom lists. One list is called detailList and the other hldList. Below is a simplified version.
class Details
{
string Fund
string DT
}
class Holding
{
string Fund
string Name
double Price
double Amount
}
What I want to do is split the hldList. All Funds in hldList exist in the detailList. I would like to split the hldList based on the DT value in the detailList, a simple example below,
detailList
Fund DT
LMON E
LMPN K
PLLM E
I will only populate the Fund property in the hldList for this example but the other properties would be populated.
hldList
Fund
LMON
LMON
LMON
LMPN
LMPN
PLLM
PLLM
So I would like this to be split on the DT property of the detailList so the answer should be,
E List K List
LMON LMPN
LMON LMPN
LMON
PLLM
PLLM
I take it the best way to achieve this is to use LINQ? However am unsure how to do this?
You can use GroupBy to group by this property. You just need to link both lists with Join:
var dtGroups = from h in hldList
join d in detailList
on h.Fund equals d.Fund
group h by d.DT into dtGroup
select dtGroup;
foreach (var grp in dtGroups)
Console.WriteLine("Key:{0} Funds:{1}"
, grp.Key, string.Join(",", grp.Select(h => h.Fund)));
If you want a List<Holding> for each dt-group:
IEnumerable<List<Holding>> dtHoldingLists =
from h in hldList
join d in detailList
on h.Fund equals d.Fund
group h by d.DT into dtGroup
select dtGroup.ToList();
First of all join both the lists as
var result = (from b in hldList
join a in detailList on b.Fund equals a.Fund
select new { a.DT, a.Fund, b.(*Other Columns)}).OrderBy(a=>a.DT).ToList();
then do grouping
var nList = result.GroupBy(a => a.DT).ToList();
And finally you can loop through each item of the splitted list
foreach (var it in nList)
{
//Here you can process each list one by one
nList.Where(i => i.Key == it.Key).ToList();
}
Related
In the following code:
var finalArticles =
from domainArticle in articlesFoundInDomain
join articleCategoryVersion in dbc.ArticlesCategoriesVersions
on domainArticle.ArticleID equals articleCategoryVersion.ArticleID
join articleCategory in dbc.ArticleCategories
on articleCategoryVersion.CategoryID equals articleCategory.CategoryID
where articleCategory.ParentID == 52
group articleCategory by articleCategory.CategoryID
into newArticleCategoryGroup
I understand that the group clause should be returning an IEnumerable where k is the Key, in this case CategoryID.
I think I'm misunderstanding Linq at this point because I assume that for each 'k' there should be a list of articles in 'v', but I don't understand the mechanisms or terminology or something. When I try to project this statement into a new anonymous object I don't seem to get any articles... where are they?
Edit:
Okay so I've got a piece of code that is working, but unfortunately it's hitting the SQL server multiple times:
var articlesAssociatedWithKnowledgeTypes =
from categories in dbc.ArticleCategories
join categoryVersions in dbc.ArticlesCategoriesVersions
on categories.CategoryID equals categoryVersions.CategoryID
join articles in articlesFoundInGivenDomain
on categoryVersions.ArticleID equals articles.ArticleID
where categories.ParentID == 52 && articles.Version == categoryVersions.Version
select new
{
ArticleID = articles.ArticleID,
ArticleTitle = articles.Title,
ArticleVersion = articles.Version,
CategoryID = categories.CategoryID,
CategoryName = categories.Name
} into knowledgeTypesFlat
group knowledgeTypesFlat by new { knowledgeTypesFlat.CategoryID, knowledgeTypesFlat.CategoryName } into knowledgeTypesNested
select new
{
CategoryID = knowledgeTypesNested.Key.CategoryID,
CategoryName = knowledgeTypesNested.Key.CategoryName,
Articles = knowledgeTypesNested.ToList()
};
I thought the ToList() on Articles would sort that out but it doesn't. But, the code works although I'm not sure if this is optimal?
The grouping returns an enumeration of IGroupings. IGrouping<K, V> itself implements IEnumerable<V>. Think of each group as an enumerable of all the members of that group plus an extra property Key
In your first query you are showing a group by and the second one is a group join, both return different results. The group by returns an IEnumerable<IGrouping<TKey, TElement>>. To get the result you're expecting you could group by CategoryId and CategoryName and project as I show below:
var finalArticles =
from domainArticle in articlesFoundInDomain
join articleCategoryVersion in dbc.ArticlesCategoriesVersions
on domainArticle.ArticleID equals articleCategoryVersion.ArticleID
join articleCategory in dbc.ArticleCategories
on articleCategoryVersion.CategoryID equals articleCategory.CategoryID
where articleCategory.ParentID == 52
group articleCategory by new{ articleCategory.CategoryID,articleCategory.CategoryName}
into g
select new {CatId=g.Key.CategoryID, CatName=g.Key.CategoryName,Articles =g.ToList() };
When you need the grouped elements you can call ToList or ToArray as I did above
Your finalArticles query results in a IEnumerable<IGrouping<int, Article>> (assuming CategoryID is int and your articles are of type Article).
These IGrouping<int, Article> provides a Key property of type int (your CategoryID and also the IEnumerable<Article> representing the sequence of articles for that CategoryID.
You can turn this for example into a Dictionary<int, List<Article>> mapping CategoryIDs to the lists of articles:
var dictionary = finalArticles.ToDictionary(group => group.Key, group => group.ToList());
or to a list of categories containing articles:
var categories = finalArticles.Select(group => new {
CategoryID = group.Key,
Articles = group.ToList()}).ToList();
Update after your comment:
var finalArticles =
from domainArticle in articlesFoundInDomain
join articleCategoryVersion in dbc.ArticlesCategoriesVersions
on domainArticle.ArticleID equals articleCategoryVersion.ArticleID
join articleCategory in dbc.ArticleCategories
on articleCategoryVersion.CategoryID equals articleCategory.CategoryID
where articleCategory.ParentID == 52
group articleCategory by new {articleCategory.CategoryID, articleCategory.Name}
into newArticleCategoryGroup
select new
{
CategoryID = newArticleCategoryGroup.Key.CategoryID,
CategoryName = newArticleCategoryGroup.Key.Name,
Articles = newArticleCateGroup.ToList()
}
I have found many examples to join list in linq but my exact problem is not solved.
I have to join list List and List
i have initialized all properties of List and when joining i have to again initialize all properties of A and added properties from B.
List<A> listB = from c in country
select new A
{
CountryId=c.CountryId
CountryName=c.CountryName
} ;
List<A> listA = from d in data
select new A
{
Name = d.Name
Age= d.Age,
City=d.City,
CountryId=d.CountryId
} ;
now to initialize Country property in List i am joining both list
here the problem starts
listA = from d in listA
join c in listB on d.CountryId=c.CountryId
select new A
{
Name = d.Name
Age= d.Age,
City=d.City,
Country=c.CountryName
} ;
see in above join i have to again initialize Name, Age and City What i can do to initialize properties at one place.
Do not use listA and listB - join countries and data directly:
from c in country
join d in data
on c.CountryId equals d.CountryId
select new A {
Name = d.Name
Age = d.Age,
City = d.City,
Country = c.CountryName
};
BTW why do you use A class for listA and listB if both contain different data? It should be two different classes.
I've been searching how to join two tables (Data and DataValues, one to many) and fill a dictionary of type .
The records of Data(s) might be thousands (e.g. 500,000 or more) and each Data may have 10 to 20 DataValues which makes it a much heavier query, so the performance is really important here.
here is the code I've write:
// Passed via the arguments, for example, sensorIDs would contain:
int[] sensorIDs = { 0, 1, 2, 3, 4, 5, 6, 17, 18 };
Dictionary<Data, List<DataValue>> dict = new Dictionary<Data, List<DataValue>>();
foreach (Data Data in dt.Datas)
{
var dValues = from d in dt.Datas
join dV in dt.DataValues on d.DataID equals dV.DataID
where (SensorIDs.Contains(dV.SensorID))
select dV;
dict.Add(Data, dValues.ToList<DataValue>());
}
But this approach has a significant performance issue and takes a long time to execute.
Not sure if I need to use SQL Views. any suggestions?
You're querying way too many times. You can do this in one query.
var dict = (from d in dt.Datas
join dV in dt.DataValues on d.DataID equals dv.DataID
where SensorIDs.Contains(dv.SensorID)
select new { d, dV }).ToDictionary(o => o.d, o => o.dV.ToList());
In your foreach loop, you are fetching all Data and for each of them, you are doing the same thing.
Edit: Now that wasn't very clear, but I think you want to join only the DataValues that are in the SensorIDs array. In this case:
var dict = (from d in dt.Datas
let dV = (from dataValue in dt.DataValues
where SensorIDs.Contains(dataValue.SensorID) &&
dataValue.DataID = d.DataID
select dataValue)
select new { d, dV }).ToDictionary(o => o.d, o => o.dV.ToList());
You do not need a foreach loop in this case, you can use group join to create the dictionary straight from linq which should give you better performance.
dict=(from DataValue d in dt.DataValues
where sensorIDs.Contains(d.SensorID)
group d by d.DataID
into datavalues
join data in dt.Datas
on datavalues.Key equals data.DataId
select new {
Key = data,
Value = datavalues
}).ToDictionary(a=>a.Key,a=>a.Value.ToList());
or you can use linq expression methods
dict = dt.DataValues.Where(d=>sensorIDs.Contains(d.SensorID))
.GroupBy(a=>a.DataID)
.Join(dt.Datas,a=>a.Key,a=>a.DataId,
(a,b)=>new{Key=b,Value=a.ToList()})
.ToDictionary(a=>a.Key,a=>a.Value);
You don't need foreach loop. Try something like this in general:
var columns = dt.Columns.Cast<DataColumn>();
dt.AsEnumerable().Select(dataRow => columns.Select(column =>
new { Column = column.ColumnName, Value = dataRow[column] })
.ToDictionary(data => data.Column, data => data.Value));
Also, consider reading this: http://blogs.teamb.com/craigstuntz/2010/01/13/38525/
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()
};
Preface: I don't understand what this does:
o => o.ID, i => i.ID, (o, id) => o
So go easy on me. :-)
I have 2 lists that I need to join together:
// list1 contains ALL contacts for a customer.
// Each item has a unique ID.
// There are no duplicates.
ContactCollection list1 = myCustomer.GetContacts();
// list2 contains the customer contacts (in list1) relevant to a REPORT
// the items in this list may have properties that differ from those in list1.
/*****/// e.g.:
/*****/ bool SelectedForNotification;
/*****/// may be different.
ContactCollection list2 = myReport.GetContacts();
I need to create a third ContactCollection that contains all of the contacts in list1 but with the properties of the items in list2, if the item is in the list[2] (list3.Count == list1.Count).
I need to replace all items in list1 with the items in list2 where items in list1 have the IDs of the items in list2. The resulting list (list3) should contain the same number of items at list1.
I feel as though I'm not making any sense. So, please ask questions in the comments and I'll try to clarify.
Joins are not so difficult, but your problem could probably use some further explanation.
To join two lists, you could do something like
var joined = from Item1 in list1
join Item2 in list2
on Item1.Id equals Item2.Id // join on some property
select new { Item1, Item2 };
this will give an IEnumerable<'a>, where 'a is an anonymous type holding an item from list1 and its related item from list2. You could then choose which objects' properties to use as needed.
To get the result to a concrete list, all that is needed is a call to .ToList(). You can do that like
var list3 = joined.ToList();
// or
var list3 = (from Item1 in list1
join Item2 in list2
on Item1.Id equals Item2.Id // join on some property
select new { Item1, Item2 }).ToList();
To do a left join to select all elements from list1 even without a match in list2, you can do something like this
var list3 = (from Item1 in list1
join Item2 in list2
on Item1.Id equals Item2.Id // join on some property
into grouping
from Item2 in grouping.DefaultIfEmpty()
select new { Item1, Item2 }).ToList();
This will give you a list where Item1 equals the item from the first list and Item2 will either equal the matching item from the second list or the default, which will be null for a reference type.
Here is what I came up with (based on this):
List<Contact> list3 = (from item1 in list1
join item2 in list2
on item1.ContactID equals item2.ContactID into g
from o in g.DefaultIfEmpty()
select o == null ? item1 :o).ToList<Contact>();
My favorite part is the big nosed smiley
:o)
Thanks for your help!
Here is a DotNetFiddle with a Linq Group Join
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
class Order
{
public int Id;
public string Name;
public Order(int id, string name)
{
this.Id = id;
this.Name = name;
}
}
class OrderItem
{
public int Id;
public string Name;
public int OrderId;
public OrderItem(int id, string name, int orderId)
{
this.Id = id;
this.Name = name;
this.OrderId = orderId;
}
}
List<Order> orders = new List<Order>()
{
new Order(1, "one"),
new Order(2, "two")
};
List<OrderItem> orderItems = new List<OrderItem>()
{
new OrderItem(1, "itemOne", 1),
new OrderItem(2, "itemTwo", 1),
new OrderItem(3, "itemThree", 1),
new OrderItem(4, "itemFour", 2),
new OrderItem(5, "itemFive", 2)
};
var joined =
from o in orders
join oi in orderItems
on o.Id equals oi.OrderId into gj // gj means group join and is a collection OrderItem
select new { o, gj };
// this is just to write the results to the console
string columns = "{0,-20} {1, -20}";
Console.WriteLine(string.Format(columns, "Order", "Item Count"));
foreach(var j in joined)
{
Console.WriteLine(columns, j.o.Name, j.gj.Count() );
}
It looks like you don't really need a full-join. You could instead do a semi-join, checking each contact in list 2 to see if it is contained in list 1:
ContactCollection list3 = list2.Where(c => list1.Contains(c));
I don't know how big your lists are, but note that this approach has O(nm) complexity unless list1 is sorted or supports fast lookups (as in a hashset), in which case it could be as efficient as O(nlog(m)) or rewritten as a merge-join and be O(n).