Get All Children to One List - Recursive C# - c#

C# | .NET 4.5 | Entity Framework 5
I have a class in Entity Framework that looks like this:
public class Location
{
public long ID {get;set;}
public long ParentID {get;set;}
public List<Location> Children {get;set;}
}
ID is the identifier of the location, ParentID links it to a parent, and Children contains all of the children locations of the parent location. I'm looking for some easy way, likely recursively, to get all "Location" and their children to one single List containing the Location.ID's. I'm having trouble conceptualizing this recursively. Any help is appreciated.
This is what I have so far, its an extension to the entity class, but I believe it could be done better/simpler:
public List<Location> GetAllDescendants()
{
List<Location> returnList = new List<Location>();
List<Location> result = new List<Location>();
result.AddRange(GetAllDescendants(this, returnList));
return result;
}
public List<Location> GetAllDescendants(Location oID, ICollection<Location> list)
{
list.Add(oID);
foreach (Location o in oID.Children)
{
if (o.ID != oID.ID)
GetAllDescendants(o, list);
}
return list.ToList();
}
UPDATED
I ended up writing the recursion in SQL, throwing that in a SP, and then pulling that into Entity. Seemed cleaner and easier to me than using Linq, and judging by the comments Linq and Entity don't seem the best route to go. Thanks for all of the help!

You can do SelectMany
List<Location> result = myLocationList.SelectMany(x => x.Children).ToList();
You can use where condition for some selective results like
List<Location> result = myLocationList.Where(y => y.ParentID == someValue)
.SelectMany(x => x.Children).ToList();
If you only required Id's of Children you can do
List<long> idResult = myLocationList.SelectMany(x => x.Children)
.SelectMany(x => x.ID).ToList();

This will do the trick:
class Extensions
{
public static IEnumerable<T> SelectManyRecursive<T>(this IEnumerable<T> source, Func<T, IEnumerable<T>> selector)
{
var result = source.SelectMany(selector);
if (!result.Any())
{
return result;
}
return result.Concat(result.SelectManyRecursive(selector));
}
}
Use it like this:
List<Location> locations = new List<Location>();
//
// your code here to get locations
//
List<string> IDs = locations.SelectManyRecursive(l => l.Children).Select(l => l.ID).ToList();

Try this Extension method:
public static IEnumerable<T> Flatten<T, R>(this IEnumerable<T> source, Func<T, R> recursion) where R : IEnumerable<T>
{
return source.SelectMany(x => (recursion(x) != null && recursion(x).Any()) ? recursion(x).Flatten(recursion) : null)
.Where(x => x != null);
}
And you can use it like this:
locationList.Flatten(x => x.Children).Select(x => x.ID);

I had no Children prop in my model, so Nikhil Agrawal's answer doesn't work for me, so here is my solution.
With following model:
public class Foo
{
public int Id { get; set; }
public int? ParentId { get; set; }
// other props
}
You can get children of one item using:
List<Foo> GetChildren(List<Foo> foos, int id)
{
return foos
.Where(x => x.ParentId == id)
.Union(foos.Where(x => x.ParentId == id)
.SelectMany(y => GetChildren(foos, y.Id))
).ToList();
}
For ex.
List<Foo> foos = new List<Foo>();
foos.Add(new Foo { Id = 1 });
foos.Add(new Foo { Id = 2, ParentId = 1 });
foos.Add(new Foo { Id = 3, ParentId = 2 });
foos.Add(new Foo { Id = 4 });
GetChild(foos, 1).Dump(); // will give you 2 and 3 (ids)

