How to build a hierarchy with use Linq to object? - c#

I have a list of data structures:
public List<Personal> Personals()
{
return new List<Personal>
{
new Personal
{
Id = 0,
Name = "Name 0"
},
new Personal
{
Id = 1,
Name = "Name 1",
ParentId = 0
},
new Personal
{
Id = 2,
Name = "Name 2",
ParentId = 0
},
new Personal
{
Id = 3,
Name = "Name 3",
ParentId = 0
},
new Personal
{
Id = 4,
Name = "Name 4",
ParentId = 1
},
new Personal
{
Id = 5,
Name = "Name 5",
ParentId = 1
},
new Personal
{
Id = 6,
Name = "Name 6",
ParentId = 2
},
new Personal
{
Id = 7,
Name = "Name 7",
ParentId = 2
},
new Personal
{
Id = 8,
Name = "Name 8",
ParentId = 4
},
new Personal
{
Id = 9,
Name = "Name 9",
ParentId = 4
},
};
}
and I want to build a tree:
public List<Tree> Trees()
{
return new List<Tree>
{
new Tree
{
Id = 0,
Name = "Name 0",
List = new List<Tree>
{
new Tree
{
Id = 1,
Name = "Name 1",
List = new List<Tree>
{
new Tree
{
Id = 4,
Name = "Name 4"
},
new Tree
{
Id = 5,
Name = "Name 5"
}
}
}
}
}
};
}
How do you build a tree with LinQ to object? I have to use but it doesn't work exactly, see below:
public List<Tree> GetTree(List<Personal> list)
{
var listFormat = list.Select(x => new Tree
{
Id = x.Id,
Name = x.Name,
ParentId = x.ParentId
}).ToList();
var lookup = listFormat.ToLookup(f => f.ParentId);
foreach (var tree in listFormat)
{
tree.List = lookup[tree.Id].ToList();
}
return listFormat;
}

You should use recursion:
public void SomeMethod() {
// here you get your `list`
var tree = GetTree(list, 0);
}
public List<Tree> GetTree(List<Personal> list, int parent) {
return list.Where(x => x.ParentId == parent).Select(x => new Tree {
Id = x.Id,
Name = x.Name,
List = GetTree(list, x.Id)
}).ToList();
}

Same as above only this code checks for the case that your root node has a ParentID that matches its own ID.
public void SomeMethod()
{
// here you get your `list`
var tree = GetTree(list, 0);
}
public List<Tree> GetTree(List<Personal> list, int parent)
{
return list.Where(x => x.ParentId == parent).Select(x => new Tree
{
Id = x.Id,
Name = x.Name,
List = x.ParentId != x.Id ? GetTree(list, x.Id) : new List<Tree>()
}).ToList();
}

Related

Convert Flat Data from C# to JSON

