linq join 1 to many get first record - linq

I have 2 list:
List1:
ID
1
2
3
List2:
ID Name
1 Jason
1 Jim
2 Mike
3 Phil
I like to join both of these but get only the first record from list2 for a given ID:
The end result would be
ID Name
1 Jason
2 Mike
3 Phil
I tried the following but was not successful:
var lst = (from lst1 in list1
join lst2 in list2
on lst1.ID equals lst2.ID).ToList().First();

You can get this result with what the 101 LINQ Samples calls "Cross Join with Group Join". Combine that with First() to get just one item from the group.
var lst = (
from lst1 in list1
join lst2 in list2 on lst1.ID equals lst2.ID into lstGroup
select lstGroup.First()
);
Example: http://ideone.com/V0sRO

Try grouping list2 by ID first and then selecting the first item from each group. After that, do the join and select what you want.
var uniqueIDList2 = list2.GroupBy(p => p.ID)
.Select(p => p.First());
var result = from lst1 in list1
join lst2 in uniqueIDList2 on lst1.ID equals lst2.ID
select new { lst1.ID, lst2.Name };

Here's one way to do it:
var lst = list1
// Select distinct IDs that are in both lists:
.Where(lst1 => list2
.Select(lst2 => lst2.ID)
.Contains(lst1.ID))
.Distinct()
// Select items in list 2 matching the IDs above:
.Select(lst1 => list2
.Where(lst2 => lst2.ID == lst1.ID)
.First());
Example: http://ideone.com/6egSc

Another way:
var query = from lst1 in list1
let first = list2.FirstOrDefault(f => f.Id == lst1.Id)
where first != null
select first;
Or if you wanted to know about items that could not be located in list2:
var query = from lst1 in list1
let first = list2.FirstOrDefault(f => f.Id == lst1.Id)
select first ?? new { Id = 0, Name = "Not Found" };

Related

LINQ for nested for each

Can you please give me a solution for the method as Linq?
I need Linq for the below method:
private List<Model> ConvertMethod(List<Model> List1, List<Model> List2)
{
foreach (var Firstitem in List1)
{
foreach (var Seconditem in List2)
{
if (Firstitem.InnerText.Trim() == Seconditem.InnerText.Trim())
{
Seconditem.A= Firstitem.A;
Seconditem.B= Firstitem.B;
Seconditem.C= Firstitem.C;
Seconditem.D= Firstitem.D;
Seconditem.E= Firstitem.E;
Seconditem.F= Firstitem.F;
}
}
}
return List2;
}
Your task is to assign values, so modify objects. That's not the purpose of LINQ which is to query datasources. So you could use LINQ to build a query that returns all items that need to be updated. Then you can use a foreach to assign the values(as you did):
var sameItems = from l1 in List1 join l2 in List2
on l1.InnerText.Trim() equals l2.InnerText.Trim()
select new { l1, l2 };
foreach(var itemsToUpdate in sameItems)
{
itemsToUpdate.l2.A = itemsToUpdate.l1.A;
// ...
}
It helps if you think about what this code is supposed to do - update records in the second list with the values of matching records from the first list.
There are various ways you can do that. One option is to replace each foreach with from and filter the rows:
var matches = from var Firstitem in List1
from var Seconditem in List2
where Firstitem.InnerText.Trim() == Seconditem.InnerText.Trim()
select (Firstitem,Seconditem);
foreach(var (Firstitem,Seconditem) in matches)
{
Seconditem.A= Firstitem.A;
Seconditem.B= Firstitem.B;
Seconditem.C= Firstitem.C;
Seconditem.D= Firstitem.D;
Seconditem.E= Firstitem.E;
Seconditem.F= Firstitem.F;
}
I'm "cheating" a bit here, using tuples and decomposition to reduce noise
Another option is to use join. In this case, the two options are identical :
var matches = from Firstitem in List1
join Seconditem in List2
on Firstitem.InnerText.Trim() equals Seconditem.InnerText.Trim()
select (Firstitem,Seconditem);
The rest of the code remains the same

is there a faster way to work with nested linq query?

I am trying to query a table with nested linq query. My query working but is too slow. I have almost 400k row. And this query work 10 seconds for 1000 rows. For 400k I think its about to 2 hours.
I have rows like this
StudentNumber - DepartmentID
n100 - 1
n100 - 1
n105 - 1
n105 - 2
n107 - 1
I want the students which have different department ID. My results looks like this.
StudentID - List
n105 - 1 2
And my query provides it. But slowly.
var sorgu = (from yok in YOKAktarim
group yok by yok.StudentID into g
select new {
g.Key,
liste=(from birim in YOKAktarim where birim.StudentID == g.Key select new { birim.DepartmentID }).ToList().GroupBy (x => x.DepartmentID).Count()>1 ? (from birim in YOKAktarim where birim.StudentID == g.Key select new { birim.DepartmentID }).GroupBy(x => x.DepartmentID).Select(x => x.Key).ToList() : null,
}).Take(1000).ToList();
Console.WriteLine(sorgu.Where (s => s.liste != null).OrderBy (s => s.Key));
I wrote this query with linqpad C# statement.
For 400K records you should be able to return the student ids and department ids into an in-memory list.
var list1 = (from r in YOKAktarim
group r by new { r.StudentID, r.DepartmentID} into g
select g.Key
).ToList();
Once you have this list, you should be able to group by StudentID and select those students who have more than one record.
var list2 = (from r in list1 group r by r.StudentID into g
where g.Count() > 1
select new
{
StudentID = g.Key,
Departments = g.Select(a => a.DepartmentID).ToList()
}
).ToList();
This should be faster as it only hits the sql database once, rather than hundreds of thousands of times.
You're iterating your source collection (YOKAktarim) three times, which makes your query *O(n^3)` query. It's going to be slow.
Instead of going back to source collection to get content of the group you can simply iterate over g.
var sorgu = (from yok in YOKAktarim
group yok by yok.StudentID into g
select new {
g.Key,
liste = from birim in g select new { birim.DepartmentID }).ToList().GroupBy (x => x.DepartmentID).Count()>1 ? (from birim in g select new { birim.DepartmentID }).GroupBy(x => x.DepartmentID).Select(x => x.Key).ToList() : null,
}).Take(1000).ToList();
However, that's still not optimal, because you're doing a lot of redundant subgrouping. Your query is pretty much equivalent to:
from yok in YOKAktarim
group yok by yok.StudentID into g
let departments = g.Select(g => g.DepartmentID).Distinct().ToList()
where departments.Count() > 1
select new {
g.Key,
liste = departments
}).Take(1000).ToList();
I can't speak for the correctness of that monster, but simply removing all ToList() calls except the outermost one will fix your issue.