I would like to contribute my own solution, which was modified from the references below:
public static IEnumerable<T> Flatten<T, R>(this IEnumerable<T> source, Func<T, R> recursion) where R : IEnumerable<T>
{
var flattened = source.ToList();
var children = source.Select(recursion);
if (children != null)
{
foreach (var child in children)
{
flattened.AddRange(child.Flatten(recursion));
}
}
return flattened;
}
Example:
var n = new List<FamilyMember>()
{
new FamilyMember { Name = "Dominic", Children = new List<FamilyMember>()
{
new FamilyMember { Name = "Brittany", Children = new List<FamilyMember>() }
}
}
}.Flatten(x => x.Children).Select(x => x.Name);
Output:
Dominic
Brittany
Class:
public class FamilyMember {
public string Name {get; set;}
public List<FamilyMember> Children { get; set;}
}
Ref. https://stackoverflow.com/a/21054096/1477388
Note: Can't find the other reference, but someone else on SO published an answer that I copied some code from.

Entity framework does not currently support recursion, and for that reason you can either
Rely on lazy loading child collections as you have done (beware the N+1 problem)
Query an arbitrary depth of objects (This will be an ugly query, though you could generate it using System.Linq.Expressions)
The only real option would be to avoid using LINQ to express the query, and instead resort to standard SQL.
Entity framework supports this scenario fairly well whether you're using code first or not.
For code-first, consider something along the lines of
var results = this.db.Database.SqlQuery<ResultType>(rawSqlQuery)
For model-first, consider using a defining query which I think is a good option as it allows further composition, or stored procedures.
To recursively get back data, you will need to understand recursive CTEs assuming you're using SQL Server, and that it is version 2005+
EDIT:
Here is the code for a recursive query to an arbitrary depth. I put this together just for fun, I doubt it would be very efficient!
var maxDepth = 5;
var query = context.Locations.Where(o => o.ID == 1);
var nextLevelQuery = query;
for (var i = 0; i < maxDepth; i++)
{
nextLevelQuery = nextLevelQuery.SelectMany(o => o.Children);
query = query.Concat(nextLevelQuery);
}
The flattened list is in the variable query

The accepted answer from #NikhilAgrawal will not recursively get all children and grandchildren as #electricalbah has pointed out.
I do miss the answer from #EricLippert that was given on Code Review.
https://codereview.stackexchange.com/a/5661/96658
static IEnumerable<T> DepthFirstTreeTraversal<T>(T root, Func<T, IEnumerable<T>> children)
{
var stack = new Stack<T>();
stack.Push(root);
while(stack.Count != 0)
{
var current = stack.Pop();
// If you don't care about maintaining child order then remove the Reverse.
foreach(var child in children(current).Reverse())
stack.Push(child);
yield return current;
}
}
Called like this:
static List<Location> AllChildren(Location start)
{
return DepthFirstTreeTraversal(start, c=>c.Children).ToList();
}
I made an example below with SelectMany. As you can see from Immediate Window you will not even get the Parent Id if you use that solution.

Create list to add all child using recursively
public static List list = new List();
recursive funtion
static void GetChild(int id) // Pass parent Id
{
using (var ctx = new CodingPracticeDataSourceEntities())
{
if (ctx.Trees.Any(x => x.ParentId == id))
{
var childList = ctx.Trees.Where(x => x.ParentId == id).ToList();
list.AddRange(childList);
foreach (var item in childList)
{
GetChild(item.Id);
}
}
}
}
Sample model
public partial class Tree
{
public int Id { get; set; }
public string Name { get; set; }
public Nullable<int> ParentId { get; set; }
}

Assuming Locations is a DbSet<Location> in your DB context, this will solve your problem "I'm looking for some easy way ... to get all 'Location' and their children to one single List containing the Location.ID's". Seems like I'm missing something, so please clarify if so.
dbContext.Locations.ToList()
// IDs only would be dbContext.Locations.Select( l => l.ID ).ToList()

This is my method for Flattening the children.
private Comment FlattenChildComments(Comment comment, ref Comment tempComment)
{
if (comment.ChildComments != null && comment.ChildComments.Any())
{
foreach (var childComment in comment.ChildComments)
{
tempComment.ChildComments.Add(childComment);
FlattenChildComments(childComment, ref tempComment);
}
}
comment.ChildComments = tempComment.ChildComments;
return comment;
}

