I have a collection of devices. For each device of this list, I have an inner list of zones. I want to group each device by zone, but I don't know how to do that in Linq, I want to get an IEnumerable<IGrouping<string, DeviceModel>>
as output.
The string if the zonename in my ZoneModel.
My device class:
public class DeviceModel
{
public List<ZoneModel> Zones { get; set; }
public string Serial { get; set; }
}
My Zone class:
public class ZoneModel
{
public string ZoneName { get; set; }
}
Now, I need to get a enumerable of group device by zone with this code:
List<DeviceModel> deviceList = new List<DeviceModel>();
... (device filled by some code)
IEnumerable<IGrouping<string, DeviceModel>> deviceByZoneGroups =
from dataInfo in listDatas.Where(d => d.Zones != null && d.Zone.Any())
group dataInfo by dataInfo.Zones into zoneGroup
orderby zoneGroup.Key
select zoneGroup;
CodePudding user response:
It looks like you want to group objects based on unique items in a property list.
This solution 1st uses a SelectMany
overload that ties back the original object to the expanded property list to achieve the grouping you are asking for. It flattens out the lists of Zones that belongs to each Device in one list. But with the overload, it maintains the relationship to it's original parent object. It basically reverses the relationship from Device with many Zones to Zone with many Devices.
Then 2nd it uses an overload of GroupBy which allows you to map each result of the grouping, so that only a list of Device objects is returned. This maps IGrouping<string, anonymous type<string, DeviceModel>>
to IGrouping<string, DeviceModel>
which is what you asked for.
This should be your complete answer:
void Main()
{
// Setup some test data
var devices = GetTestData();
// The main solution
var group = devices
.SelectMany(d => d.Zones, (device, zone) =>
new
{
ZoneName = zone.ZoneName,
Device = device
})
.GroupBy(d => d.ZoneName, x => x.Device);
// Just for visibility
group
.Select(d => $"{d.Key}::{(string.Join(",", d.Select(x => x.Serial)))}")
.ToList()
.ForEach(x => Console.WriteLine(x));
// This returns:
// Living Room::12,34
// Entryway::12,56
}
public class DeviceModel
{
public List<ZoneModel> Zones { get; set; }
public string Serial { get; set; }
}
public class ZoneModel
{
public string ZoneName { get; set; }
}
public static List<DeviceModel> GetTestData()
{
return
new List<DeviceModel>
{
new DeviceModel
{
Serial = "12",
Zones = new List<ZoneModel>
{
new ZoneModel { ZoneName = "Living Room" },
new ZoneModel { ZoneName = "Entryway" },
}
},
new DeviceModel
{
Serial = "34",
Zones = new List<ZoneModel>
{
new ZoneModel { ZoneName = "Living Room" }
}
},
new DeviceModel
{
Serial = "56",
Zones = new List<ZoneModel>
{
new ZoneModel { ZoneName = "Entryway" }
}
}
};
}
CodePudding user response:
You can use SelectMany and GroupBy
public static IEnumerable<IGrouping<string, DeviceModel>> GetGrouped(IEnumerable<DeviceModel> deviceModels)
=> deviceModels.SelectMany(i => i.Zones.Select(j => new { j.ZoneName, i })).GroupBy(i => i.ZoneName, i => i.i);