How to combine rows using LINQ? - linq

Say I have an entity with following properties [Id, UserName, ProductName], where Id is the PK, and other fields are not unique, so the rows with same UserName repeat multiple times.
For one of the views I need to get a collection that would have unique UserName, and other fields would be combined together using string concatenation or something similar.
If I have
[0, John, Alpha]
[1, Mary, Beta]
[2, John, Gamma]
I need a query that would get me a collection like
[John, Alpha Gamma]
[Mary, Beta]
And it would be awesome if all that could be accomplished on the database side without loading the entities.

You are looking for GroupBy():
var results = context.MyEntities.GroupBy( x => x.UserName);
foreach (var item in results)
{
Console.WriteLine("{0} : {1}", item.Key, string.Join(",", item.Select( x=> x.ProductName));
}

Related

Linq Query Filter from two lists where row differs

Not sure how to formulate this Linq query.
I have two lists, each of which contains HashCheck objects:
class HashCheck
{
public string Id {get; set;}
public string Hash {get; set;}
}
So, given
List<HashCheck> list1;
List<HashCheck> list2;
I need a query that will result in a list having rows where the Ids of the rows matches, but the Hash does not.
So for example
List1 =
{1, 12345,
2, 34323,
3, 34083,
4, 09887}
List2 =
{1, 00001, << matching id, not matching hash
2, 34323,
3, 11112, << matching id, not matching hash
4, 09887
5, 98845}
ResultList =
{1, 00001,
3, 11112}
NOTE: in List2, there is an extra row, it would be a bonus if this were included in the ResultList. But I know how to do that in a separate query if necessary.
Thanks for any help.
try this code:
var list3 = (from i in list1
from j in list2
where i.Id == j.Id && i.Hash != j.Hash
select new HashCheck() { Id = j.Id, Hash = j.Hash
}).ToList<HashCheck>();
You can use join. something like below code:
var list3 = (from i in list1
join j in list2 on i.Id equals j.Id
where i.Hash != j.Hash
select new HashCheck() { Id = j.Id, Hash = j.Hash
}).ToList<HashCheck>();
It looks like you want your result to contain the HashCheck objects from list2, which would simply mean:
var ans = list2.Where(hc2 => !list1.Any(hc1 => hc1.Id == hc2.Id && hc1.Hash == hc2.Hash));
e.g. return all list2 elements without a list1 element that matches in both Id and Hash.
If list1 (and/or list2) is very large and performance is a consideration, you can convert list1 to a Dictionary and do lookups against that:
var list1map = list1.ToDictionary(hc1 => hc1.Id, hc1 => hc1.Hash);
var ans2 = list2.Where(hc2 => !list1map.TryGetValue(hc2.Id, out var hash1) || hash1 != hc2.Hash);
Another alternative would be to implement Equals/GetHashCode for your class and then you can use LINQ Except.
Add the following methods to your class:
public override bool Equals(object other) => (other is HashCheck hco) ? Id == hco.Id && Hash == hco.Hash : false;
public override int GetHashCode() => (Id, Hash).GetHashCode();
Now the computation is simple:
var ans3 = list2.Except(list1);
NOTE: Implementing Equals/GetHashCode in this way can be problematic if your HashCode objects are not treated as immutable. Some collection classes really won't like it if the hash code of an object already stored in them changes.
Also, it would be best practice to implement operator== and operator!= as well and possibly IEquatable.

Linq get records based by in clause and sort by

I have persisted a sorted list of ids into the database. Now i want to get the records back based on the persisted id's.
However, the records are coming back in the order of the primary keys not the order of the int[] which I have persisted. Not quite sure how this can be acheived.
I currently have the following:
int[] ids = {8, 1, 5};
var items = from i in ContentPage.All()
where ids.Contains(i.ContentPageId)
select i;
Currently the records are coming out in the order of 1, 5, 8 where I actually want 8,1,5
Database does not obliged to return items in certain order. You can then process them this way:
int[] ids = {8, 1, 5};
var items = (from i in ContentPage.All()
where ids.Contains(i.ContentPageId)
select i).ToList();
var answer = (from id in ids
join item in items
on id equals item.ContentPageId
select item).ToList();

c# group by alphabets

I need to show list of authors group by last name first letter.
e.g.
A
Kim, Ami
Dim, Amaiar
jin, Amairaz
B
Bin, Bom
Kin, Bomo
C
Cin, Ci
Con, Co
....
Could some one please help me what's the best way to solve the above problem?
If you want to group by, use GroupBy, I assumed you want the output to be ordered (OrderBy), Change the GroupBy expression to match your exact requirment:
List<String> names = new List<String>{"Bill", "Mark", "Steve", "Amnon", "Benny"};
foreach(var g in names.GroupBy(name => name.First()).OrderBy(g => g.Key)){
Console.WriteLine(g.Key);
g.OrderBy(name => name).ToList().ForEach(Console.WriteLine);
}
Will output:
A
Amnon
B
Bill
Benny
M
Mark
S
Steve
You can use GroupBy extension method over Linq object to get the desire result.
List<string> firstNames = new List<string>(){ "Ami", "Amaiar","Amiraz","Bom","Bomo","Ci","Co" };
var groups = firstNames.GroupBy(x=>x[0]);
foreach (var element in groups)
{
Console.WriteLine("{0}", element.Key);
foreach (var word in element)
Console.WriteLine(" {0}", word);
}

Entity Framework Linq - how to get groups that contain all your data

Here a sample dataset:
OrderProduct is a table that contains the productIds that were part of a given order.
Note: OrderProduct is a database table and I am using EF.
OrderId, ProductId
1, 1
2, 2
3, 4
3, 5
4, 5
4, 2
5, 2
5, 3
What I want to be able to do is find an order that contains only the productIds that I am searching for. So if my input was productIds 2,3, then I should get back OrderId 5.
I know how I can group data, but I am unsure of how to perform the select on the group.
Here is what I have:
var q = from op in OrderProduct
group op by op.OrderId into orderGroup
select orderGroup;
Not sure how to proceed from here
IEnumerable<int> products = new List<int> {2, 3};
IEnumerable<OrderProduct> orderProducts = new List<OrderProduct>
{
new OrderProduct(1, 1),
new OrderProduct(2, 2),
new OrderProduct(3, 4),
new OrderProduct(3, 5),
new OrderProduct(4, 5),
new OrderProduct(4, 2),
new OrderProduct(5, 2),
new OrderProduct(5, 3),
};
var orders =
(from op in orderProducts
group op by op.OrderId into orderGroup
//magic goes there
where !products.Except(orderGroup.Select(x => x.ProductId)).Any()
select orderGroup);
//outputs 5
orders.Select(x => x.Key).ToList().ForEach(Console.WriteLine);
Or you can have another version as pointed in another answer, just replace
where !products.Except(orderGroup.Select(x => x.ProductId)).Any()
on
where products.All(pid => orderGroup.Any(op => op.ProductId == pid))
second one will have ~ 15% better performance (I've checked that)
Edit
According to the last requirement change, that you need orders that contain not all productIds you are searching, but exactly those and only those productIds, I wrote an updated version:
var orders =
(from op in orderProducts
group op by op.OrderId into orderGroup
//this line was added
where orderGroup.Count() == products.Count()
where !products.Except(orderGroup.Select(x => x.ProductId)).Any()
select orderGroup);
So the only thing you'll need is to add a precondition ensuring that collections contains the same amount of elements, it will work for both previous queries, and as a bonus I suggest 3rd version of the most important where condition:
where orderGroup.Select(x => x.ProductId).Intersect(products).Count() == orderGroup.Count()
At first glance, I'd try something like this:
var prodIds = new[] {2, 3};
from o in context.Orders
where prodIds.All(pid => o.OrderProducts.Any(op => op.ProductId == pid))
select o
In plain language: "get the orders that have a product with every ID in the given list."
Update
Since it appears you are using LINQ to SQL rather than LINQ to Entities, here's another approach:
var q = context.Orders;
foreach(var pid in prodIds)
{
q = q.Where(o => o.OrderProducts.Any(op => op.ProductId == pid));
}
Rather than using a single LINQ statement, you essentially build the query piecemeal.
Thanks to StriplingWarrior's answer I managed to figure this out. Not sure if this is the best way to do this, but it works.
List<int> prodIds = new List<int>{2,3};
var q = from o in Orders
//get all orderproducts that contain products in the ProdId list
where o.OrderProducts.All(op => prodIds.Contains(op.ProductId))
//now group the OrderProducts by the Orders
select from op in o.OrderProducts
group op by op.OrderId into opGroup
//select only those groups that have the same count as the prodId list
where opGroup.Count() == prodIds.Count()
select opGroup;
//get rid of any groups that may be empty
q = q.Where(fi => fi.Count()> 0);
(I am using LinqPad, which is why the query looks a little funky - no context, etc)

LINQ: order results of join based on inner collection

Is it possible to order the results of a LINQ join operation based on the inner collection order?
Say I have two collections:
var names = new[]{"John", "Mary", "David"};
var persons= new[]{ new Person{Name="John", Title"Prof"}, new Person{Name="Mary", Title="Accountant"}, new Person{Name="David", Title="Mechanic"}, new Person{Name="Peter", Title="Homeless"}}
if I do a LINQ join to get a subset of persons as follows:
var taxPayers =
persons
.Join(names , p => p.Name, n => n, (p, n) => p)
.Select(f => new KeyValuePair<string, object>(f.Name, f.Title));
The result is ordered based on the persons array.
It is possible using LINQ, to order taxPayers based on the order of names? Or is this not a LINQable operation?
TIA.
Simply reversing the join should work. Instead of joining names to persons, join persons to names.

Resources