For the people who needs something generic:
/// <summary>
/// Recursively enumerate all children, grandchildren etc... in a 1-dimentional IEnumerable
/// </summary>
/// <typeparam name="TModel">The type of the model</typeparam>
/// <param name="root">The root from which to enumerate children</param>
/// <param name="childSelector">The selector on how to select the children of the root.</param>
/// <returns>A 1-dimentional IEnumerable of all it's children, grandchildren etc.. recursively.</returns>
public static IEnumerable<TModel> EnumerateChildren<TModel>(TModel root, Func<TModel, IEnumerable<TModel>> childSelector)
{
var children = childSelector.Invoke(root);
if (children == null)
{
yield break;
}
foreach (var child in children)
{
yield return child;
foreach (var grandChild in EnumerateChildren(child, childSelector))
{
yield return grandChild;
}
}
}
Usage:
var location = GetLocation(); // Get your root.
var children = EnumerateChildren(location, l => l.Children);

Related

recursive function call in foreach throwing system.stackoverflowexception

I am getting a System.StackOverflowException: 'Exception of type 'System.StackOverflowException' was thrown.' message.
My code as follows, Here I want to assign value to a variable recursively based on the condition and return the list.
public class FancyTree
{
public string title { get; set; }
public string key { get; set; }
public List<FancyTree> children { get; set; }
}
For example the FancyTree Class produces the output like parent->child or parent->parent->child or parent->parent->parent->child just like the Treeview structure.
public JsonResult EmployeesTree()
{
var output = converttoFancyTree(db.Database.GetEmployees(true));
return Json(output, JsonRequestBehavior.AllowGet);
}
public List<FancyTree> converttoFancyTree(List<EmpTable> emps)
{
var output = new List<FancyTree>();
foreach (var emp in emps)
{
var fancyTreeItem = new FancyTree();
fancyTreeItem.key = emp.EMP_ID.ToString();
fancyTreeItem.title = emp.EMP_NAME;
if (!string.IsNullOrEmpty(emp.TEAM))
{
//var empIDs = emp.TEAM?.Split(',')?.Select(Int32.Parse)?.ToList();
var tms = emp.TEAM.Split(',');
if (tms.Length > 0) {
var empIDs = new List<int>();
foreach (var t in tms)
{
empIDs.Add(int.Parse(t));
}
var TeamMembers = emps.Where(x => empIDs.Contains(x.EMP_ID)).ToList();
if (TeamMembers.Count > 0)
{
var childrens = converttoFancyTree(TeamMembers);
fancyTreeItem.children = childrens;
}
}
}
output.Add(fancyTreeItem);
}
return output;
}
I would assume your input is in the form of a plain list of objects, where each object contains the IDs of all the children, and you want to convert this to an object representation, i.e. something like:
public class Employee{
public int Id {get;}
public List<int> SubordinateIds {get;}
}
public class EmployeeTreeNode{
public IReadOnlyList<EmployeeTreeNode> Subordinates {get;} ;
public int Id {get;}
public EmployeeTreeNode(int id, IEnumerable<EmployeeTreeNode> subordinates){
Id = id;
Subordinates = subordinates;
}
To convert this to a tree representation we can start by finding the roots of the tree, i.e. employees that are not subordinate to anyone.
var allSubordinates = allEmployees.SelectMany(e => e.SubordinateIds).ToList();
var allRoots = allEmployees.Select(e => e.Id).Except(allSubordinates);
We then need an efficient way to find a specific employee by the Id, i.e. a dictionary:
var employeeById = allEmployees.ToDictionary(e => e.Id, e => e.SubordinateIds);
We can then finally do the actual recursion, and we can create a generic helper method to assist:
public static TResult MapChildren<T, TResult>(
T root,
Func<T, IEnumerable<T>> getChildren,
Func<T, IEnumerable<TResult>, TResult> map)
{
return RecurseBody(root);
TResult RecurseBody(T item) => map(item, getChildren(item).Select(RecurseBody));
}
...
var tree = allRoots.Select(r => MapChildren(
r,
id => employeeById[id],
(id, subordinates) => new EmployeeTreeNode(id, subordinates)));
This will recurse down to any employee without any subordinates, create EmployeeTreeNode for these, and then eventually traverse up the tree, creating node objects as it goes.
This assumes that there are no loops/cycles. If that is the case you do not have a tree, since trees are by definition acyclic, and the code will crash. You will instead need to handle the more general case of a graph, and this is a harder problem, and you will need to decide how the cycles should be handled.

LINQ - Select statement on nested collection object

I have a class file as mentioned below:
public class TestA
{
public string Name {get; set;}
public List<TestA> TestCollection {get;set;}
}
above you can see type of TestCollection is the List of parent class.
Now I want to retrieve Names from each and every object.
I tried with SelectMany as shown below: but that doesn't work for me.
testAList.SelectMany(t => t.Name)
How can I achieve this ?
Here is the fiddle code: Fiddle Code
Assuming that testAList is a List<TestA> and you want the Name values of the TestA objects inside the TestCollection property of every TestA object inside testAList:
testAList.SelectMany(x => x.TestCollection.Select(y => y.Name))
Or, if you want the Name of the objects in testAList:
testAList.Select(x => x.Name)
Finally, if you want ALL the names, you will need to use more than just LINQ because of the recursive nature of your class:
IEnumerable<string> GetNames(List<TestA> testAList)
{
return testAList.Select(x => x.Name)
.Concat(testAList.SelectMany(x => GetNames(x.TestCollection)));
}
var names = GetNames(testAList);
Your collection is recursive, so you need t a recursive query to get its results:
IEnumerable<string> GetAllNames(TestA root) {
return GetNames(new[] {root}); // Forward to a method taking IEnumerable<T>
}
IEnumerable<string> GetAllNames(IEnumerable<TestA> tests) {
return tests
.Select(t => t.Name) // Names at this level
.Concat(tests.SelectMany(t => GetAllNames(t.TestCollection))); // Names of children
}
However, C# offers a better approach using yield return:
IEnumerable<string> GetAllNames(TestA node) {
yield return node.Name;
foreach (var childName in node.TestCollection.SelectMany(t => GetAllNames(t))) {
yield return childName;
}
}
Here is an alternate option that does not use linq but can provide the same result
IEnumerable<string> GetAllNames(TestA root) {
return GetAllNames(new[] { root });
}
IEnumerable<string> GetAllNames(IEnumerable<TestA> tests) {
var queue = new Queue<TestA>(tests);
while (queue.Count > 0) {
var current = queue.Dequeue();
yield return current.Name;
if (current.TestCollection != null) {
foreach (var child in current.TestCollection) {
queue.Enqueue(child);
}
}
}
}
The above based on your fiddle would produce
Parent_0
Parent_1
Parent_2
Parent_3
Parent_4
Child_0_0
Child_0_1
Child_1_0
Child_1_1
Child_2_0
Child_2_1
Child_3_0
Child_3_1
Child_4_0
Child_4_1
If you use a stack approach
IEnumerable<string> GetAllNamesStack(IEnumerable<TestA> tests) {
var stack = new Stack<TestA>(tests.Reverse());
while (stack.Count > 0) {
var current = stack.Pop();
yield return current.Name;
if (current.TestCollection != null) {
current.TestCollection.Reverse();
foreach (var child in current.TestCollection) {
stack.Push(child);
}
}
}
}
The output would be
Parent_0
Child_0_0
Child_0_1
Parent_1
Child_1_0
Child_1_1
Parent_2
Child_2_0
Child_2_1
Parent_3
Child_3_0
Child_3_1
Parent_4
Child_4_0
Child_4_1

Linq to filter tree

Class structure
public clas Item
{
public Item Parent { get; set; }
public string Code { get; set; }
}
example tree
AAA
- AAB
- BBB
CCC
- CCA
So i want to filter tree by CODE == BBB and result should be
AAA
- AAB
- BBB
but if i filter like this
IQueryable<Item> itemsQuery = GetQuery();
itemsQuery = itemsQuery.Where(x => x.Code == "BBB")
result does not contain parent nodes. So, how to include parent nodes, if their child nodes satisfy certain conditions?
In simple way, you cannot get recursive tree using EF. EF returns flat collections. However, there are workarounds.
Variant 1:
Add to your Item public Item Root { get; set; } and public ICollection<Item> AllSiblings { get; set; } property, which points all items to the actual root and second is the other way (all nested items).
The query than make look like:
IQueryable<Item> itemsQuery = GetQuery().Include(x => x.AllSiblings);
itemsQuery = itemsQuery.Where(x => x.Code == "BBB" || x.AllSiblings.Any(s => s.Code == "BBB")).ToList();
Now you have all items in your app and you can than recursively make the tree in C#.
Variant 2:
You can make several SQL queries to get each parent of found items. This is not recommended, because it will get very slow on more found results.
It is hard to apply LinQ here since there is no reference from parent items to children. What about doing simple enumeration to parent after applying filter? It will give you list of all matched items in the tree and then you'll might need to print it in a "tree" manner. Here is a BFS example
IQueryable<Item> itemsQuery = items.AsQueryable();
itemsQuery = itemsQuery.Where(x => x.Code == "BBB");
var bfsQueue = new Queue<Item>(itemsQuery);
var matchedItemsSet = new HashSet<Item>();
while (bfsQueue.Count > 0) {
var item = bfsQueue.Dequeue();
matchedItemsSet.Add(item);
var parent = item.Parent;
if (parent != null && !matchedItemsSet.Contains(parent))
{
bfsQueue.Enqueue(parent);
}
}
foreach (var item in matchedItemsSet) {
Console.WriteLine(item.Code);
}
I prefer universal approaches.
public static IEnumerable<T> SelectUntil<T>(this T element, Func<T, T> nextMemberSelector, Func<T, bool> stopCondition)
{
while (!stopCondition(element))
{
yield return element;
element = nextMemberSelector(element);
}
}
public static IEnumerable<Item> GetAncestors(this Item e)
{
// Or don't Skip(1) if you need the child itself included.
return e.SelectUntil(T => T.Parent, T => T.Parent == null).Skip(1);
}
private static void Main(string[] args)
{
IEnumerable<Item> itemsQuery = GetQuery();
IEnumerable<Item> filter = itemsQuery.Where(T => T.Code == "BBB");
foreach (Item item in filter)
{
Item[] allParents = item.GetAncestors().ToArray();
}
}

Linq handling variable number of OrderBy

I need to support a variable number of Orderby terms in a Linq (to Entity) statement. That is, my function will accept a list of properties on which the data should be order. The properties can have both ascending or descending sorts. What is the best way to handle constructing the Linq query?
Thanks!
You should be able to do something along these lines:
public IEnumerable<MyType> DoSomething(params Expression<Func<MyType,object>>[] properties)
{
var query = // create LINQ query that returns IQueryable<MyType>
query = query.OrderBy(properties.First());
foreach (var property in properties.Skip(1))
{
query = query.ThenBy(property);
}
}
…
var results = DoSomething(() => x.Age, () => x.Height, () => x.LastName);
You'd need to handle the case where fewer than 2 properties are specified.
Following on from Jay's answer, this can be made into a nice extension method:
public static class EnumerableExtensions
{
public static IEnumerable<T> OrderByMany<T>(this IEnumerable<T> enumerable,
params Expression<Func<T, object>>[] expressions)
{
if (expressions.Length == 1)
return enumerable.OrderBy(expressions[0].Compile());
var query = enumerable.OrderBy(expressions[0].Compile());
for (int i = 1; i < expressions.Length;i++)
{
query = query.ThenBy(expressions[i].Compile());
}
return query;
}
}
Usage becomes quite simple, given a test object:
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
This is then possible:
var people = new Person[]
{
new Person() {Name = "John", Age = 40},
new Person() {Name = "John", Age = 20},
new Person() {Name = "Agnes", Age = 11}
};
foreach(var per in people.OrderByMany(x => x.Name, x => x.Age))
{
Console.WriteLine("{0} Age={1}",per.Name,per.Age);
}
Output:
Agnes Age=11
John Age=20
John Age=40
UPDATE
You could add another overload of the OrderByMany method to support SortOrder as well, although it gets clunky rather quickly. Personally I'd just go for the syntax
var query = from p
in people
order by Name, Age descending;
However, for the record, in C#4 at least, I would accomplish the overload using an enum & tuple.
public enum SortOrder
{
Ascending,
Descending
}
and the extra overload:
public static IEnumerable<T> OrderByMany<T>(this IEnumerable<T> enumerable,
params Tuple<Expression<Func<T, object>>,SortOrder>[] expressions)
{
var query = (expressions[0].Item2 == SortOrder.Ascending)
? enumerable.OrderBy(expressions[0].Item1.Compile())
: enumerable.OrderByDescending(expressions[0].Item1.Compile());
for (int i = 1; i < expressions.Length; i++)
{
query = expressions[i].Item2 == SortOrder.Ascending
? query.ThenBy(expressions[i].Item1.Compile())
: query.ThenByDescending(expressions[i].Item1.Compile());
}
return query;
}
Usage becomes clumsy and hard to read:
foreach (var per in people.OrderByMany(
new Tuple<Expression<Func<Person, object>>, SortOrder>(x => x.Age, SortOrder.Descending),
new Tuple<Expression<Func<Person, object>>, SortOrder>(x => x.Name, SortOrder.Ascending)))
{
Console.WriteLine("{0} Age={1}", per.Name, per.Age);
}
To sort by an arbitrary property, you need to build an expression tree to pass to OrderBy.
To sort by an arbitrary number of properties, you need to call ThenBy in a loop.
I like Jamiec's idea but I hate using Tuples because the syntax is ugly. Therefore I built a small class that encapsulates the Tuple and exposes getters for the Item1 and Item2 properties with better variable names.
Also notice that I used a default sort order of ascending so you only need to specify a SortOrder if you want to sort in descending order.
public class SortExpression<T>
{
private Tuple<Expression<Func<T, object>>, SortOrder> tuple;
public SortExpression( Expression<Func<T, object>> expression, SortOrder order =SortOrder.Ascending )
{
tuple = new Tuple<Expression<Func<T,object>>, SortOrder>(expression, order);
}
public Expression<Func<T, object>> Expression {
get { return tuple.Item1; }
}
public SortOrder Order {
get { return tuple.Item2; }
}
}
In my specific application, I have a repository base class which takes an IQueryable and converts it to a ObservableCollection. In that method I use the SortExpression class:
public ObservableCollection<T> GetCollection(params SortExpression<T>[] sortExpressions) {
var list = new ObservableCollection<T>();
var query = FindAll();
if (!sortExpressions.Any()) {
query.ToList().ForEach(list.Add);
return list;
}
var ordered = (sortExpressions[0].Order == SortOrder.Ascending)
? query.OrderBy(sortExpressions[0].Expression.Compile())
: query.OrderByDescending(sortExpressions[0].Expression.Compile());
for (var i = 1; i < sortExpressions.Length; i++) {
ordered = sortExpressions[i].Order == SortOrder.Ascending
? ordered.ThenBy(sortExpressions[i].Expression.Compile())
: ordered.ThenByDescending(sortExpressions[i].Expression.Compile());
}
ordered.ToList().ForEach(list.Add);
return list;
}
Here is the method in use:
var repository = new ContactRepository(UnitOfWork);
return repository.GetCollection(
new SortExpression<Contact>(x => x.FirstName),
new SortExpression<Contact>(x => x.LastName));

Multiple columns in Linq

Problem: how to simplify the code below, as I'm aiming towards 30 different properties in the thing class.
The code looks for uniqueness in a 'thing' property.
public class thing
{
public string Name { get; set; }
public string Colour { get; set; }
public string Position { get; set; }
public string Height { get; set; }
}
public List<thing> SeeIfAnyInListHaveAUniqueSingleColumn(List<thing> listOfThings)
{
// try colour
IEnumerable<IGrouping<string, thing>> thingQuery2 = from t in listOfThings
group t by t.Colour;
List<thing> listOfThingsFound = new List<thing>();
foreach (var thingGroup in thingQuery2)
{
if (thingGroup.Count() == 1)
{
foreach (thing thing in thingGroup) // there is only going to be 1
listOfThingsFound.Add(thing);
}
}
// try position
IEnumerable<IGrouping<string, thing>> thingQuery3 = from t in listOfThings
group t by t.Position;
foreach (var thingGroup in thingQuery3)
{
if (thingGroup.Count() == 1)
{
foreach (thing thing in thingGroup) // there is only going to be 1
listOfThingsFound.Add(thing);
}
}
return listOfThingsFound;
}
Downloadable code on http://www.programgood.net/2010/11/06/FindingUniquenessInData.aspx
I think that if you abstract away the FindUnique operation, you can more easily write the tests:
static IEnumerable<T> FindDistinct<T, TKey>(this IEnumerable<T> source,
Func<T, TKey> keySelector)
{
return from item in source
group item by keySelector(item) into grp
where grp.Count() == 1
from single in grp
select single;
}
Then you can write:
var thingsWithUniqueName = listOfThings.FindDistinct(t => t.Name);
var thingsWithUniquePosition = listOfThings.FindDistinct(t => t.Position);
var thingsWithUniqueHeight = listOfThings.FindDistinct(t => t.Height);
You would like to write code like:
foreach var property in Thing.Properties
{
IEnumerable<IGrouping<string, thing>> thingQuery2 = from t in listOfThings
group t by t.property;
List<thing> listOfThingsFound = new List<thing>();
foreach (var thingGroup in thingQuery2)
{
if (thingGroup.Count() == 1)
{
foreach (thing thing in thingGroup) // there is only going to be 1
listOfThingsFound.Add(thing);
}
}
...
}
You can only do that through Reflection and that is something you should stay away from. The only thing I can think of is store the properties in some kind of collection, like a dictionary and iterate that.
I just noticed that Gabe has provided the same answer I was about to post. I thought I would post this anyway just to reinforce that this answer is a good strong use of LINQ. Please accept Gabe's answer rather than this one. Well done Gabe!
public static IEnumerable<T> WhereUniqueByKey<T, P>(
this IEnumerable<T> #this, Func<T, P> keySelector)
{
return #this
.GroupBy(keySelector)
.Where(gt => gt.Count() == 1)
.SelectMany(gt => gt, (_, t) => t);
}
As per Gabe's answer, my function is an extension method, and it needs to be defined in a static class. The only real difference between our answers is that Gabe has used the LINQ query syntax and I used direct LINQ method calls. The result is the same and so is the usage:
var thingsWithUniqueName = listOfThings.WhereUniqueByKey(t => t.Name);
var thingsWithUniquePosition = listOfThings.WhereUniqueByKey(t => t.Position);
var thingsWithUniqueHeight = listOfThings.WhereUniqueByKey(t => t.Height);

Categories