Nested LINQ query - linq

I'm trying to perform a nested linq query.
public class Sic
{
public int Id { get; set; }
public string Code { get; set; }
}
public class Message
{
public List<Sic> Sics { get; set; }
public int Id { get; set; }
}
List<Message> msgList = new List<Message>();
Message m1 = new Message
{
Id = 0,
Sics = new List<Sic>()
{
new Sic() {Id = 0, Code = "A2A"},
new Sic() {Id = 1, Code = "A2B"},
new Sic() {Id = 2, Code = "A2C"},
new Sic() {Id = 3, Code = "A2D"}
}
};
Message m2 = new Message
{
Id = 1,
Sics = new List<Sic>()
{
new Sic() {Id = 4, Code = "B2A"},
new Sic() {Id = 5, Code = "B2B"},
new Sic() {Id = 6, Code = "B2C"},
new Sic() {Id = 7, Code = "B2D"}
}
};
msgList.Add(m1);
msgList.Add(m2);
List<string> searchList = new List<string> {"A2A", "A2B"};
I want to find messages in msgList where searchList is contained by the Sics of each each message, i.e. m1 should be found using the above searchList.

You can use something like,
msgList.Where(msg=>msg.Sics.Any(sic=>searchList.Contains(sic.Code)));
or if you need to match all search terms,
msgList.Where(msg => searchList.All(searchTerm=>msg.Sics.Any(sic=>sic.Code==searchTerm)))

If all ids in searchlist need to be matched
msgList.Where(msg => searchList.Any() && searchList.All(s => msg.Sics.Any(sic => s==sic.Code)))
If any id in searchlist need to be matched
msgList.Where(msg=>msg.Sics.Any(sic=>searchList.Contains(sic.Code)));

Related

LINQ: Traverse up a hierarchy to retrieve parent hierarchy

I have some data which represents parent-child relationship on the same entity. Given a node, I need to find its entire upper hierarchy (parent, grand-parent, great grand-parent, etc..)
My entity is like this:
public partial class Location{
public int LocationId { get; set; }
public int? FkParentLocationId { get; set; }
..... more properties here.......
public virtual Location FkParentLocation { get; set; }
public virtual ICollection<Location> InverseFkParentLocation { get; set; }
}
I'm referring to the Hierarchy traverse implementation suggested here, but it works when you go down the hierarchy. How would retrieve the upper hierarchy using LINQ?
Sample data:
List<Location> locations = new List<Location> {
new Location { LocationId = 5, FkParentLocationId = 3, LocationName = "Windsor", LocationDisplayName = "Windsor"},
new Location { LocationId = 15, FkParentLocationId = 3, LocationName = "Hampshire", LocationDisplayName = "Hampshire" },
new Location { LocationId = 12, FkParentLocationId = 3, LocationName = "Sussex", LocationDisplayName = "Sussex"},
new Location { LocationId = 13, FkParentLocationId = 3, LocationName = "Willowood", LocationDisplayName = "Willowood"},
new Location { LocationId = 1, FkParentLocationId = 3, LocationName = "Gerbshire", LocationDisplayName = "Gerbshire"},
new Location { LocationId = 3, FkParentLocationId = 2, LocationName = "Lincoln", LocationDisplayName = "Lincoln"},
new Location { LocationId = 2, LocationName = "Mains", LocationDisplayName = "Mains" } };
Expected output: given location Id:5, I should get a list containing the locations 3 and 2 (as they are the parents).
Here is an approach you could use, demoed with a console app. Heavily borrowing from Jon Skeet.
using System;
using System.Collections.Generic;
using System.Linq;
namespace Locations
{
public partial class Location
{
public int LocationId { get; set; }
public int? FkParentLocationId { get; set; }
public virtual Location FkParentLocation { get; set; }
public virtual ICollection<Location> InverseFkParentLocation { get; set; }
public string LocationName { get; set; }
public string LocationDisplayName { get; set; }
}
class Program
{
static void Main(string[] args)
{
List<Location> locations = new List<Location> {
new Location { LocationId = 5, FkParentLocationId = 3, LocationName = "Windsor", LocationDisplayName = "Windsor"},
new Location { LocationId = 15, FkParentLocationId = 3, LocationName = "Hampshire", LocationDisplayName = "Hampshire" },
new Location { LocationId = 12, FkParentLocationId = 3, LocationName = "Sussex", LocationDisplayName = "Sussex"},
new Location { LocationId = 13, FkParentLocationId = 3, LocationName = "Willowood", LocationDisplayName = "Willowood"},
new Location { LocationId = 1, FkParentLocationId = 3, LocationName = "Gerbshire", LocationDisplayName = "Gerbshire"},
new Location { LocationId = 3, FkParentLocationId = 2, LocationName = "Lincoln", LocationDisplayName = "Lincoln"},
new Location { LocationId = 2, LocationName = "Mains", LocationDisplayName = "Mains" } };
var result = GetAncestorsIds(locations, 5);
foreach (var id in result)
{
System.Console.WriteLine(id);
}
}
private static IEnumerable<int> GetAncestorsIds(List<Location> locations, int id)
{
Location location = locations.SingleOrDefault(l => l.LocationId == id);
if(location != null)
{
while(location != null && location.FkParentLocationId != null)
{
location = locations.SingleOrDefault(l => l.LocationId == location.FkParentLocationId);
if(location != null)
{
yield return location.LocationId;
}
}
}
}
}
}
And this approach can be turned into your own Linq extension. Here's how it could look.
public static class MyExtensions
{
public static IEnumerable<int> GetAncestorIds<TSource>(this IEnumerable<TSource> source, Func<TSource, int> pk, Func<TSource, int?> fk, int id)
{
TSource currentObj = source.SingleOrDefault(s => pk(s) == id);
while(currentObj != null && fk(currentObj) != null)
{
currentObj = source.SingleOrDefault(s => pk(s) == fk(currentObj));
if(currentObj != null)
{
yield return pk(currentObj);
}
}
}
}
and then to call this for your scenario you would do this
var result = locations.GetAncestorIds(l => l.LocationId, l => l.FkParentLocationId, 5);