Linq: Produce one result from two linq sources

I have two linq statements which both prouce an output of the same tye. How do can I merge this two linqstatements into, so that I get a single list.
Example:
var list1 = new List<string>() {"hello", "world", "!"};
var list2 = new List<string>() {"hello 2", "world 2", "! 2"};
var linq1 = from item in list1
where item.Contains('o')
select item;
var linq2 = from item in list2
where item.Contains('l')
select item;
var joined = linq1.Concat(linq2);
Output:
[0]: "hello"
[1]: "world"
[2]: "hello 2"
[3]: "world 2"
I want to have this in a single linq statement.
You just use Concat as you have. But instead of linq.Concat(linq2), you just put the queries there:
var joined = list1.Where (w => w.Contains("o"))
.Concat(list2.Where (w => w.Contains("l")));
Concat doesnt have a query syntax equivalent. (http://msdn.microsoft.com/en-us/library/bb386979(v=vs.110).aspx)
So you'd do something like this:
var joined = (from item in list1
where item.Contains('o')
select item)
.Concat(from item in list2
where item.Contains('l')
select item);

How can I get the top categories by join in LINQ?

I am working on classified ads project I am facing problem to get top categories where most ads are posted every category have sub category also.
I made query but it is working for sub category ads I want if any category have no sub category then parent category ads should counted.
var result = (from c in db.Category
join a in db.Ad on c.CategoryId equals a.CategoryId
where c.ParentId != null
group c by c.ParentId into g
select new { cat = g.Key, a = g.Count() })
.OrderBy(c => c.cat)
.OrderByDescending(a => a.a);
My Category Table is like this
CategoryId ----- ParentId -----Name
How can I do this?
I guess this could be done with separate queries:
Get a list of categories that don't have any parentId set:
var resultsWithoutSubs = from c in db.Category
where c.ParentId == null
&& !result.Any(r => cat == c.CategoryId)
select c;
Based on the list above, repeat the original query:
var counts = from c in resultsWithoutSubs
join a in dbAd on c.CategoryId equals a.CategoryId
group c by c.CategoryId into g
select new {cat = g. Key, a = g.Count};
Then you should be able to Union these the first and the last list and sort the results.
Try this:
var results = db.Category
.Join(db.Ad, cat => cat.CategoryId,
ad => ad.CategoryId, (cat, ad) => new { cat, ad } )
.Where (c => !dbCategory
.Any (d => c.cat.CategoryId == d.ParentId) && c.cat.ParentId == null)
.GroupBy (c => c.cat.CategoryId)
.Select (g => new
{ cat = g.Key, a = g.Count () }
);

Join 2 lists by order instead of condition in LINQ

How can I join 2 lists of equal lengths (to produce a 3rd list of equal length) where I do not want to specify a condition but simply rely on the order of items in the 2 lists.
Eg how can I join:
{1,2,3,4} with {5,6,7,8}
to produce:
{{1,5}, {2,6}, {3,7}, {4,8}}
I have tried the following:
from i in new []{1,2,3,4}
from j in new []{5,6,7,8}
select new { i, j }
but this produces a cross join. When I use join, I always need to specify the "on".
You could use Select in the first list, use the item index and access the element on the second list:
var a = new [] {1,2,3,4};
var b = new [] {5,6,7,8};
var qry = a.Select((i, index) => new {i, j = b[index]});
If you are using .Net 4.0, you can use the Zip extension method and Tuples.
var a = new [] {1,2,3,4};
var b = new [] {5,6,7,8};
var result = a.Zip(b, (an, bn) => Tuple.Create(an, bn));
Alternatively, you can keep them as arrays:
var resultArr = a.Zip(b, (an, bn) => new []{an, bn});
There is a half way solution, if you want to use query syntax. Half way in the sense that you need to use the Select method on both lists in order to get the indexes that you will use in the where clause.
int[] list1 = {1,2,3,4};
int[] list2 = {5,6,7,8};
var result = from item1 in list1.Select((value, index) => new {value, index})
from item2 in list2.Select((value, index) => new {value, index})
where item1.index == item2.index
select new {Value1 = item1.value, Value2 = item2.value};
The benefit with this solution is that it wont fail if the lists have different lengths, as the solution using the indexer would do.

Resources