'preventing the item to add to the dictionary
I want to create a hierarchical list in a 3 level category.
My data are in the following ways:
var appCategoryList = new List<AppCategoryDataModel>() {
new AppCategoryDataModel() { AppCategoryId = 4844, ParentappCategoryId = null , AppCategoryName = "ItemResearch", ProtocolId = 5164, ProtocolTitle="ABC: Evaluation" },
new AppCategoryDataModel() { AppCategoryId = 4844, ParentappCategoryId = null, AppCategoryName = "ItemResearch", ProtocolId = null, ProtocolTitle=""},
new AppCategoryDataModel() { AppCategoryId = 4845, ParentappCategoryId = null, AppCategoryName = "ItemHostpital", ProtocolId = null, ProtocolTitle="" },
new AppCategoryDataModel() { AppCategoryId = 4845, ParentappCategoryId = null, AppCategoryName = "ItemHostpital", ProtocolId =5162 , ProtocolTitle="ABC: 3/28/20" },
new AppCategoryDataModel() { AppCategoryId = 4845, ParentappCategoryId = null, AppCategoryName = "ItemHostpital", ProtocolId =5164 , ProtocolTitle="ABC: Evaluation" },
new AppCategoryDataModel() { AppCategoryId = 4845, ParentappCategoryId = null, AppCategoryName = "ItemHostpital", ProtocolId =5165 , ProtocolTitle="ABC: section", },
new AppCategoryDataModel() { AppCategoryId = 4845, ParentappCategoryId = null, AppCategoryName = "ItemHostpital", ProtocolId =5192 , ProtocolTitle="ABC: 3/31/20", },
new AppCategoryDataModel() { AppCategoryId = 4846, ParentappCategoryId = null, AppCategoryName = "Contact information", ProtocolId = null, ProtocolTitle="", },
new AppCategoryDataModel() { AppCategoryId = 4846, ParentappCategoryId = null, AppCategoryName = "Contact information", ProtocolId =5164 , ProtocolTitle="ABC: Evaluation", },
new AppCategoryDataModel() { AppCategoryId = 4852, ParentappCategoryId = null, AppCategoryName = "UP", ProtocolId =null , ProtocolTitle="", },
new AppCategoryDataModel() { AppCategoryId = 4852, ParentappCategoryId = null, AppCategoryName = "UP", ProtocolId = 5164, ProtocolTitle="ABC: Evaluation", },
new AppCategoryDataModel() { AppCategoryId = 5023, ParentappCategoryId = null, AppCategoryName = "Call survival guide", ProtocolId = null, ProtocolTitle="" },
new AppCategoryDataModel() { AppCategoryId = 5023, ParentappCategoryId = null, AppCategoryName = "Call survival guides", ProtocolId =5164 , ProtocolTitle="ABC: Evaluation" },
new AppCategoryDataModel() { AppCategoryId = 5085, ParentappCategoryId = null, AppCategoryName = "OE", ProtocolId =null, ProtocolTitle="" },
new AppCategoryDataModel() { AppCategoryId = 5085, ParentappCategoryId = null, AppCategoryName = "OE", ProtocolId =5164 , ProtocolTitle="ABC: Evaluation" },
new AppCategoryDataModel() { AppCategoryId = 5099, ParentappCategoryId = null, AppCategoryName = "OEM", ProtocolId =null , ProtocolTitle=null },
new AppCategoryDataModel() { AppCategoryId = 5334, ParentappCategoryId = 5085, AppCategoryName = "mmkk update", ProtocolId =null , ProtocolTitle= null },
new AppCategoryDataModel() { AppCategoryId = 5336, ParentappCategoryId = 5085, AppCategoryName = "xdgdrg", ProtocolId =null , ProtocolTitle=null },
new AppCategoryDataModel() { AppCategoryId = 5348, ParentappCategoryId = 5023, AppCategoryName = "test", ProtocolId =null , ProtocolTitle=null },
new AppCategoryDataModel() { AppCategoryId = 5341, ParentappCategoryId = 5023, AppCategoryName = "New Category Level-1", ProtocolId =null , ProtocolTitle= null },
new AppCategoryDataModel() { AppCategoryId = 5349, ParentappCategoryId = 5341, AppCategoryName = "New Category Level-2", ProtocolId =null , ProtocolTitle= null },
new AppCategoryDataModel() { AppCategoryId = 5352, ParentappCategoryId = 5348, AppCategoryName = "New category3", ProtocolId =null , ProtocolTitle= null },
}.OrderBy(ap => ap.AppCategoryName);
AppCategoryDataModel
public class AppCategoryDataModel
{
public int AppCategoryId { get; set; }
public int? ParentappCategoryId { get; set; }
public string AppCategoryName { get; set; }
public int? ProtocolId { get; set; }
public string ProtocolTitle { get; set; }
}
Dictionary<int, FirstLevelCategory> dictCategories = new Dictionary<int, FirstLevelCategory>();
foreach (AppCategoryDataModel category in appCategoryList)
{
if (!dictCategories.ContainsKey(category.AppCategoryId))
{
dictCategories.Add(category.AppCategoryId, new FirstLevelCategory()
{
AppCategoryId = category.AppCategoryId,
AppCategoryName = category.AppCategoryName,
AppCategoryProtocolData = category.ProtocolId != null ?
new List<ProtocolDetails>() {
new ProtocolDetails()
{
ProtocolId = (int)category.ProtocolId,
ProtocolTitle = category.ProtocolTitle
}
} : new List<ProtocolDetails>(),
SubCategory = category.ParentappCategoryId != null
? GetSubCategoryList(appCategoryList, category.AppCategoryId)
: new List<SecondLevelCategory>()
});
}
else
{
if (category.ProtocolId != null)
dictCategories[category.AppCategoryId].AppCategoryProtocolData.Add(new ProtocolDetails()
{
ProtocolId = (int)category.ProtocolId,
ProtocolTitle = category.ProtocolTitle
});
}
}
foreach (var key in dictCategories.Keys.ToList())
{
dictCategories[key].CategoryProtocolCount = dictCategories[key].AppCategoryProtocolData.Count();
}
FirstLevelCategory
public class FirstLevelCategory
{
public int AppCategoryId { get; set; }
public string AppCategoryName { get; set; }
public bool IsExpand { get; set; }
public bool IsExpandAll { get; set; }
public bool IsCategoryHierarchyExpanded { get; set; }
//count of AppCategoryProtocolData
public int CategoryProtocolCount { get; set; }
public List<SecondLevelCategory> SubCategory { get; set; }
public List<ProtocolDetails> AppCategoryProtocolData { get; set; }
}
public class ProtocolDetails
{
public int ProtocolId { get; set; }
public string ProtocolTitle { get; set; }
}
My problem here is the item where ParentappCategoryId != null should come as a sub-category, but that has also been added in the first level category. I am not able to filter that properly.
How should I prevent the item that has ParentAppCategory to be added in the first.
SecondLevelCategory
public class SecondLevelCategory
{
public int AppCategoryId { get; set; }
public string AppCategoryName { get; set; }
public bool? ToggleSubCategory { get; set; }
public bool IsExpand { get; set; }
public int subcategoryProtocolCount { get; set; }
public IEnumerable<ProtocolDetails> SubCategoryProtocolData { get; set; }
public List<ThirdLevelCategory> ThirdLevelCategory { get; set; }
}
ThirdLevelCategory
public class ThirdLevelCategory
{
public int AppCategoryId { get; set; }
public string AppCategoryName { get; set; }
public bool? ToggleThirdLevelCategory { get; set; }
public int? ThirdLevelCategoryProtocolCount { get; set; }
public IEnumerable<ProtocolDetails> ThirdLevelCategoryProtocolData { get; set; }
}
Expected Result:
ItemResearch
ABC: Evaluation
ItemHostpital
ABC: 3/28/20
ABC: Evaluation
ABC: section
ABC: 3/31/20
Contact information
ABC: Evaluation
UP
ABC: Evaluation
OE
ABC: Evaluation
mmkk update --subcategory
xdgdrg --subcategory
Call survival guides
ABC: Evaluation --details
test --subcategory
New category3 --thirdcategory
New Category Level-1 --subcategory
New Category Level-2 -- thirdcategory
Solution 1:[1]
My problem here is the item where ParentappCategoryId != null should come as a sub-category, but that has also been added in the first level category. I am not able to filter that properly.
I interpret this as I want only items with ParentappCategoryId == null in the first level of my dictionary.
If that is the case, it should be possible to solve by filtering the app category list before iterating through it to add the first-level items to your dictionary.
Here is an example of how that can be achieved using .Where() from the System.Linq namespace:
Replace
foreach (AppCategoryDataModel category in appCategoryList)
with
//using System.Linq;
var firstLevelItems = appCategoryList.Where(app => app.ParentappCategoryId == null);
foreach (AppCategoryDataModel category in firstLevelItems)
Note: A separate variable is not needed; I would mainly consider using it for readability.
You might as well implement it as follows:
//using System.Linq;
foreach (AppCategoryDataModel category in appCategoryList.Where(app => app.ParentappCategoryId == null))
Also -- I am not sure if this part causes a problem for you, but the logic looks strange to me:
SubCategory = category.ParentappCategoryId != null
? GetSubCategoryList(appCategoryList, category.AppCategoryId)
: new List<SecondLevelCategory>()
I interpret the code as:
Populate
category'sSubCategoryonly ifcategoryhas a parent
To me, given your condition for the first-level items of not having a parent, this seems to imply that none of the first-level items should have any sub categories. I find this to be contradictive. Please correct me if I'm wrong.
If I am right, I'd suggest implementing GetSubCategoryList() so that it returns an empty list if none of the items in appCategoryList has a ParentappCategoryId == category.AppCategoryId.
If you have that, you can simply go with
SubCategory = GetSubCategoryList(appCategoryList, category.AppCategoryId)
Update: Possible implementation of GetSubCategoryList (GetSubCategories)
You mention in your comment that your current implementation of GetSubCategoryList (and GetThirdLevelCategoryList) pretty much repeats the implementation of your main foreach loop.
To my understanding, this indicates that the classes FirstLevelCategory and SecondLevelCategory (and ThirdLevelCategory) could be replaced by a single class: AppCategory.
public class AppCategory
{
public int Id { get; set; }
public string Name { get; set; }
public int ProtocolCount { get; set; }
public List<AppCategory> SubCategories { get; set; }
public List<ProtocolDetails> ProtocolData { get; set; }
}
Furthermore, the logic for populating AppCategoryProtocolData could be simplified by grouping all the app categories that share (AppCategory)Id and creating a list of ProtocolDetails based on that grouping.
From here, we can make use of recursion to get the sub categories of each category.
private static List<AppCategory> GetSubCategories(
List<AppCategoryDataModel> appCategories,
int? parentAppCategoryId)
{
// return empty list if no sub categories exist
if (!appCategories.Any(ac => ac.ParentAppCategoryId == parentAppCategoryId))
{
return new();
}
List<AppCategory> subCategories = appCategories
.Where(ac => ac.ParentAppCategoryId == parentAppCategoryId)
// Simplify the logic of populating the ProtocolData content
.GroupBy(ac => ac.AppCategoryId)
.Select(gr => new AppCategory
{
Id = gr.Key,
Name = gr.First().AppCategoryName,
ProtocolData = gr
.Where(ac => ac.ProtocolId != null)
.Select(ac => new ProtocolDetails
{
Id = ac.ProtocolId.Value,
Title = ac.ProtocolTitle
})
.ToList()
})
.ToList();
foreach (var subCat in subCategories)
{
// Make recursive method call to populate _this_ category's sub categories
subCat.SubCategories = GetSubCategories(appCategories, subCat.Id);
subCat.ProtocolCount = subCat.ProtocolData.Count;
}
return subCategories;
}
As it turns out, all the first-level items in your dictionary may in fact be considered as being sub categories of a (non-existing) category with Id == null. We can take advantage of this observation by finding our first-level items using our implementation of GetSubCategories.
The main code block may be reduced to:
Dictionary<int, AppCategory> dictCategories = new();
var firstLevelCategories = GetSubCategories(appCategoryList, null);
foreach (var category in firstLevelCategories)
{
dictCategories.Add(category.Id, category);
}
The resulting hierarchy, using your example input data, looks as follows:
Call survival guide
ABC: Evaluation
New Category Level-1
New Category Level-2
test
New category3
Contact information
ABC: Evaluation
ItemHostpital
ABC: 3/28/20
ABC: Evaluation
ABC: section
ABC: 3/31/20
ItemResearch
ABC: Evaluation
OE
ABC: Evaluation
mmkk update
xdgdrg
OEM
UP
ABC: Evaluation
Note that this result includes OEM, which is not included in your expected result. If you only want first-level items in your dictionary that contain ProtocolData, you could e.g. filter as follows:
foreach (var category in firstLevelCategories.Where(c => c.ProtocolCount > 0))
{
dictCategories.Add(category.Id, category);
}
Example fiddle here.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