How to write a Linq that can retrieve all parent table records and total of sub-table record, I mean 'separate' into two parts

Let's say I have two tables, parent table 'P' and sub-table 'S', I usually wrote the Linq like this to get what I want:
var rows = from p in db.P
join s in db.S on p.Id equals s.ParentId into subContent
where (some condition here)
select new{
Id = p.Id,
Title = p.Title
SubContentCount = subContent.Count()
}
It's very simple, but if for some reason I have to pass a parameter into this query when there has one (let's say 'key'), I have to do this (I guess :-):
var rows = from p in db.P
join s in db.S on p.Id equals s.ParentId into subContent
where (some condition here)
select p;
if(!string.IsNullOrEmpty(key)){ // I'm using C#
rows = rows.Where(q => q.Title.Contains(key))
}
And then:
var list = rows.Select(q => new ()
{
Id = q.Id,
Title = q.Title,
subCount = ???.Count()
});
Is that passable to do Linq like this? if so, how?
Thanks for any kind help!
You could create a method that receives a Func<Table, bool>as parameter and use it to filter your dataset:
public static void Main(string[] args)
{
var rows = new List<Table>
{
new Table { Id = 1, Title = "A", SubContent = new [] { "A1" } },
new Table { Id = 2, Title = "B", SubContent = new [] { "B1", "B2" } },
new Table { Id = 3, Title = "C", SubContent = new [] { "C1", "C2", "C3" } },
};
var title = "C";
foreach (var item in Filter(rows, table =>
String.IsNullOrEmpty(title) || table.Title == title))
{
Console.WriteLine(
"Title={0}, SubContent.Length={1}",
item.Title, item.SubContent.Length);
}
}
public static List<Table> Filter(List<Table> original, Func<Table, bool> filter)
{
return original.Where(filter).ToList();
}
public class Table
{
public int Id { get; set; }
public string Title { get; set; }
public string[] SubContent { get; set; }
}
Why not include the filter in the where clause?
where string.IsNullOrEmpty(key) || p.Title.Contains(key)
Quick example in the interactive console:
public class Parent { public int Id {get; set;} public string Title {get; set;} }
public class SubTable { public int Id {get; set;} public int ParentId {get; set;} }
public class Result { public int Id {get; set;} public string Title {get; set;} public int SubContentCount {get; set;} }
var p1 = new Parent() { Id = 1, Title = "Parent_1" };
var p2 = new Parent() { Id = 2, Title = "Parent_2" };
var p3 = new Parent() { Id = 3, Title = "Parent_3" };
var s1_1 = new SubTable() { Id = 11, ParentId = 1 };
var s1_2 = new SubTable() { Id = 12, ParentId = 1 };
var s1_3 = new SubTable() { Id = 13, ParentId = 1 };
var s2_1 = new SubTable() { Id = 21, ParentId = 2 };
var s2_2 = new SubTable() { Id = 22, ParentId = 2 };
var s3_1 = new SubTable() { Id = 31, ParentId = 3 };
var db_P = new List<Parent>() { p1, p2, p3 };
var db_S = new List<SubTable>() { s1_1, s1_2, s1_3, s2_1, s2_2, s3_1 };
public IEnumerable<Result> GetResults(string key = null)
{
var rows = from p in db_P
join s in db_S on p.Id equals s.ParentId into subContent
where string.IsNullOrEmpty(key) || p.Title.Contains(key)
select new Result() {
Id = p.Id,
Title = p.Title,
SubContentCount = subContent.Count()
};
return rows;
}
And example output (formatted onto multiple lines for readability)
> GetResults().ToList()
List<Submission#0.Result>(3) {
Submission#0.Result { Id=1, SubContentCount=3, Title="Parent_1" },
Submission#0.Result { Id=2, SubContentCount=2, Title="Parent_2" },
Submission#0.Result { Id=3, SubContentCount=1, Title="Parent_3" }
}
> GetResults("1").ToList()
List<Submission#0.Result>(1) {
Submission#0.Result { Id=1, SubContentCount=3, Title="Parent_1" }
}
>

