LINQ C# Except items from one list - c#

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:

Related

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 query to return items of list with another list inside it

I'm new to Linq queries, and this problem encountered me (after the other dev was fired).
I'm consuming a freight API that returns a list of "n" freight prices for each product/item in the requisition. The response classes are:
public class FreightSimulation
{
public List<Item> Items { get; set; }
}
public class Item
{
public int Id { get; set; }
public List<ItemFreight> Freights { get; set; }
}
public class ItemFreight
{
public Company Company { get; set; }
public Type Type { get; set; }
public double Price { get; set; }
public int Days { get; set; }
public List<Error> Errors { get; set; }
}
public class Company
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Type
{
public int Id { get; set; }
public string Description { get; set; }
}
public class Error
{
public string Message { get; set; }
}
I have a response model for 2 items (but it can also be "n" items), one of them have 5 freights possibilities, with no errors, but the other has two errors meaning that one company doesn't have a freight service for that item:
var response = new FreightSimulation
{
Items = new List<Item>
{
new Item
{
Id = 1,
Freights = new List<ItemFreight>
{
new ItemFreight
{
Errors = null,
Price = 10,
Days = 8,
Company = new Company
{
Id = 1
},
Type = new Type
{
Id = 3
}
},
new ItemFreight
{
Errors = null,
Price = 20,
Days = 3,
Company = new Company
{
Id = 1
},
Type = new Type
{
Id = 8
}
},
new ItemFreight
{
Errors = null,
Price = 20,
Days = 10,
Company = new Company
{
Id = 2
},
Type = new Type
{
Id = 1
}
},
new ItemFreight
{
Errors = null,
Price = 35,
Days = 4,
Company = new Company
{
Id = 2
},
Type = new Type
{
Id = 2
}
},
new ItemFreight
{
Errors = null,
Price = 15,
Days = 6,
Company = new Company
{
Id = 7468
},
Type = new Type
{
Id = 1
}
}
}
},
new Item
{
Id = 2,
Freights = new List<ItemFreight>
{
new ItemFreight
{
Errors = null,
Price = 10,
Days = 8,
Company = new Company
{
Id = 1
},
Type = new Type
{
Id = 3
}
},
new ItemFreight
{
Errors = null,
Price = 20,
Days = 3,
Company = new Company
{
Id = 1
},
Type = new Type
{
Id = 8
}
},
new ItemFreight
{
Errors = new List<Error>
{
new Error
{
Message = "Not found."
}
},
Company = new Company
{
Id = 2
},
Type = new Type
{
Id = 1
}
},
new ItemFreight
{
Errors = new List<Erro>
{
new Error
{
Message = "Not found."
}
},
Company = new Company
{
Id = 2
},
Type = new Type
{
Id = 2
}
},
new ItemFreight
{
Errors = null,
Price = 22,
Days = 4,
Company = new Company
{
Id = 7468
},
Type = new Type
{
Id = 1
}
}
}
}
}
};
What I need at first is a Linq Query that brings a List where the children List are common between all the Items and doesn't have errors in it.
In this case I have something like:
Company Id Type Id Error
Item 1 1 3 N
Item 1 1 8 N
Item 1 2 1 N
Item 1 2 2 N
Item 1 7468 1 N
Item 2 1 3 N
Item 2 1 8 N
Item 2 2 1 Y
Item 2 2 2 Y
Item 2 7468 1 N
So in this case I need a list where the combinations would be only the company 1 and 7468 (could be "n" companies). So in item 2 I have errors on my company 2 response, so it has to be eliminated. The result would be:
Company Id Type Id Error
Item 1 1 3 N
Item 1 1 8 N
Item 1 7468 1 N
Item 2 1 3 N
Item 2 1 8 N
Item 2 7468 1 N
It could happen with any of the combination companies/types, so it has to be some kind of a dynamic query, if it's possible.
Thanks in advance.
You can get the companies that return an error for any freight:
var errorCompanyIds = response.Items
.SelectMany(x => x.Freights)
.Where(y => y.Errors !=null && y.Errors.Any())
.Select(y => y.Company.Id)
.ToList();
Then you can create your new list and filter any freight from that comapny
var newList = new FreightSimulation
{
Items = response.Items.Select(x => new Item
{
Id = x.Id,
Freights = x.Freights.Where(y => !errorCompanyIds.Contains(y.Company.Id)).ToList()
}).ToList()
};
This should work:
var companyIds = new[] { 1, 7468 };
var result = response.Items.SelectMany(i => i.Freights.Select(f => new { itemId = i.Id, Freight = f }))
.Where(i => companyIds.Contains(i.itemId) && (!(i.Freight.Errors?.Any() ?? false)));
What you are doing here is creating pairs to preserve the ids and then filtering those pairs based on your desired criteria.
Alternatively, you can filter the original list of freights first, then pick by company:
var companyIds = new[] { 1, 7468 };
var result = response.Items.SelectMany(i => i.Freights.Where(f => (!(f.Errors?.Any() ?? false)))
.Select(f => new { itemId = i.Id, Freight = f }))
.Where(i => companyIds.Contains(i.itemId));
Just in case this part is a bit confusing:
(!(i.Freight.Errors?.Any() ?? false))
This essentially translates to:
i.Freight.Errors == null || !i.Freight.Errors.Any()
Meaning freights with null errors or no errors (empty collection).

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

