Nested foreach for self referencing table - c#

i am struggling to find a different approach for this code.
I want to create a dropdown list to select a category.
As you can see it is not clean and does not work for multiple levels.
I am not an expert in .NET and would like to learn how a professional does this.
List<SelectListItem> list = new List<SelectListItem>();
foreach (Category item in db.CategorySet.Where(x => x.ParentCategory == null))
{
list.Add(new SelectListItem { Value = item.Id.ToString(), Text = item.Name });
foreach (Category subitem in item.SubCategories)
{
list.Add(new SelectListItem { Value = subitem.Id.ToString(), Text = " - " + subitem.Name });
foreach (Category subsubitem in subitem.SubCategories)
{
list.Add(new SelectListItem { Value = subsubitem.Id.ToString(), Text = " - - " + subsubitem.Name });
foreach (Category subsubsubitem in subsubitem.SubCategories)
{
list.Add(new SelectListItem { Value = subsubsubitem.Id.ToString(), Text = " - - - " + subsubsubitem.Name });
//...
}
}
}
}
public partial class Category
{
public Category()
{
this.Products = new HashSet<Product>();
this.SubCategories = new HashSet<Category>();
}
public int Id { get; set; }
public string Name { get; set; }
public string Icon { get; set; }
public Nullable<int> ParentCategoryId { get; set; }
public virtual ICollection<Product> Products { get; set; }
public virtual ICollection<Category> SubCategories { get; set; }
public virtual Category ParentCategory { get; set; }
}
Thank you in advance...

Seems like you are making hierarchial tree (using the "-", "- -", and so on).
Assuming your Categories is non-cyclic, you should consider using recursive function to solve your issue, passing your list as well as your printed prefix (the "-") or your "depth" on the recursive search.
Something like below would probably serve:
public void addCatToList(List<SelectedItemList> list, int depth, IEnumerable<Category> cats){
foreach (Category item in cats)
{
list.Add(new SelectListItem { Value = item .Id.ToString(), Text = printDash(depth) + item.Name });
addCatToList(list, depth + 1, item.SubCategories);
}
}
private string printDash(int number){
string dash = string.Empty;
for(int i = 0; i < number; ++i){
if (i == 0)
dash += " ";
dash += "- ";
}
return dash;
}
And then you call it the first time with depth = 0:
List<SelectListItem> list = new List<SelectListItem>();
addCatToList(list, 0, db.CategorySet.Where(x => x.ParentCategory == null));

Related

How to read property names on related list

With these models:
public class Course
{
public Guid Id { get; set; }
public string Title { get; set; }
public IList<CourseEvent> CourseEvents { get; set; }
}
public class CourseEvent
{
public Guid Id { get; set; }
public Guid CourseId { get; set; }
public DateTime StartDate { get; set; }
}
...and this method:
private void WritePropertyNames(object obj)
{
foreach (var item in obj.GetType().GetProperties())
{
Console.WriteLine(item.Name + " => " + item.GetValue(obj, null));
}
}
...I can get the property names and values on any object provided:
var course = new Course { Title = "My course", CourseEvents = new List<CourseEvent>() { new() { StartDate = DateTime.Today }, new() { StartDate = DateTime.Today.AddDays(-1) } } };
WritePropertyNames(course);
But how do I get the related CourseEvents property names and values too? I've tried this, but that's not right:
foreach (var item2 in obj.GetType().GetProperty("CourseEvents").GetType().GetProperties())
{
Console.WriteLine(item2.Name + " => " + item2.GetValue(obj, null));
}
It might be ugly and not optimal and won't work for all the cases (quickly wrote it just to demonstrate the idea)
So you could check if property is Generic by item.PropertyType.IsGenericType, you could also check if it type implements IEnumerable typeof(IEnumerable).IsAssignableFrom(item.PropertyType). Then you could get the underlying type using item.PropertyType.GenericTypeArguments[0]. Then it basically becomes your logic that you have - you iterating over objects and getting props as you already have
private static void WritePropertyNames(object obj)
{
foreach (var item in obj.GetType().GetProperties())
{
if(item.PropertyType.IsGenericType && typeof(IEnumerable).IsAssignableFrom(item.PropertyType))
{
var innerProps = item.PropertyType.GenericTypeArguments[0].GetProperties();
foreach(var innerItem in item.GetValue(obj, null) as IEnumerable)
{
foreach(var innerProp in innerProps)
{
Console.WriteLine(item.Name + " => " + innerProp.Name + " => " + innerProp.GetValue(innerItem, null));
}
}
}
else
{
Console.WriteLine(item.Name + " => " + item.GetValue(obj, null));
}
}
}

