I have a dataset: List<school> schools. I want to print the data in a form of output tree.
How to group the values by id, to display in this format:
-Department
--Education
---District
----Public Schools
-----School1
-----School4
----Private Schools
-----College5
-----College3
-----College2
List<school> Schools = new List<school>()
{
new school() { id = 1, Description = "Department", ParentID = null},
new school() { id = 2, Description = "Education", ParentID = 1},
new school() { id = 3, Description = "District", ParentID = 2},
new school() { id = 4, Description = "Public Schools", ParentID = 3},
new school() { id = 5, Description = "College2", ParentID = 6},
new school() { id = 6, Description = "Private Schools", ParentID = 3},
new school() { id = 7, Description = "School4", ParentID = 4},
new school() { id = 8, Description = "College5", ParentID = 6},
new school() { id = 9, Description = "School1", ParentID = 4},
new school() { id = 9, Description = "College3", ParentID = 6}
};
foreach(var _school in Schools)
{
if(_school.ParentID != null)
{
for (int i = 0; i < Schools.Count; i++)
{
IEnumerable<school> query = Schools.Where(s => s.ParentID + i == s.ID);
var dash = "-";
foreach (var school in query)
{
dash += dash;
Console.WriteLine( dash + t.Description);
}
};
}
else
{
Console.WriteLine("-" + _school.Description);
};
};
You can do something like this, using a recursive method:
In your main function:
var root = schools.SingleOrDefault(x => x.ParentID == null);
if(root != null) {
PrintSub(schools, root, 1);
}
And a (static) recursive function:
private static void PrintSub(List<School> schools, School current, int level)
{
Console.WriteLine(new string('-', level) + current.Description);
var children = schools.Where(x => x.ParentID == current.id);
++level;
foreach (var school in children)
{
PrintSub(schools, school, level);
}
}
With your data structure algorithm must be like next:
public void Print(List<school> nodes, int? parentId, string prefix)
{
var nodesToPrint = nodes.Where(x => x.ParentID == parentId);
foreach (var item in nodesToPrint)
{
Console.WriteLine(prefix + item.Description);
Print(nodes, item.id, prefix + prefix[0]);
}
}
And print it from code as:
Print(Schools, null, "-");
But I want to suggest you to store your tree as logical tree.
For example:
public class school
{
public string Description { get; set; }
public List<school> Childs { get; set; }
}
with printing method inside this class:
public void Print(string prefix = string.Empty)
{
Console.WriteLine(prefix + this.Description);
if (this.Childs != null)
{
foreach (var item in this.Childs)
{
item.Print(prefix + "-");
}
}
}
So if you will have only one parent item, you could print whole tree like this:
var sch = //your method to get schools
sch.Print();
Related
I have multiple items (List)
I need to get distinct features for the items but the issue is that each item has two features. So both features need to match on GroupTypeId and GroupId to become a distinct group.
I need to group items that have the same features (per above distinct groups found). I don't need features here again at the item level since I will have these per above in a separate object.
I need to keep items order intact, the first item will go in group 1, then second might go in group 1 or group 2 and so on.
Also, each item in group, item number needs to be overwritten per the new sequence in that group.
Can i do above tasks purely with LINQ rather than using nested loops?
In the below sample for items
i have 3 distinct feature groups
and 3 item groups. Item 2 and 4 needs to be grouped together and the line no needs to change to 1 and 2. For item 1 and 3, line number should become as 1.
Need to add to List whose count will be 3
Index[0] will have 2 features and 1 item
Index[1] will have 2 features and 2 items
Index[2] will have 2 features and 1 item
public class ItemPicked
{
public int Id { get; set; }
public string Description { get; set; }
public int LineNumber { get; set; }
public int PartId { get; set; }
public List<ItemFeature> Features { get; set; }
}
public class ItemFeature
{
public string OriginalReceived { get; set; }
public Group Feature { get; set; }
}
public class Group
{
public int Id { get; set; }
public string GroupTypeId { get; set; }
public string GroupId { get; set; }
public int SequenceNo { get; set; }
}
public class PickedGrouping
{
public List<ItemFeature> Features { get; set; }
public List<ItemPicked> Items { get; set; }
}
var SampleFeatures1 = new List<ItemFeature>() {
new ItemFeature {
OriginalReceived = "SomeThing",
Feature = new Group() {
Id = 1, SequenceNo = 1, GroupTypeId = "A", GroupId = "B"
}
},
new ItemFeature {
OriginalReceived = "SomeThing2",
Feature = new Group() {
Id = 2, SequenceNo = 2, GroupTypeId = "Y", GroupId = "Z"
}
}
};
var SampleFeatures2 = new List<ItemFeature>() {
new ItemFeature {
OriginalReceived = "SomeThing3",
Feature = new Group() {
Id = 3, SequenceNo = 3, GroupTypeId = "C", GroupId = "D"
}
},
new ItemFeature {
OriginalReceived = "SomeThing4",
Feature = new Group() {
Id = 4, SequenceNo = 4, GroupTypeId = "X", GroupId = "Y"
}
}
};
var SampleFeatures3 = new List<ItemFeature>() {
new ItemFeature {
OriginalReceived = "SomeThing5",
Feature = new Group() {
Id = 3, SequenceNo = 3, GroupTypeId = "C", GroupId = "D"
}
},
new ItemFeature {
OriginalReceived = "SomeThing5",
Feature = new Group() {
Id = 2, SequenceNo = 2, GroupTypeId = "M", GroupId = "K"
}
}
};
var items = new List<ItemPicked>(){
new ItemPicked{
Id = 1, Description = "Item 1", LineNumber = 1, Features = SampleFeatures1
},
new ItemPicked{
Id = 2, Description = "Item 2", LineNumber = 2, Features = SampleFeatures2
},
new ItemPicked{
Id = 3, Description = "Item 3", LineNumber = 3, Features = SampleFeatures3
},
new ItemPicked{
Id = 4, Description = "Item 4", LineNumber = 4, Features = SampleFeatures2
}
};
var pickedGroupings = new List<PickedGrouping>();
PickedGrouping selectedGroup = null;
foreach (var item in items)
{
var found = 0;
if(item.Features == null || !item.Features.Any())
{
selectedGroup = pickedGroupings.FirstOrDefault(x => x.Features == null || !x.Features.Any());
if (selectedGroup == null) selectedGroup = new PickedGrouping();
selectedGroup.Features.AddRange(item.Features);
}
else
{
foreach (var feature in item.Features)
{
foreach (var pg in pickedGroupings)
{
if ((item.Features == null || !item.Features.Any()) && (pg.Features == null || !pg.Features.Any())){
selectedGroup = pg;
found += 1;
}
else
{
foreach (var pgf in pg.Features)
{
if (pgf.Feature == null) continue;
if (pgf.Feature.GroupId == feature.Feature.GroupId && pgf.Feature.GroupTypeId == feature.Feature.GroupTypeId)
{
selectedGroup = pg;
found += 1;
}
}
}
}
}
if (found < 2)
{
pickedGroupings.Add(new PickedGrouping() { Features = item.Features });
selectedGroup = pickedGroupings[pickedGroupings.Count - 1];
}
}
//add item
if (selectedGroup.Items == null) selectedGroup.Items = new List<ItemPicked>();
selectedGroup.Items.Add(item);
}
//update line number
foreach(var pg in pickedGroupings)
{
var lineNum = 1;
foreach(var item in pg.Items)
{
item.LineNumber = lineNum;
lineNum += 1;
}
}
I want to use the Dev Express AspxLtreeList control in my UI and bind it to data from a .NetCore2 API
I am a fair newbie to both ASP and .NetCore
Can I , and if so how do I translate the following code to .NetCore?
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace TreeListDragDropMultipleNodes.Models
{
public class Data
{
public int ID { set; get; }
public int ParentID { set; get; }
public string Title { set; get; }
}
public static class DataHelper
{
public static List<Data> GetData()
{
List<Data> data = HttpContext.Current.Session["Data"] as List<Data>;
if (data == null)
{
data = new List<Data>();
data.Add(new Data { ID = 1, ParentID = 0, Title = "Root" });
data.Add(new Data { ID = 2, ParentID = 1, Title = "A" });
data.Add(new Data { ID = 3, ParentID = 1, Title = "B" });
data.Add(new Data { ID = 4, ParentID = 1, Title = "C" });
data.Add(new Data { ID = 5, ParentID = 2, Title = "A1" });
data.Add(new Data { ID = 6, ParentID = 2, Title = "A2" });
data.Add(new Data { ID = 7, ParentID = 2, Title = "A3" });
data.Add(new Data { ID = 8, ParentID = 3, Title = "B1" });
data.Add(new Data { ID = 9, ParentID = 3, Title = "B2" });
data.Add(new Data { ID = 10, ParentID = 4, Title = "C1" });
data.Add(new Data { ID = 11, ParentID = 8, Title = "B1A" });
data.Add(new Data { ID = 12, ParentID = 8, Title = "B1B" });
HttpContext.Current.Session["Data"] = data;
}
return data;
}
public static void MoveNodes(int[] nodeKeys, int parentID)
{
var data = GetData();
var processedNodes = data.Join(nodeKeys, x => x.ID, y => y, (x, y) => x);
foreach(var node in processedNodes)
{
if (processedNodes.Where(x => x.ID == node.ParentID).Count() == 0)
{
if (node.ParentID == 0)
{
MakeParentNodeRoot(parentID);
}
node.ParentID = parentID;
}
}
}
public static void MoveNode(int nodeID, int parentID)
{
var data = GetData();
var node = data.Find(x => x.ID == nodeID);
if (node.ParentID == 0)
{
MakeParentNodeRoot(parentID);
}
node.ParentID = parentID;
}
public static void MakeParentNodeRoot(int id)
{
GetData().Find(x => x.ID == id).ParentID = 0;
}
}
}
You will need to find a way inject IHttpContextAccessor into the dependent class as HttpContext.Current is not available in .net-core.
To convert the current code first refactor the helper to not be static and it should also be backed by an abstraction/interface
public interface IDataHelper {
List<Data> GetData();
void MoveNodes(int[] nodeKeys, int parentID);
void MoveNode(int nodeID, int parentID);
void MakeParentNodeRoot(int id);
}
Have the class follow explicit dependency via constructor injection for IHttpContextAccessor
public class DataHelper : IDataHelper {
private readonly IHttpContextAccessor accessor;
public DataHelper(IHttpContextAccessor accessor) {
this.accessor = accessor;
}
public List<Data> GetData() {
List<Data> data = accessor.HttpContext.Session.Get<List<Data>>("Data");
if (data == null) {
data = new List<Data>();
data.Add(new Data { ID = 1, ParentID = 0, Title = "Root" });
data.Add(new Data { ID = 2, ParentID = 1, Title = "A" });
data.Add(new Data { ID = 3, ParentID = 1, Title = "B" });
data.Add(new Data { ID = 4, ParentID = 1, Title = "C" });
data.Add(new Data { ID = 5, ParentID = 2, Title = "A1" });
data.Add(new Data { ID = 6, ParentID = 2, Title = "A2" });
data.Add(new Data { ID = 7, ParentID = 2, Title = "A3" });
data.Add(new Data { ID = 8, ParentID = 3, Title = "B1" });
data.Add(new Data { ID = 9, ParentID = 3, Title = "B2" });
data.Add(new Data { ID = 10, ParentID = 4, Title = "C1" });
data.Add(new Data { ID = 11, ParentID = 8, Title = "B1A" });
data.Add(new Data { ID = 12, ParentID = 8, Title = "B1B" });
accessor.HttpContext.Session.Set("Data", data);
}
return data;
}
public void MoveNodes(int[] nodeKeys, int parentID) {
var data = GetData();
var processedNodes = data.Join(nodeKeys, x => x.ID, y => y, (x, y) => x);
foreach(var node in processedNodes) {
if (processedNodes.Where(x => x.ID == node.ParentID).Count() == 0) {
if (node.ParentID == 0) {
MakeParentNodeRoot(parentID);
}
node.ParentID = parentID;
}
}
}
public void MoveNode(int nodeID, int parentID) {
var data = GetData();
var node = data.Find(x => x.ID == nodeID);
if (node.ParentID == 0) {
MakeParentNodeRoot(parentID);
}
node.ParentID = parentID;
}
public void MakeParentNodeRoot(int id) {
GetData().Find(x => x.ID == id).ParentID = 0;
}
}
Some additional extension would be needed to store the data in the session
//Extensions used to stores complex objects as JSON string in session
public static class SessionExtensions {
public static void Set(this ISession session, string key, object value) {
session.SetString(key, JsonConvert.SerializeObject(value));
}
public static T Get<T>(this ISession session, string key) {
var value = session.GetString(key);
return value == null ? default(T) : JsonConvert.DeserializeObject<T>(value);
}
}
Configure the service collection to allow the needed dependencies to be resolved when requested.
services.AddScoped<IDataHelper, DataHelper>();
services.AddHttpContextAccessor();//IHttpContextAccessor is not added by default.
Any class that was dependent on the data helper would now also need to be refactored to depend on its abstraction.
This allows the code to be cleaner and more manageable as it will be decoupled from static implementation concerns.
how can I get N elements of the child list? Let's say I'd like to get 2 children for each parent.
public class Program
{
static void Main(string[] args)
{
var data = new List<Parent>()
{
new Parent()
{
Id = 1,
Name = "ParentName1",
Children = new List<Child>()
{
new Child() { Id = 1, Name = "ChildName1"},
new Child() { Id = 2, Name = "ChildName2"},
new Child() { Id = 3, Name = "ChildName3"},
new Child() { Id = 4, Name = "ChildName4"},
new Child() { Id = 5, Name = "ChildName5"},
}
},
new Parent()
{
Id = 2,
Name = "ParentName2",
Children = new List<Child>()
{
new Child() { Id = 6, Name = "ChildName6"},
new Child() { Id = 7, Name = "ChildName7"},
new Child() { Id = 8, Name = "ChildName8"},
new Child() { Id = 9, Name = "ChildName9"},
new Child() { Id = 10, Name = "ChildName10"},
}
}
};
// Get only 2 child elements for parent
var filteredData = data.Where(x => x.Children.Count >= 2)
.ToList();
foreach (var filteredParent in filteredData)
{
Console.WriteLine($"Parent {filteredParent.Id} with {filteredParent.Children.Count} children.");
}
Console.ReadKey();
}
}
public class Parent
{
public int Id { get; set; }
public string Name { get; set; }
public List<Child> Children { get; set; }
}
public class Child
{
public int Id { get; set; }
public string Name { get; set; }
public Parent Parent { get; set; }
}
One way to do it is to use a multi statement lambda expression for the result selector and List<T>'s RemoveRange method:
var query = data.Select
(
p =>
{
p.Children.RemoveRange(2, p.Children.Count - 2);
return p;
}
);
As Flater commented, It might be better to return a shallow copy of the parent, with only the first two children. That way your query does not change the source data:
var query = data.Select
(
p => new Parent()
{
Id = p.Id,
Name = p.Name,
Children = p.Children.Take(2).ToList()
}
);
I have a c# project, and I've created a class called Employees.
Inside this class, I have a new list:
public class Employees
{
public int Id { get; set; }
public string Name { get; set; }
public int? ManagerId { get; set; }
public List<Employees> employees { get; set; }
}
Imagine that I have the following structure shown in the image:
companytree
Then in the main program, I have this structure to represent the picture above:
class Program
{
static void Main(string[] args)
{
var root = new Employees()
{
Id = 15,
Name = "President",
employees = new List<Employees>()
{
new Employees() {
Id = 23, ManagerId = 15, Name = "Director23",
employees = new List<Employees>()
{
new Employees() {
Id = 21, ManagerId = 23, Name = "Manager21",
employees = new List<Employees>()
{
new Employees() { Id = 31, ManagerId=21, Name = "Employee31" },
new Employees() { Id = 41, ManagerId=21, Name = "Employee41" },
new Employees() { Id = 51, ManagerId=21, Name = "Employee51" }
}
},
new Employees() {
Id = 22, ManagerId = 23, Name = "Manager22",
employees = new List<Employees>()
{
new Employees() { Id = 32, ManagerId=22, Name = "Employee32" },
new Employees() { Id = 42, ManagerId=22, Name = "Employee42" },
new Employees() { Id = 52, ManagerId=22, Name = "Employee52" }
}
}
}
},
new Employees() {
Id = 25, ManagerId = 15, Name = "Director25",
employees = new List<Employees>()
{
new Employees() {
Id = 51, ManagerId = 25, Name = "Manager51",
employees = new List<Employees>()
{
new Employees() { Id = 61, ManagerId=51, Name = "Employee61" },
new Employees() { Id = 71, ManagerId=51, Name = "Employee71" },
new Employees() { Id = 81, ManagerId=51, Name = "Employee81" }
}
},
new Employees() {
Id = 62, ManagerId = 25, Name = "Manager62",
employees = new List<Employees>()
{
new Employees() { Id = 72, ManagerId=62, Name = "Employee72" },
new Employees() { Id = 82, ManagerId=62, Name = "Employee82" }
}
}
}
}
}
};
Console.ReadLine();
}
}
How to create a function where I pass the tree root list of the employee and the ID of any employee of the company and you need to return your manager closer or higher and also the employee itself.
Remembering that you could pass the ID of a director (you would have to return the president), the ID of the manager(you would have to return the director), the ID of the employee (you would have to return the manager), the ID of the presidente return himself.
What better way to do this research taking into account that we can have a much larger hierarchical structure than this example. It would be costly to scan all lists.
Use hastable, dictionary, hashset??
A recursive function is generally used to search a tree. I assumed you fix the variable names as suggested:
public static Employee FindById(Employee root, int id) {
if (root.Id == id)
return root;
else if (root.Employees != null) {
foreach (var e in root.Employees) {
var pe = FindById(e, id);
if (pe != null)
return pe;
}
}
return null;
}
To use, find the employee and then the manager:
var emp = FindById(root, 51);
var manager = emp.ManagerId.HasValue ? FindById(root, emp.ManagerId.Value) : null;
Add there two functions :
public static IDictionary<int, Employees> EmployeesToDictionary(Employees employees)
{
var dictionary = new Dictionary<int, Employees>();
EmployeesToDictionary(employees, dictionary);
return dictionary;
}
private static void EmployeesToDictionary(Employees employees, IDictionary<int, Employees> dictionary)
{
if (employees == null) return;
dictionary.Add(employees.Id, employees);
if (employees.employees == null) return;
foreach (var sub in employees.employees)
{
EmployeesToDictionary(sub);
}
}
And usage :
var id = 5;
var dict = EmployeesToDictionary(root);
var employee = dict[id];
var manager = dict[employee.ManagerId.Value];
Edit :
#haindl Documentation is an admission of failure Yeah you're right.
#devweb I have added the check for the exception. The dictionnary is created only one time, check again
If you want to do a traversal only once, there are this possibility :
public static void FindById(Employees root, int id, out Employees employees, out Employees manager)
{
employees = manager = null;
// todo stack
var stack = new Stack<Employees>();
stack.Push(root);
// all managers seens
var managers = new List<Employees>();
while (stack.Count > 0)
{
var e = stack.Pop();
if (e.Id == id) // if found
{
employees = e;
manager = managers.FirstOrDefault(m => m.Id == e.ManagerId);
return;
}
else if (e.employees != null)
{
// add only managers with employee
managers.Add(e);
foreach (var ep in e.employees)
{
stack.Push(ep);
}
}
}
}
I have a question about a LINQ grouping.
I thought that grouping would be a simple matter of using the GroupBy function on the result set and specifying what to group it by. However my items appear to not be grouping together and instead are displaying as if the GroupBy function wasn't there. I want to group by the itemPk, but I'm can't seem to do it. I have tried grouping by both category.ItemFk and Item.Itempk, but no luck. Could someone give me a pointer on this?
var itemIds = items.Select(i => i.ItemId).ToList();
var itemAndCatJoin =
from item in Context.SCS_Items
join category in Context.SCS_ItemCategories
on item.ItemPk equals category.ItemFk
into temp
from category in temp.DefaultIfEmpty()
select new ExportItemTable
{
Category = category,
Item = item
};
return itemAndCatJoin.Where(i => itemIds.Contains(i.Item.ItemPk))
.GroupBy(n => new {n.Item, n.Category})
.Select(i => new ExportableItem
{
ItemPk = i.Key.Item.ItemPk,
Name = i.Key.Item.Name,
Description = i.Key.Item.Description,
Price = i.Key.Item.Price,
Category = i.Key.Category.Category.Category_Name,
GLDepartment = i.Key.Category.GL_Department.Name ?? "",
GLName = i.Key.Category.GL_Name.Name ?? "",
StartDate = i.Key.Item.StartDate,
EndDate = i.Key.Item.EndDate,
FiscalYear = i.Key.Item.SCS_FiscalYear.Name,
School = i.Key.Item.School != null ? i.Key.Item.School.School_Name : i.Key.Item.Board.Board_Name,
Beneficiary = i.Key.Item.SCS_Beneficiary.Name,
Quantity = i.Key.Item.MaxQuantity,
Deleted = i.Key.Item.DeletedFlag,
OptionalStudents = i.Key.Item.SCS_Attachments.Where(a => !a.IsRequired).SelectMany(a => a.SCS_StudentAttachments).Where(s => !s.DeletedFlag).Select(s => s.StudentFk).Distinct().Count(),
RequiredStudents = i.Key.Item.SCS_Attachments.Where(a => a.IsRequired).SelectMany(a => a.SCS_StudentAttachments).Where(s => !s.DeletedFlag).Select(s => s.StudentFk).Distinct().Count(),
IsPublic = i.Key.Item.IsPublic,
AllowRecurring = i.Key.Item.AllowRecurringPayments,
EffectiveCutoff = i.Key.Item.SCS_Attachments.Where(a => !a.DeletedFlag && a.CourseDropCutoff.HasValue).Select(a => a.CourseDropCutoff).OrderBy(a => a).FirstOrDefault(),
CreatedDate = i.Key.Item.CreatedDate
}).OrderBy(i => i.ItemPk).ToList();
}
your groupbyy is indeed doing nothing for you, you need to tell the groupby what to group by....
like
.GroupBy(n => n.Category)
Here is a simple example to your grouping question:
class Program
{
static void Main()
{
var allItems = GetAllItems();
var groups = from item in allItems
group item by item.Category
into newGroup
select newGroup;
foreach (var group in groups)
{
Console.WriteLine($"\nCategory: {group.Key}");
foreach (var item in group)
{
Console.WriteLine($"{item.Name}: {item.Price}");
}
}
Console.ReadLine();
}
static List<Category> GetAllCategories()
{
return new List<Category>()
{
new Category() { Id = 1, Name = "Programming Books" },
new Category() { Id = 2, Name = "Fiction Books" }
};
}
static List<Item> GetAllItems()
{
return new List<Item>()
{
new Item() { Id = 1, Name = "Embedded Linux", Category = 1, Price = 9.9 },
new Item() { Id = 2, Name = "LINQ In Action", Category = 1, Price = 36.19 },
new Item() { Id = 3, Name = "C# 6.0 and the .NET 4.6 Framework", Category = 1, Price = 40.99 },
new Item() { Id = 4, Name = "Thinking in LINQ", Category = 1, Price = 36.99 },
new Item() { Id = 5, Name = "The Book Thief", Category = 2, Price = 7.99 },
new Item() { Id = 6, Name = "All the Light We Cannot See", Category = 2, Price = 16.99 },
new Item() { Id = 7, Name = "The Life We Bury", Category = 2, Price = 8.96 }
};
}
}
public class Item
{
public int Id { get; set; }
public string Name { get; set; }
public double Price { get; set; }
public int Category { get; set; }
}
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
}
This example is simple enough for anyone new to LINQ. I am sure you can make some adjustment to make it work for your specific issue. Hope this will help.