Get tuple and list linked as result

I have this query :
var query = (from tables ...
where ...
select new
{
ClientName = ClientName,
ClientNumber = ClientNumber,
ClientProduct = ClientProduct
}).Distinct();
which returns rows with 3 values.
ClientName and ClientNumber can be linked to multiple products.
So we can have :
NameA NumberA Product1
NameA NumberA Product2
NameA NumberA Product3
NameB NumberB Product4
NameC NumberC Product5
I would like to know if it is possible to store that in a List of a certain class which would be like :
class MyClass
{
string ClientName,
int ClientNumber,
List<int> ClientProducts
}
So there are no duplicate of ClientName and ClientNumber.
Thank you in advance.
With this class structure to represent your data:
class MyClass
{
public string ClientName { get; set; }
public int ClientNumber { get; set; }
public List<int> ClientProducts { get; set; }
}
class Procuct
{
public string ClientName { get; set; }
public int ClientNumber { get; set; }
public int ProductID { get; set; }
}
and this test data:
List<Procuct> Products = new List<Procuct>()
{
new Procuct() { ClientName = "A", ClientNumber = 1, ProductID = 1},
new Procuct() { ClientName = "A", ClientNumber = 1, ProductID = 2},
new Procuct() { ClientName = "A", ClientNumber = 1, ProductID = 3},
new Procuct() { ClientName = "B", ClientNumber = 2, ProductID = 4},
new Procuct() { ClientName = "C", ClientNumber = 2, ProductID = 5}
};
you can use the following linq query:
var q = from p in Products
group p by new
{
cName = p.ClientName,
cNumber = p.ClientNumber
} into pGroup
select new MyClass
{
ClientName = pGroup.Key.cName,
ClientNumber = pGroup.Key.cNumber,
ClientProducts = pGroup.Select(x => x.ProductID).ToList()
};
to get exactly what you want, i.e. a collection of MyClass objects.
The Grouping performed in the above linq query essentially guarantees that there will be no duplicates on (ClientName, ClientNumber).
Since you mention Linq-to-sql, most probably you Client entity already has the products linked. You might look for an overcomplicated solution.
It depends a bit on your foreign key stucture, but if your datamodel would be
Client has 1-many product and you have a Foreign key from product to client it is already present.
So you can just reference client.Products.
So in your case it would be
var query = (from Clients...
where ...
select new
{
ClientName = Client.ClientName,
ClientNumber = Client.ClientNumber,
ClientProduct = Client.Products.Select(s=>s.id).ToList()
});
But you might as well simply use your client entity with a eager load of the products.
It all depends on your datamodel + proper foreign key structure
if you have a many-many associations like Product-per-client between your client and product you can start from that entity. Have a look at this documentation - it provides a good starting point for Linq-2-sql.
http://weblogs.asp.net/scottgu/using-linq-to-sql-part-1
I solve same problem , I think it useful to you
Only check your Where Condition properly
Thank...
var query = (from tables ...
where ...
select new
{
ClientName = ClientName,
ClientNumber = ClientNumber,
ClientProduct = ClientProduct.ToList()
}).Distinct();

