Having a list of type A, each containing a list of type B, what's the best way to get a list of all type B, each containing a list of type A to which they belong?
Having a list like the following:
var parents = new List<Parent> {
{
new Parent {
ID = 1,
Childs = new List<Child> {
{
new Child {
ID = 1
}
},
{
new Child {
ID = 2
}
},
{
new Child {
ID = 3
}
}
}
}
},
{
new Parent {
ID = 2,
Childs = new List<Child> {
{
new Child {
ID = 3
}
},
{
new Child {
ID = 4
}
},
{
new Child {
ID = 5
}
}
}
}
}
};
I would like to query this to receive the following result:
[
{
Child = 1,
InParent = [1]
},
{
Child = 2,
InParent = [1]
},
{
Child = 3,
InParent = [1, 2]
},
{
Child = 4,
InParent = [2]
},
{
Child = 5,
InParent = [2]
},
]
EDIT: I tried an approach to flatten the childs first using SelectMany & Distinct, but not sure how to link this to the parent again:
var foo =
from childId in parents.SelectMany(x => x.Childs).Select(x => x.ID).Distinct()
select
new
{
childId = childId,
inParent = // Missing Part
};
You have to use SelectMany first to flatten them, then use GroupBy to group by child-id and String.Join to concat each parent-id:
var childParents = parents
.SelectMany(p => p.Childs.Select(c => new {Parent = p, Child = c}))
.GroupBy(x => x.Child.ID)
.Select(g => new
{
Child = g.Key,
InParent = String.Join(", ", g.Select(x => x.Parent.ID))
});
Result:
If you actually don't want that InParent property is a string but a List<int>(or array) use this:
.....
InParent = g.Select(x => x.Parent.ID).ToList() // or ToArray()
You can split your large problem into two simpler problems:
Create an anonymous object for each parent/child pair, containing a reference to the parent as well as to the child. You can use a simple LINQ query with two from clauses for that.
Group those objects into the representation you need. The group by clause is your friend here.
I guess you should try changing your data model if you are looking for storing tree like structure, in that scenario you should always use single Linked List with custom object & nested reference to corresponding parent / child in the similar way you store in database.
It is an ideal way to handle data structures of such kind as such otherwise you will end up in many nested queries.
Related
First question :)
I have a List<Materiau> (where Materiau implements IComparable<Materiau>), and I would like to remove all duplicates and add them together
(if two Materiau is the same (using the comparator), merge it to the first and remove the second from the list)
A Materiau contains an ID and a quantity, when I merge two Materiau using += or +, it keeps the same ID, and the quantity is added
I cannot control the input of the list.
I would like something like this:
List<Materiau> materiaux = getList().mergeDuplicates();
Thank you for your time :)
Check out Linq! Specifically the GroupBy method.
I don't know how familiar you are with sql, but Linq lets you query collections similarly to how sql works.
It's a bit in depth to explain of you are totally unfamiliar, but Code Project has a wonderful example
To sum it up:
Imagine we have this
List<Product> prodList = new List<Product>
{
new Product
{
ID = 1,
Quantity = 1
},
new Product
{
ID = 2,
Quantity = 2
},
new Product
{
ID = 3,
Quantity = 7
},
new Product
{
ID = 4,
Quantity = 3
}
};
and we wanted to group all the duplicate products, and sum their quantities.
We can do this:
var groupedProducts = prodList.GroupBy(item => item.ID)
and then select the values out of the grouping, with the aggregates as needed
var results = groupedProducts.Select( i => new Product
{
ID = i.Key, // this is what we Grouped By above
Quantity = i.Sum(prod => prod.Quantity) // we want to sum up all the quantities in this grouping
});
and boom! we have a list of aggregated products
Lets say you have a class
class Foo
{
public int Id { get; set; }
public int Value { get; set; }
}
and a bunch of them inside a list
var foocollection = new List<Foo> {
new Foo { Id = 1, Value = 1, },
new Foo { Id = 2, Value = 1, },
new Foo { Id = 2, Value = 1, },
};
then you can group them and build the aggregate on each group
var foogrouped = foocollection
.GroupBy( f => f.Id )
.Select( g => new Foo { Id = g.Key, Value = g.Aggregate( 0, ( a, f ) => a + f.Value ) } )
.ToList();
List<Materiau> distinctList = getList().Distinct(EqualityComparer<Materiau>.Default).ToList();
Here is my scenario
List<object> obj = new List<object>();
obj.Add(new {id = 1, name = "Jakob"});
obj.Add(new {id = 2, name = "Sam"});
obj.Add(new {id = 3, name = "Albert"});
obj.Add(new {id = 1, name = "Jakob"});
How do you filter List<object> like these so it returns a List of users with name "Jakob"?
obj.Where(t => t.name == "Jakob") doesn't work
The best option you have is to declare a class that represents a user.
public class User
{
public int Id { get; set; }
public string Name { get; set; }
}
Then create a list of User objects and query this list.
var users = new List<User>
{
new User { Id = 1, Name = "Jakob" },
new User { Id = 2, Name = "Sam" },
new User { Id = 3, Name = "Albert" }
}
var filteredUsers = users.Where(user => user.Name == "Jakob");
Otherwise, you have to rely on the solution that Tengiz suggested.
If you convert your object to dynamic, it should work:
obj.Where(t => ((dynamic)t).name == "Jakob")
EDIT:
For completeness, I should mention couple of things:
Usage of dynamic type comes down to usage of object with reflection on top of it, so eventually you don't get something better than reflection if you use this approach.
Usage of dynamic at all involves loading necessary assemblies (a.k.a. DLR) into CLR, which would not load if you don't use dynamic at all. In other words, it's an overhead.
So, use with your own discretion.
Yet another alternative is create array of anonymous types and then convert it to list via ToList IEnumerable extension method:
var obj = (new[] {
new { id = 1, name = "Jakob" },
new { id = 2, name = "Sam" },
new { id = 3, name = "Albert" },
new { id = 1, name = "Jakob" }}).ToList();
obj.Where(c => c.name == "Jakob");
If you don't really need a list and array is fine too - just don't convert to list. Benefit is you got strongly typed list and not list of arbitrary objects.
You could use reflection
var l = new List<object>();
l.Add(new {key = "key1", v = "value1"});
l.Add(new {key = "key2", v = "value2", v2="another value"});
l.Add(new {key = "key3", v = "value3", v3= 4});
l.Add(new {key = "key4", v = "value4", v4 = 5.3});
var r = l.Where(x=> (string)x.GetType().GetProperty("key")?.GetValue(x) == "key1");
Get the type of your elements and find the property you are looking for. Then get the value for the current instance and compare it to the value you want to filter for.
But on the other hand, this approach has the advantage of working even if the List contains items of several different anonymous types (if they have different properties), as long as they all have the property you are filtering for.
EDIT
With c# 6 you can use the ? operator, which is sort of an inline check for null. Ie, if GetProperty() returns null because the property is not found, the expression returns null without executing GetValue() (which would otherwise throw a NullReferenceException)
I am using a recursive method to go through a tree of items and add its children to a flat collection:
public class Thing
{
public int Id { get; set; }
public string Name { get; set; }
public int? ParentId { get; set; }
}
void Main()
{
var sampleData = new List<Thing>
{
new Thing { Id = 1, Name = "root1", ParentId = null },
new Thing { Id = 2, Name = "2", ParentId = 1 },
new Thing { Id = 3, Name = "3", ParentId = 1 },
new Thing { Id = 4, Name = "4", ParentId = 2 },
new Thing { Id = 5, Name = "5", ParentId = 2 },
new Thing { Id = 6, Name = "6", ParentId = 2 },
new Thing { Id = 7, Name = "7", ParentId = 6 },
new Thing { Id = 8, Name = "8", ParentId = 7 },
new Thing { Id = 9, Name = "9", ParentId = 8 },
new Thing { Id = 10, Name = "10", ParentId = 9 },
new Thing { Id = 11, Name = "11", ParentId = 10 },
new Thing { Id = 12, Name = "12", ParentId = 11 },
new Thing { Id = 13, Name = "13", ParentId = 12 },
new Thing { Id = 14, Name = "14", ParentId = 13 },
new Thing { Id = 15, Name = "root15", ParentId = null }
};
var subThings = new HashSet<Thing>();
var stopWatch = new Stopwatch();
stopWatch.Start();
//AddSubThings(subThings, sampleData, new List<int> { 1 });
AddSubThingsUsingList(subThings, sampleData, new List<int> { 1 });
stopWatch.Elapsed.Dump();
subThings.Dump();
}
private void AddSubThings(HashSet<Thing> resultThings, IEnumerable<Thing> sourceThings, IEnumerable<int> parentIds)
{
if (!sourceThings.Any() || !parentIds.Any())
{
return;
}
var subThings = sourceThings.Where(st => st.ParentId.HasValue && parentIds.Contains(st.ParentId.Value));
resultThings.UnionWith(subThings);
AddSubThings(resultThings, sourceThings.Except(subThings), subThings.Select(st => st.Id));
}
private void AddSubThingsUsingList(HashSet<Thing> resultThings, List<Thing> sourceThings, List<int> parentIds)
{
if (!sourceThings.Any() || !parentIds.Any())
{
return;
}
var subThings = sourceThings.Where(st => st.ParentId.HasValue && parentIds.Contains(st.ParentId.Value));
resultThings.UnionWith(subThings);
AddSubThingsUsingList(resultThings, sourceThings.Except(subThings).ToList(), subThings.Select(st => st.Id).ToList());
}
When I use the AddSubThings method it takes around 90 seconds to process. However if I use the AddSubThingsUsingList method it does not even take a second. Why is this?
The problem is because your create subThings from sourceThings like this
var subThings = sourceThings.Where(
st => st.ParentId.HasValue && parentIds.Contains(st.ParentId.Value));
Then you pass the following as sourceThings to the recursive call.
sourceThings.Except(subThings)
Which is equivalent to
sourceThings.Except(
sourceThings.Where(
st => st.ParentId.HasValue && parentIds.Contains(st.ParentId.Value)))
That query when iterated with have to iterate over the original list twice. With each recursive call the query will build up and need to iterate the original list 2^n times where n is the recursion level. And your query is being iterated by the Any and the HashSet.UnionWith calls meaning it's more like 2^(n+1).
The other one immediately iterates the query before passing them and thus avoids this doubling problem.
You could pass the following to your recursive call for sourceThings instead to make it faster as it doesn't double up the required iterating of the original list on each recursive call.
sourceThings.Where(st => !st.ParentId.HasValue || !parentIds.Contains(st.ParentId.Value))
Ok. This is a bit complex.
Operations on IEnumerable are lazy, i.e. they are not executed until you need the result. Now when you pass sourceThins and subThings to AddSubThings, you've not sent a materialized collection of things, all you've done is you've defined how these collections are calculated from the original Lists.
Now when the method calls itself recursively, it adds more filtering and selection to the data it has received.
All these layers of selection and filtering will be called when you call Any().
On the other hand, in the case of Lists, things are materialized after calls to Where, Except and Select, because you call ToList.
parent category contains a list of sub categories and sub categories contains a list of sub categories. Now I want to fetch all parent categories if its status is true along with subcategories. I want to check the subcategories status also while fetching. My current query is
db.Categories.Where(x => x.Status)
.Include(x=>x.SubCategories)
. OfType<ParentCategory>().ToList();
how to check the status of subcategories in this query??
Try LINQ's SelectMany() method. It is used to flatten nested collections. Instead of using nested for loops, we can do the task in a more 'LINQ' way.
Ex given below -
Master m1 = new Master() { name = "A", lstObj = new List<obj> { new obj { i = 1, s = "C++" }, new obj { i = 1, s = "C#" }, new obj { i = 1, s = "Java" } } };
Master m2 = new Master() { name = "A", lstObj = new List<obj> { new obj { i = 4, s = "PHP" }, new obj { i = 5, s = "Ruby" }, new obj { i = 6, s = "Perl" } } };
List<Master> lstMasters = new List<Master> { m1, m2 };
var result = lstMasters.SelectMany(m => m.lstObj).Where(o => o.s == "PHP");
Just replace the Master class with your own master class.
I have a data table that contains a one-to-many self referential relationship:
Plant
{
ID,
Name,
ParentID
}
I'm trying to create a linq query that will tell me the total number of descendants stemming from one Plant.
Example:
I have 5 plants:
One {ID = 1, Name = Pine, Parent = null};// This is the root
Two {ID = 2, Name = Evergreen, Parent = 1}
Three {ID = 3, Name = Winter Evergreen, Parent = 2}
Four {ID = 4, Name = Christmas Tree, Parent = 1}
Five {ID = 5, Name = Maple, Parent = null};// This is the root
When I call my LINQ query with an input of ID = 1, I want it to return 3, because there are 3 descendants of One; Two, Three and Four. Five is not a decedent of One.
The only way I can think about doing this involves nested recursive linq queries on the inner results. I have no idea how to do this and I feel as though there is an easier way.
I'm using C# 4.0 and LINQ if that matters.
If you were using SQL Server, the query you would want constructed would be:
DECLARE #TargetElementId int
SET #TargetElementId = 1;
WITH Plants AS
(
SELECT ID, Name, ParentId
FROM PlantsTable
WHERE ParentId = #TargetElementId
UNION ALL
SELECT ID, Name, ParentId
FROM PlantsTable
INNER JOIN Plants ON PlantsTable.ParentId = Plants.ID
)
SELECT COUNT(ID) - 1 FROM Plants
I don't believe this can be done with LinqToSql, see Common Table Expression (CTE) in linq-to-sql?, which is a question of a similar nature.
EDIT adding code that supports LINQ to SQL thanks to #Kirk Woll comment.
class Program
{
private static Table<Plant> plants;
static void Main(string[] args)
{
using (var dataContext = new DataClasses1DataContext())
{
plants = dataContext.Plants;
var treesNodes = GetTreesNodes(plants.Where(plant => plant.ID == 1));
Console.WriteLine(string.Join(",",
treesNodes.Select(plant => plant.ID)));
}
}
public static IEnumerable<Plant> GetTreesNodes(IEnumerable<Plant> roots)
{
if(!roots.Any())
{
return roots;
}
var children = roots.SelectMany(root =>
plants.Where(plant => plant.Parent == root));
return roots.Union(GetTreesNodes(children));
}
}
Previous version that match LINQ to Objects:
This method can provide all the nodes in the tree:
public IEnumerable<Plant> GetTreesNodes(IEnumerable<Plant> roots)
{
if(!roots.Any())
{
return Enumerable.Empty<Plant>();
}
var rootsIds = roots.Select(root => root.ID);
var children = plants.Where(plant => rootsIds.Contains(plant.Parent));
return roots.Union(GetTreesNodes(children));
}
It can be used as in the following example:
[Test]
public void ExampleTest()
{
var One = new Plant() {ID = 1, Name = "Pine", Parent = 0};
var Two = new Plant() {ID = 2, Name = "Evergreen", Parent = 1};
var Three = new Plant() {ID = 3, Name = "Winter Evergreen", Parent = 2};
var Four = new Plant() {ID = 4, Name = "Christmas Tree", Parent = 1};
var Five = new Plant() {ID = 5, Name = "Maple", Parent = 0};
plants = new []{One,Two,Three,Four,Five};
var nestedElements = GetTreesNodes(new[]{One});
var numOfNestedElementsWithoutRoot = nestedElements.Count()-1;
Assert.That(numOfNestedElementsWithoutRoot, Is.EqualTo(3));
}
The code assumes there are no cyclic references.