I have a c# class that looks something like this:
public class Item
{
public int Id { get; set; }
public int? ParentId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string Type { get; set; }
}
and i would like to convert it to json where json property name is item name and its value is item description. if some item has any children then i would like the json property name to stay as item name and the children to be added like item name:item description(the parent item description and type become empty string if it has any children, except when the type is array). The type has following values: array, string, int, object. if the item type is an array then each time a child is added to the type array item, the child is item description. so i would like those values to be added to the json array as well.
if the type is string or int the json property value should be int or string.
I am trying to write custom JsonSerializer but i am getting nowhere.
so if i have a list with items:
List<Item> MyItems = new List<Item>()
{
new Item { Id = 1, ParentId = null, Name = "Name1", Description = "", Type = "" },
new Item { Id = 2, ParentId = 1, Name = "Name2", Description = "Description1", Type = "String" },
new Item { Id = 3, ParentId = 1, Name = "Name3", Description = "", Type = "Array" },
new Item { Id = 4, ParentId = 3, Name = "", Description = "ArrayItem1", Type = "" },
new Item { Id = 5, ParentId = 3, Name = "", Description = "ArrayItem2", Type = "" },
new Item { Id = 6, ParentId = 3, Name = "", Description = "ArrayItem3", Type = "" },
new Item { Id = 7, ParentId = null, Name = "Name4", Description = "5", Type = "Int" },
};
then the json should like this:
{
"name1":{
"name2":"description1",
"name3":[
"ArrayItem1",
"ArrayItem2",
"ArrayItem1"
]
},
"name4":5
}
Here's an extension method that I think does what you want:
public static class Ex
{
public static string ToJson(this List<Item> items)
{
var lookup = items.ToLookup(x => x.ParentId);
JObject ToJson(int? parentId)
{
JProperty ToProperty(Item item)
{
switch (item.Type)
{
case "":
return new JProperty(item.Name, ToJson(item.Id));
case "String":
return new JProperty(item.Name, item.Description);
case "Array":
return new JProperty(item.Name, lookup[item.Id].Select(x => x.Description).ToArray());
case "Int":
return new JProperty(item.Name, int.Parse(item.Description));
default:
return new JProperty(item.Name);
}
}
return new JObject(lookup[parentId].Select(x => ToProperty(x)));
}
var output = ToJson(null);
var text = Newtonsoft.Json.JsonConvert.SerializeObject(output, Newtonsoft.Json.Formatting.Indented);
return text;
}
}
I run it like this:
List<Item> MyItems = new List<Item>()
{
new Item { Id = 1, ParentId = null, Name = "Name1", Description = "", Type = "" },
new Item { Id = 2, ParentId = 1, Name = "Name2", Description = "Description1", Type = "String" },
new Item { Id = 3, ParentId = 1, Name = "Name3", Description = "", Type = "Array" },
new Item { Id = 4, ParentId = 3, Name = "", Description = "ArrayItem1", Type = "" },
new Item { Id = 5, ParentId = 3, Name = "", Description = "ArrayItem2", Type = "" },
new Item { Id = 6, ParentId = 3, Name = "", Description = "ArrayItem3", Type = "" },
new Item { Id = 7, ParentId = null, Name = "Name4", Description = "5", Type = "Int" },
};
Console.WriteLine(MyItems.ToJson());
And I get this out:
{
"Name1": {
"Name2": "Description1",
"Name3": [
"ArrayItem1",
"ArrayItem2",
"ArrayItem3"
]
},
"Name4": 5
}

Reduce Time Complexity of Code with nested foreach loops

I am new to C# development, I have a "Comments" Class as below.
class Comments
{
public Id {get; set;}
public Text {get;set;}
public ParentId {get;set;}
public List<Comments> childComments {get;set;}
}
We have child comments field within main comments. I am saving each comment object as a Document in NoSQL DB. I need to fetch these all comments and convert them into single comment object by placing all of its child comments inside 'childComments' field. ParentId will be null if comment is at level 0(top most level or first level comment).
I wrote the below code to retrieve it.
List<Comments> parentcomments = <from DB>.Where(t => t.ParentId == ObjectId.Empty).ToList();
List<Comments> childcomments = <from DB>.Where(t => t.ParentId != ObjectId.Empty).ToList();
foreach(comment t in parentcomments)
{
finalCommentTree = AggregateComment(childcomments, t.Id);
}
public List<Comments> AggregateComment(List<Comments> childcomments, ObjectId parentId)
{
List<Comments> recursiveObjects = new List<Comments>();
foreach (Comments item in childcomments.Where(x => x.ParentId.Equals(t.ParentId)))
{
recursiveObjects.Add(new Comments
{
Id = item.Id,
Text = item.Text,
childComments = AggregateComment(childcomments, item.Id)
});
}
return recursiveObjects;
}
Code works good without any issues, but problem is with time complexity. Is there a way to reduce time complexity and improve performance?
Another approach:
List<Comments> parentList = new List<Comments>()
{ new Comments() { Id = 1, Text = "Parent1", ParentId = -1 },
new Comments() { Id = 2, Text = "Parent2", ParentId = -1 },
new Comments() { Id = 3, Text = "Parent3", ParentId = -1 },
};
List<Comments> childList = new List<Comments>()
{
new Comments() { Id = 91, Text = "child1", ParentId = 3 },
new Comments() { Id = 92, Text = "child2", ParentId = 2 },
new Comments() { Id = 93, Text = "child3", ParentId = 1 },
new Comments() { Id = 94, Text = "child4", ParentId = 2 },
new Comments() { Id = 95, Text = "child5", ParentId = 2 },
new Comments() { Id = 96, Text = "child6", ParentId = 1 },
new Comments() { Id = 97, Text = "child7", ParentId = 2 }
};
List<Comments> k = ( from c in childList
join p in parentList
on c.ParentId equals p.Id
group c by new
{
c.ParentId
,p.Text
} into stdGrp
select new Comments
{
Id = stdGrp.Key.ParentId,
Text = stdGrp.Key.Text,
ParentId = -1,
childComments = stdGrp.OrderBy(j => j.Id).ToList(),
}
).ToList();