I need to create List<a> include List<t> with in it. Anyone can help for this

I have a set of data that have duplication because of joining to another table. I need to remove the duplicate and add to another List and each of that element include a list of some element.
As example :
I need to insert this data to the list: List<ExistingQuestionDTO> existingQuestions = new List<ExistingQuestionDTO>();
public class ExistingQuestionDTO
{
public int Id { get; set; }
public int QuestionId { get; set; }
public string QuestionTitle { get; set; }
public string SurveyTitle { get; set; }
public int SurveyId { get; set; }
public string OptionType { get; set; }
public IEnumerable<Tag> Tags { get; set; }
}
I took this data to below List:IEnumerable<ExistingQuestionSpResult> existingQuestionSpResults
and try to make an list using below algorithm. But it wont give an expected result.
private IEnumerable<ExistingQuestionDTO> MakeExistingQuestionSearchableDTO(
IEnumerable<ExistingQuestionSpResult> existingQuestionSpResults)
{
int previousQuestionId = 0;
List<Tag> exitingTags = new List<Tag>();
List<Tag> newTags = new List<Tag>();
List<ExistingQuestionDTO> existingQuestions = new List<ExistingQuestionDTO>();
ExistingQuestionDTO previousExistingQuestionDTO = new ExistingQuestionDTO();
foreach (var questionSpResult in existingQuestionSpResults)
{
if (questionSpResult.QuestionId == previousQuestionId ||
previousQuestionId == 0)
{
//Adding new tag if questionId exist for existing tag
if (!(newTags.Count == 0))
{
exitingTags.AddRange(newTags);
//Clear newTags array here...
newTags.Clear();
}
//Add Tags for same array.
if (!(questionSpResult.ColumnName == "NULL"))
{
Tag tag = new Tag
{
TagId = (Guid)questionSpResult.TagId,
TagName = questionSpResult.ColumnName
};
exitingTags.Add(tag);
}
}
else
{
//Add Tags for new array with Other Items too...
exitingTags.Clear();
newTags.Clear();
if (!(questionSpResult.ColumnName == "NULL"))
{
Tag tag = new Tag
{
TagId = (Guid)questionSpResult.TagId,
TagName = questionSpResult.ColumnName
};
newTags.Add(tag);
}
}
ExistingQuestionDTO existingQuestionDTO = new ExistingQuestionDTO
{
Id = questionSpResult.Id,
QuestionId = questionSpResult.QuestionId,
QuestionTitle = questionSpResult.QuestionTitle,
SurveyId = questionSpResult.SurveyId,
SurveyTitle = questionSpResult.SurveyTitle,
OptionType = questionSpResult.OptionType,
Tags = exitingTags.Count != 0 ? exitingTags : newTags
};
if (questionSpResult.QuestionId == previousQuestionId)
{
//Update Tag in relevant node...
//existingQuestions.RemoveAt((int)questionSpResult.QuestionId - 1);
//existingQuestions.Remove(previousExistingQuestionDTO);
//existingQuestions.Add(existingQuestionDTO);
//existingQuestions.Insert(((int)questionSpResult.QuestionId - 1),
// existingQuestionDTO);
var foundQuestion = existingQuestions.Find(a =>
a.QuestionId == questionSpResult.QuestionId);
foundQuestion.Tags = exitingTags;
existingQuestions[(int)questionSpResult.QuestionId - 1] = foundQuestion;
}
else
{
existingQuestions.Add(existingQuestionDTO);
}
previousQuestionId = questionSpResult.QuestionId;
previousExistingQuestionDTO = existingQuestionDTO;
}
IEnumerable<ExistingQuestionDTO> exitingQuestionList = existingQuestions;
return exitingQuestionList;
}
Can somebody show me what went wrong here ?
QuestionId 3 should have Tags.count => 2. But for here it gives 17. I am so confused.
Since redundancy reason is not mentioned properly and row-wise its completely unique except for questionid 3 where the status has both critical and important. So, first, you have to add a filter for the same and see if you are getting completely unique results or not.

