LINQ - Select statement on nested collection object - c#

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

Related

How to use Use List<T>.RemoveAll to simplify for loop and remove items from collection

I have this implementation below. It uses a for loop. Is there a way to eliminate the for loop and simplify the code using C# List Extensions?
public class Member
{
public string member { get; set; }
}
public class PatternMatch
{
public static List<Member> Remove()
{
var prefix = new string[] { "usa-", "o-", "a-" };
var members = new List<Member>();
members.Add(new Member { member = "a-o#b.com" });
members.Add(new Member { member = "usa-b#d.com" });
members.Add(new Member { member = "c#d.com" });
// don't use foreach since we will be modifying the collection
for (var i = members.Count - 1; i >= 0; i--)
{
foreach (var pattern in prefix)
{
if (members[i].member.StartsWith(pattern, StringComparison.InvariantCultureIgnoreCase))
{
members.RemoveAt(i);
}
}
}
return members;
}
}
RemoveAll implementation could look like this
members.RemoveAll(x => prefix.Any(p => x.member.StartsWith(p, StringComparison.InvariantCultureIgnoreCase)))
RemoveAll removes every item that matches the given Predicate<T>.
A Predicate<T> is very basically a method with a parameter of T and a return type of bool.
So the above code could be re-written as
members.RemoveAll(ConditionToRemove);
bool ConditionToRemove(Member x) => true;
The condition
x => prefix.Any(p => x.member.StartsWith(p, StringComparison.InvariantCultureIgnoreCase))
Does the following
for every T - I declared it as the variable x - it will check if their is Any entry in the list prefix that has the condition of
x.member.StartsWith(p, StringComparison.InvariantCultureIgnoreCase)
So, prefix.Any will return true if there is atleast one entry x.member starts with.
With Linq, something like this will work:
return members
.Where(m => !prefix
.Any(p => m.member.StartsWith(p, StringComparison.InvariantCultureIgnoreCase)))
.ToList();

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();
}
}

Get All Children to One List - Recursive 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);

If else alternative for a conditional operator filter in a predicate?

After a long search i have my first question.
I have this piece of code:
var strings = new MyStringList { "orange", "APPLE", "grape", "pear" };
foreach (var item in strings.Where(s => s.Length == 5))
{
txtLog.WriteLine(item);
}
And a public class:
public class MyStringList : List<string>
{
public IEnumerable<string> Where(Predicate<string> filter)
{
return this.Select(s => filter(s) ? s.ToUpper() : s);
}
}
Is there a way to rewrite the return statement in an If Else construction?
I came to something like this, only the if gives an error:
if (this.Select(s=> filter(s)))
{
return this.Select(s => s.ToUpper());
}
else
{
return this.Select(s => s);
}
Since your filter works on a single item, you have to iterate through your collection first. During iteration there is no longer any need to use Select() instead you use yield return to dynamically return an iterator.
public IEnumerable<string> Where(Predicate<string> filter)
{
foreach (var s in this)
{
if (filter(s))
yield return s.ToUpper();
else
yield return s;
}
}

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