LINQ C# Except items from one list

I have Posts table:
Id ReplyId
1 null
2 1
3 null
4 3
5 null
posts contains all of these items.
I want to except posts where Id = ReplyId, so in result I want to get posts with Ids 2,4,5.
In other words, we can see ReplyId = 1 then we need to remove from list Post with Id = 1. Also ReplyId = 3 then remove from list Post with Id = 3.
How can I implement that?
posts.Where(x => !posts.Any(y => y.ReplyId == x.Id))
test:
void Main()
{
var posts = new[] {
new Post { Id = 1, ReplyId = null},
new Post { Id = 2, ReplyId = 1},
new Post { Id =3, ReplyId = null},
new Post { Id = 4, ReplyId = 3},
new Post { Id = 5, ReplyId = null},
};
var endItems = posts.Where(x => !posts.Any(y => y.ReplyId == x.Id));
foreach (var element in endItems)
Console.WriteLine(element.Id);
}
class Post
{
public int Id { get; set; }
public int? ReplyId { get; set; }
}
Try this:
var posts = new[]
{
new { Id = 1, ReplyId = (int?)null, },
new { Id = 2, ReplyId = (int?)1, },
new { Id = 3, ReplyId = (int?)null, },
new { Id = 4, ReplyId = (int?)3, },
new { Id = 5, ReplyId = (int?)null, },
};
var query =
from p in posts
join p2 in posts on p.Id equals p2.ReplyId into g
where !g.Any()
select p;
I get:

Linq query to return object list with multiple child arrays

Incoming list
var list = new List<Franchise>()
{
new Franchise()
{Id = 10, Name = "Franchise1", Code= "DD1", IsDomestic= 1, ParentCompanyId=1, GroupId=100 },
new Franchise()
{Id = 10, Name = "Franchise1", Code= "DD1", IsDomestic= 1, ParentCompanyId=2, GroupId=100 },
new Franchise()
{Id = 10, Name = "Franchise1", Code= "DD1", IsDomestic= 1, ParentCompanyId=3, GroupId=200 },
new Franchise()
{Id = 15, Name = "Franchise5", Code= "FD1", IsDomestic= 0, ParentCompanyId=4, GroupId=300 },
new Franchise()
{Id = 15, Name = "Franchise5", Code= "FD1", IsDomestic= 0, ParentCompanyId=3, GroupId=300 },
new Franchise()
{Id = 15, Name = "Franchise5", Code= "FD1", IsDomestic= 0, ParentCompanyId=2, GroupId=400 },
};
I want this to be transformed to list of the class below using LINQ
public class FranchiseNew
{
public int Id { get; set; }
public string Name{ get; set; }
public int[] CategoryIds { get; set; }
public int[] DivisionIds { get; set; }
public int IsDomestic{ get; set; }
}
output - one row per franchise with ParentCompanyIds and GroupIds in arrays
var list = new List<Franchise>()
{
new Franchise()
{Id = 10, Name = "Franchise1", Code= "DD1", IsDomestic= 1, ParentCompanyIds=[1, 2, 3], GroupIds = [100, 200 ]},
new Franchise()
{Id = 15, Name = "Franchise2", Code= "FD1", IsDomestic= 0, ParentCompanyIds=[4, 3, 2], GroupIds = [300, 400] }
};
What is the efficient LINQ query to achieve this? Thanks in advance.
You can try like below:
var collectionGroup = list.GroupBy(x => new { x.Name, x.Id, x.Code, x.IsDomestic }).ToList();
var result = collectionGroup.Select(x => new FranchiseNew
{
Id = x.Key.Id,
Name = x.Key.Name,
Code = x.Key.Code,
IsDomestic = x.Key.IsDomestic,
CategoryIds = x.GroupBy(s => s.ParentCompanyId).Select(y => y.Key).ToArray(),
DivisionIds = x.GroupBy(s => s.GroupId).Select(y => y.Key).ToArray()
}).ToList();
And in you're FranchiseNew model, add Code field.