merging 2 collections and find all the unique items

I have 2 collections need to create a 3 one if you like by merging the 2 and giving me a third one with all the unique items only
class Program
{
static void Main(string[] args)
{
ObservableCollection<Person> collectionA = new ObservableCollection<Person>
{
new Person {Id = 1, Name = "Name1", Surname = "Surname1"},
new Person {Id = 2, Name = "Name2", Surname = "Surname2"},
new Person {Id = 3, Name = "Name3", Surname = "Surname3"},
new Person {Id = 4, Name = "Name4", Surname = "Surname4"}
};
ObservableCollection<Person> collectionB = new ObservableCollection<Person>
{
new Person {Id = 5, Name = "Name5", Surname = "Surname5"},
new Person {Id = 2, Name = "Name2", Surname = "Surname2"},
new Person {Id = 6, Name = "Name6", Surname = "Surname6"},
new Person {Id = 4, Name = "Name4", Surname = "Surname4"}
};
ObservableCollection<Person> result=????
}
}
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
}
}
Any ideas?Thanks a lot
EDIT CLARIFICATION
I have collectionA, then I create collection B, compare the two collection add any item to FIND ALL THE ITEMS IN COLLECTIONB THAT DONT EXISTS IN COLLECTION A AND CREATE A RESULT COLLECTION.Hope clear now
If Id is a unique identifier of you person try this one:
ObservableCollection<Person> result = new ObservableCollection<Person>(collectionB
.Where(p => !collectionA.Any(p2=>p2.Id==p.Id)));
Edited answer:
ObservableCollection<Person> result = new ObservableCollection<Person>(collectionB.Except(collectionA));
Note that this will create a new collection that is not tied to the old collections - so if you add a person to collectionA, they will not show up in result automatically.

Linq to Objects - Where search within a list

Linq to Objects - Where search within a list
internal class ProdQtyByWarehouse
{
public int id { get; set; }
public List<ProdWarehouseQty> ProdWarehouseQtys { get; set; }
}
internal class ProdWarehouseQty
{
public int id { get; set; }
public string PName { get; set; }
}
protected void Page_Load(object sender, EventArgs e)
{
var list1 = new List<ProdWarehouseQty>
{
new ProdWarehouseQty
{
id = 3,
PName = "list1PN1"
},
new ProdWarehouseQty
{
id = 4,
PName = "list1PN2"
}
};
var list2 = new List<ProdWarehouseQty>
{
new ProdWarehouseQty
{
id = 5,
PName = "list2PN1"
},
new ProdWarehouseQty
{
id = 6,
PName = "list2PN2"
}
};
var prodQtyByWarehouses = new List<ProdQtyByWarehouse>
{
new ProdQtyByWarehouse {id = 1, ProdWarehouseQtys = list1},
new ProdQtyByWarehouse {id = 1, ProdWarehouseQtys = list2}
};
List<int> integers = new List<int>{2,3,4,6};
List<ProdQtyByWarehouse> list =
(from c in prodQtyByWarehouses
where c.ProdWarehouseQtys.Contains(new ProdWarehouseQty {id = 3})
select c).ToList(); // no object is returned
}
How can i achieve:
List<ProdQtyByWarehouse> list =
(from c in prodQtyByWarehouses
where c.ProdWarehouseQtys.Contains(new ProdWarehouseQty {id in integers})
select c).ToList();
List<ProdQtyByWarehouse> list =
(
from c in prodQtyByWarehouses
where c.ProdWarehouseQtys.Exists(x => integers.Contains(x.id))
select c
).ToList();

Resources