I'd like to get one list of IDs from all nested entities.
Code:
// Entities
class Floor
{
public int Id { get; set; }
public ICollection<Room> Rooms { get; set; } = new List<Room>();
}
class Room
{
public int Id { get; set; }
public ICollection<Chair> Chairs { get; set; } = new List<Chair>();
}
class Chair
{
public int Id { get; set; }
}
// Setup
var floor = new Floor() { Id = 1000 };
var room = new Room() { Id = 100 };
var chair = new Chair() { Id = 10 };
room.Chairs.Add(chair);
floor.Rooms.Add(room);
var floors = new List<Floor>() { floor };
// Select all IDs
var ids = floors.???
Expected result:
{ 10, 100, 1000 }
What I've tried. It selects IDs only from the deepest level, not all of them:
// Select all IDs
var ids = floors
.SelectMany(f => f.Rooms)
.SelectMany(r => r.Chairs)
.Select(ch => ch.Id)
.ToList();
CodePudding user response:
SelectMany
is what you need together with Append
:
var ids = floors
.SelectMany(f => f.Rooms
.SelectMany(r => r.Chairs
.Select(c => c.Id).Append(r.Id)).Append(f.Id));
CodePudding user response:
Your current code flattens the hierarchy to collection of Chair
and selects only their ids.
With pure LINQ you can do via nesting SelectMany
and using Append
/Prepend
:
var ids = floors
.SelectMany(f => f.Rooms
.SelectMany(r => r.Chairs
.Select(ch => ch.Id) // select chairs ids
.Append(r.Id)) // append "current" room id to it's collection of chair ids
.Append(f.Id)) // append "current" floor id to it's collection of rooms and chairs ids
.ToList();
CodePudding user response:
An ugly hack would be to append "fakes" to the child rooms/chairs with the ID of their parent:
var ids = floors
.SelectMany(f => f.Rooms.Append(new Room { Id = f.Id }))
.SelectMany(r => r.Chairs.Append(new Chair { Id = r.Id }))
.Select(ch => ch.Id)
.ToList();
CodePudding user response:
It can be done with a recursive helper function like this:
IEnumerable<int> CollectIds<T>(T x) => x switch {
Floor f => f.Rooms.SelectMany(CollectIds).Prepend(f.Id),
Room r => r.Chairs.SelectMany(CollectIds).Prepend(r.Id),
Chair c => Enumerable.Repeat(c.Id, 1),
_ => throw new NotSupportedException()
};
Usage:
var ids = floors
.SelectMany(CollectIds)
.ToList();
You could consider extracting this function into a common interface to avoid the awkward type switch and the recursion.