Fill one Datagridview's Column by a List

I have a datagridview that's bounded to a dataBase (I have the bindingSource and the bindingNavigator)
so if I want to display all the table (livre in my case) I write this code:
query = from x in ctx.livre
select x;
livreBindingSource.DataSource = query.ToList();
I added a column called Hierarchy to the dataGridView in order to add some informations (contained in a list) which are not in the table livre.
so I want to bind this list to that column Hierarchy
Example (without column Hierarchy):
query = from x in ctx.livre
select x;
livreBindingSource.DataSource = query.ToList();
id | name
1 | name1
2 | name2
....
Example (with column Hierarchy):
query = from x in ctx.livre
select x;
livreBindingSource.DataSource = query.ToList();
//Some code here for binding list<string> to column Hierarchy
id | name | Hierarchy
1 | name1 | 1 is for name1
2 | name2 | this is second
....
How can I do this.
Thanks!
change your select to add the column in the linq?
like:
select new { x.id, x.name, Hierarchy = (x.id == 1 ? "1 is for name1" : "this is second") };
if the hierarchy data is in another list, you can just join to it and then reference
var result = from y in ctx.Livre
join h in hierList on y.id equals h.hierId
select new { y.id, y.name, Hierarchy = h.hierString };
Ok adding some code here to show the flattened hierarchy
public class Rayon
{
public int idLocation { get; set; }
public string LocationName { get; set; }
public List<Rayon> idParentLocation { get; set; }
}
public class Livre
{
public int id { get; set; }
public string name { get; set; }
public string author { get; set; }
public List<Rayon> Location { get; set; }
}
List<Rayon> library = new List<Rayon>();
library.Add(new Rayon() { idLocation = 1, idParentLocation = null, LocationName = "BookCase1" });
library.Add(new Rayon() { idLocation = 10, idParentLocation = new List<Rayon>() { library.Find(i => i.LocationName.Equals("BookCase1")) }, LocationName = "1Shelf1" });
library.Add(new Rayon() { idLocation = 11, idParentLocation = new List<Rayon>() { library.Find(i => i.LocationName.Equals("BookCase1")) }, LocationName = "1Shelf2" });
library.Add(new Rayon() { idLocation = 12, idParentLocation = new List<Rayon>() { library.Find(i => i.LocationName.Equals("BookCase1")) }, LocationName = "1Shelf3" });
library.Add(new Rayon() { idLocation = 2, idParentLocation = null, LocationName = "BookCase2" });
library.Add(new Rayon() { idLocation = 20, idParentLocation = new List<Rayon>() { library.Find(i => i.LocationName.Equals("BookCase2")) }, LocationName = "2Shelf1" });
library.Add(new Rayon() { idLocation = 21, idParentLocation = new List<Rayon>() { library.Find(i => i.LocationName.Equals("BookCase2")) }, LocationName = "2Shelf2" });
library.Add(new Rayon() { idLocation = 22, idParentLocation = new List<Rayon>() { library.Find(i => i.LocationName.Equals("BookCase2")) }, LocationName = "2Shelf3" });
library.Add(new Rayon() { idLocation = 3, idParentLocation = null, LocationName = "BookCase3" });
library.Add(new Rayon() { idLocation = 30, idParentLocation = new List<Rayon>() { library.Find(i => i.LocationName.Equals("BookCase3")) }, LocationName = "3Shelf1" });
library.Add(new Rayon() { idLocation = 31, idParentLocation = new List<Rayon>() { library.Find(i => i.LocationName.Equals("BookCase3")) }, LocationName = "3Shelf2" });
library.Add(new Rayon() { idLocation = 32, idParentLocation = new List<Rayon>() { library.Find(i => i.LocationName.Equals("BookCase3")) }, LocationName = "3Shelf3" });
List<Livre> bkList = new List<Livre>();
bkList.Add(new Livre() { id = 1, name = "Catch-22", author = "Heller", Location = new List<Rayon>() { library.Find(i => i.idLocation == 30) } });
var test = bkList.SelectMany(b => b.Location.SelectMany(c => c.idParentLocation.Select(p => new { id = b.id, name = b.name, author = b.author, hierarchy = p.LocationName + "->" + c.LocationName + "->" + b.name })));

Categories