How to set a value in one list when comparing it with other list using LINQ

I have two lists:
List A List B
ID FirstName WorkingID ID FirstName WorkingID
5 John Null 5 John 1
9 Patrick Null 9 Patrick 2
16 Ryan Null 16 Ryan 3
I want to compare these lists using LINQ and if IDs from both lists are equal, I want to set WorkingID in the first list, A list.
Thanks.
Also like this:
A.ForEach(c => c.WorkingID = B.Where(m => m.ID == c.ID).Select(s => s.WorkingID).FirstOrDefault());
Full source:
public class Test
{
public int ID;
public int? WorkingID;
}
class Program
{
static void Main(string[] args)
{
var A = new List<Test>()
{
new Test { ID = 5, WorkingID = null },
new Test { ID = 9, WorkingID = null },
new Test { ID = 16, WorkingID = null },
new Test { ID = 18, WorkingID = null }
};
var B = new List<Test>()
{
new Test { ID = 5, WorkingID = 1 },
new Test { ID = 9, WorkingID = 2 },
new Test { ID = 16, WorkingID = 3 }
};
A.ForEach(c => c.WorkingID = B.Where(m => m.ID == c.ID).Select(s => s.WorkingID).FirstOrDefault());
}
}
Try this:
listA.Where(a => listB.Any(b => a.ID == b.ID))
.ToList()
.ForEach(a => a.WorkingID = listB.First(b => b.ID == a.ID).WorkingID);
You could try something like this (not a pure LINQ approach):
foreach(var item in listA)
{
var itemInB = listB.FirstOrDefault(x=>x.ID==item.ID);
if(itemInB!=null)
item.WorkingID = itemInB.WorkongID
}
var query = from a in lista
join b in listb on a.ID equals b.ID
where b.WorkingID != null
select new {a, b};
foreach (var item in query)
{
item.a.WorkingID = item.b.WorkingID;
}
easy as that.
Here is one way, assuming a class structure as follows:
class MyItem
{
public int ID { get; set; }
public string FirstName { get; set; }
public int WorkingID { get; set; }
}
You can join the second list on matching IDs:
var list1 = new List<MyItem>() {
new MyItem() { ID = 5, FirstName = "John" },
new MyItem() { ID = 9, FirstName = "Patrick" },
new MyItem() { ID = 16, FirstName = "Ryan" },
};
var list2 = new List<MyItem>() {
new MyItem() { ID = 5, FirstName = "John", WorkingID = 1 },
new MyItem() { ID = 9, FirstName = "Patrick", WorkingID = 2 },
new MyItem() { ID = 16, FirstName = "Ryan", WorkingID = 3 },
};
var mergedList = from item1 in list1
join item2 in list2
on item1.ID equals item2.ID
select new MyItem() { ID = item1.ID, FirstName = item1.FirstName, WorkingID = item2.WorkingID };
I think using Join then looping through the list would be more efficient. The below is practically the same as the answer provided by Florian Schmidinger:
A.AsEnumerable().Join(B.AsEnumerable(), a => a.ID, b => b.ID, (a, b) => new { a, b })
.ToList().ForEach(obj => { obj.a.WorkingID = obj.b.WorkingID; });

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