Cannot convert type list.<string> to string error

I have a controller method that looks like this
//cycles through sites in order to populate variables
foreach (Site s in sites)
{
foreach (OffSiteItemDetails d in s.ItemDetails)
{
if (d.itemID != null)
{
osiItemCost[s.ID] = d.qty * db.Items.Where(x => x.ID == d.itemID).FirstOrDefault().cost(1, false, false);
osiLoItemCost[s.ID] += d.qty * db.Items.Where(x => x.ID == d.itemID).FirstOrDefault().cost(1, false, true);
osiItemLastCost[s.ID] += db.Items.Where(x => x.ID == d.itemID).FirstOrDefault().cost(d.qty, true, false);
}
}
}
o it generates a value for each variable for example osiItemCost[s.ID] could equal 0 for one site ID but it could equal 70 for another Site
So I am trying to create a table for each site ID. Currently if I leave it like
model.OffReportColumns = new List()
It will just keep getting overwritten by the next site Id in the list. So I am trying to assign the list to the sites ID.
foreach (Site s in sites)
{
foreach (OffSiteItemDetails d in s.ItemDetails)
{
model.OffReportColumns[s.ID] = new List<string>()
{
s.Name,
"",
"",
"Average Cost",
"",
"",
"Average Cost (With labour)"
};
Here is my Model class
public class SummaryReportModel
{
public string Title { get; set; }
public string ReportTitle { get; set; }
public string OffReportTitle { get; set; }
public List<string> ValuationColumns { get; set; }
public List<string> OffReportColumns { get; set; }
public List<List<string>> ValuationRows { get; set; }
public List<List<string>> OffReportRows { get; set; }
public List<List<string>> Total { get; set; }
public List<List<string>> OffReporTotal { get; set; }
public List<List<string>> Tital { get; set; }
public List<List<string>> SecondTital { get; set; }
public List<List<string>> osiGrandTotal { get; set; }
}
Where s.ID represents a "site ID"
Currently I receive the errors
Cannot implicitly convert type
'systems.collections.generic.List' to 'string'
However, it works when I remove the [s.ID] so that it looks like this
model.OffReportColumns = new List<string>()
Why is the s.ID causing an issue?
When you use model.OffReportColumns[s.ID] you are referencing the individual string with an index of s.ID. When you use model.OffReportColumns you are referencing the entire list.
You cannot set model.OffReportColumns[s.ID] equal to new List<string>() because a List<string> is not a string
Is not really clear what you are trying to do here. It seems that OffReportColumns is a list of strings, and you are trying to add a list a string to a certain index of it.
If you do this
model.OffReportColumns.AddRange(new List<String>(){
s.Name,
"string test",
"etc."
})
You will add all items of your new list of strings to the end of OffReportColumns.

Copy value of matching id

First, read the Class code below. There you will find a property called- CommonId which is common in Item class and ItemGallery and both has matching int value. Now check the Program class which is the main console program. Here I am adding some data to both classes to make an example. On the bottom of main program class, I am trying to loop through each Item and find its commonId matching with ItemGallery commonId if that commonId matched then in ItemGallery ItemId will be copied from its matching Item Id. The main goal is- Just take the copy from Item class Id to ItemGallery ItemId which has matching commonId. How to do that? I already tried foreach like bellow but this not the correct way.
Main Program Class:
class Program {
static void Main(string[] args) {
List<Item> MyItemList = new List<Item>();
MyItemList.Add(new Item {
CommonId = 502,
Id = 3,
Link = "some string1"
});
MyItemList.Add(new Item {
CommonId = 502,
Id = 4,
Link = "some string2"
});
MyItemList.Add(new Item {
CommonId = 502,
Id = 5,
Link = "some string3"
});
MyItemList.Add(new Item {
CommonId = 506,
Id = 6,
Link = "some string4"
});
List<ItemGallery> MyitemGalleries = new List<ItemGallery>();
MyitemGalleries.Add(new ItemGallery {
CommonId = 502,
Link = "",
});
MyitemGalleries.Add(new ItemGallery {
CommonId = 502,
Link = "",
});
MyitemGalleries.Add(new ItemGallery {
CommonId = 502,
Link = "",
});
foreach (var _MyItemList in MyItemList) {
MyitemGalleries.FirstOrDefault().ItemId = _MyItemList.Where(x => x.CommonId == MyitemGalleries.CommonId).FirstOrDefault().Id;
}
Console.ReadKey();
}
}
Class:
class Item {
public int Id { get; set; }//this id need to set to ItemGallery ItemId matching their CommonId
public int CommonId { get; set; }
public string Link { get; set; }
}
class ItemGallery {
public int ItemId { get; set; }
public int CommonId { get; set; }
public string Link { get; set; }
}
If I understand you and disregarding any other problems, there are few ways to do this. However, a simple foreach and FirstOrDefault should do
foreach (var gallery in MyitemGalleries)
{
var item = _MyItemList.FirstOrDefault(x => x.CommonId == gallery.CommonId);
// note if there are none we choose not to do anything, or grab the first
if(item == null)
continue;
gallery.ItemId = item.Id;
}

Convert list of items to list of tree nodes that have children

I have the following class
public class Item
{
public int Id { get; set; }
public int? ParentId { get; set; }
public string Text { get; set; }
}
And the following class for tree view
public class TreeViewModel
{
public TreeViewModel()
{
this.Children = new List<TreeViewModel>();
}
public int Id { get; set; }
public int NodeId { get; set; }
public string Text { get; set; }
public bool Expanded { get; set; }
public bool Checked { get; set; }
public bool HasChildren
{
get { return Children.Any(); }
}
public IList<TreeViewModel> Children { get; private set; }
}
I will receive list of items and I would to convert it to tree.
the item that not have parent id it will the main node.
example: if i have the following items
item[0] = Id:0 Text:User ParentId:3
item[1] = Id:1 Text:Role ParentId:3
item[2] = Id:2 Text:SubUser ParentId:0
item[3] = Id:3 Text:Admin ParentId:null
item[4] = Id:4 Text:SuperAdmin ParentId:null
item[5] = Id:5 Text:Doha ParentId:4
the following item it will list of tree
I tried to make recursive function to do that , but i have no result
You don't need a recursive function to do this:
var models = items.Select(i => new TreeViewModel
{
Id = i.Id,
...
}).ToList();
foreach (var model in models){
model.Children.AddRange(models.Where(m => m.ParentId == model.Id));
}
If you then want to get the roots of your tree, you can use:
var roots = models.Where(m => !m.ParentId.HasValue);
Here is a fast O(N) time complexity method of doing that:
List<Item> list = ...;
// Pre create all nodes and build map by Id for fast lookup
var nodeById = list
.Select(item => new TreeViewModel { Id = item.Id, Text = item.Text })
.ToDictionary(item => item.Id);
// Build hierarchy
var tree = new List<TreeViewModel>();
foreach (var item in list)
{
var nodeList = item.ParentId == null ? tree, nodeById[item.ParentId.Value].Children;
nodeList.Add(nodeById[item.Id]);
}

Categories

Resources