Reputation: 35
I have something working in plain simpel loops but I want to know how to do it with LINQ, if possible.
I don't even know how to describe what i want to do. But stack overflow wants me to do so in words instead of with an example, so this is me tricking it...
The classes (stripped down)
public class Product
{
public int ProductId { get; set; }
public string Name { get; set; }
public List<CategoryGroup> CategoryGroups { get; set; }
}
public class CategoryGroup
{
public int CategoryGroupId { get; set; }
public string Name { get; set; }
public List<Category> Categories { get; set; }
}
public class Category
{
public int CategoryId { get; set; }
public string Name { get; set; }
}
What I want to do
From
Product 1
CategoryGroup 1
Category 11
Category 12
Category 13
CategoryGroup 2
Category 21
Category 22
Product 2
Category Group 1
Category 11
Category 14
Category Group 2
Category 21
Category Group 3
Category 31
To
Category Group 1
Category 11
Category 12
Category 13
Category 14
Category Group 2
Category 21
Category 22
Category Group 3
Category 31
The working code
var categoryGroups = new List<CategoryGroup>();
foreach (var product in products)
{
foreach (var categoryGroup in product.CategoryGroups)
{
var group = categoryGroups.SingleOrDefault(cg => cg.CategoryGroupId == categoryGroup.CategoryGroupId);
if (group == null)
{
group = new CategoryGroup {
Categories = new List<Category>(),
CategoryGroupId = categoryGroup.CategoryGroupId,
Name = categoryGroup.Name
};
categoryGroups.Add(group);
}
foreach (var category in categoryGroup.Categories)
{
if (group.Categories.Any(c => c.CategoryId == category.CategoryId))
{
continue;
}
group.Categories.Add(category);
}
}
}
Upvotes: 1
Views: 87
Reputation: 171
Here is the query to get all the different category groups
var categoryGroups = products.SelectMany(g => g.CategoryGroups)
.GroupBy(x => x.CategoryGroupId)
.Select(y => new { CategoryGroupId = y.Key,
Categories = y.SelectMany(category => category.Categories).Distinct().ToList() });
To eliminate duplicate categories you will have to modify your Category object to implement the IEquatable interface as follows
public class Category : IEquatable<Category>
{
public int CategoryId { get; set; }
public string Name { get; set; }
public bool Equals(Category other)
{
//Check whether the compared object is null.
if (Object.ReferenceEquals(other, null)) return false;
//Check whether the compared object references the same data.
if (Object.ReferenceEquals(this, other)) return true;
//Check whether the products' properties are equal.
return CategoryId.Equals(other.CategoryId) && Name.Equals(other.Name);
}
// If Equals() returns true for a pair of objects
// then GetHashCode() must return the same value for these objects.
public override int GetHashCode()
{
//Get hash code for the Name field if it is not null.
int hashProductName = Name == null ? 0 : Name.GetHashCode();
//Get hash code for the Code field.
int hashProductCode = CategoryId.GetHashCode();
//Calculate the hash code for the product.
return hashProductName ^ hashProductCode;
}
}
Implementing the interface allows the Distinct() call in LINQ to compare the list of objects and eliminate duplicates.
Hope that helps.
Upvotes: 0
Reputation: 7526
You can do many subselections through SelectMany:
var result = products
.SelectMany(x => x.CategoryGroups)
.GroupBy(x => x.CategoryGroupId)
.Select(x => new CategoryGroup
{
Categories = x.SelectMany(y => y.Categories)
.Distinct(y => y.CategoryId)
.OrderBy(y => y.CategoryId)
.ToList(),
CategoryGroupId = x.Key,
Name = x.Values.First().Name
})
.OrderBy(x => x.CategoryGroupId)
.ToList();
Upvotes: 3