Reduce Time Complexity of Code with nested foreach loops - c#

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();

Related

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:

How to group a table by a certain attribute then count those elements that are present in an other table (with foreign keys) in LINQ?

Let's say I have two tables (entities):
class Person
{
public int Id { get; set; } // primary key
public string City { get; set; } // the attribute to group by
}
class JoinTable
{
public int Id { get; set; } // primary key
public int Person_Id { get; set; } // foreign key referencing a Person entity/row
public int SomeOther_Id { get; set; } // foreign key referencing some other irrelevant entity/row
}
I want to group all Person entities by their "City" attribute and count how many people are referenced in the JoinTable by each city.
How do I query that in LINQ?
I'm not quite sure, what you want to acchieve. But i think something like this:
// Example Data (would be great if you could write some in your questions..)
List<Person> ps = new List<Person>()
{
new Person() { Id = 1, City = "Cologne" },
new Person() { Id = 2, City = "Cologne" },
new Person() { Id = 3, City = "Berlin" },
new Person() { Id = 4, City = "Berlin" },
};
List<JoinTable> join = new List<JoinTable>()
{
new JoinTable() { Id = 1, Person_Id = 1, SomeOther_Id = 1000 },
new JoinTable() { Id = 1, Person_Id = 1, SomeOther_Id = 2000 },
new JoinTable() { Id = 1, Person_Id = 2, SomeOther_Id = 1000 },
new JoinTable() { Id = 1, Person_Id = 2, SomeOther_Id = 2000 },
new JoinTable() { Id = 1, Person_Id = 3, SomeOther_Id = 3000 },
new JoinTable() { Id = 1, Person_Id = 3, SomeOther_Id = 4000 },
};
// Join the Table and select a new object.
var tmp = ps.Join(
join, // which table/list should be joined
o => o.Id, // key of outer list
i => i.Person_Id, // key of inner list
(o, i) => new { City = o.City, Id = i.Id, SomeOtherId = i.SomeOther_Id}); // form a new object with three properties..
// now we can group out new objects
var groupingByCity = tmp.GroupBy(g => g.City);
// let's see what we got..
foreach (var g in groupingByCity)
{
Console.WriteLine(g.Key + ": " + g.Count());
foreach (var g2 in g.GroupBy(a => a.SomeOtherId)) // And yes we can group out values again..
{
Console.WriteLine(" " + g2.Key + ": " + g2.Count());
}
}

Cannot group data in LINQ

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.

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 })));

How to build a hierarchy with use Linq to object?

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();
}

Categories