Search code examples
c#linqselectdistinct

Distinct of Enumerable.Select of nested List in C#


public class Country
{
    public List<State> States { get; set; } = new List<State>();
}

public class State
{
    public List<City> Cities { get; set; } = new List<City>();
}

public class City
{
    public decimal IdSeaLevel { get; set; }
}

IdSeaLevel Has these possible expected values: 0, 1, 2.

Then is needed to check al values inserted by user to prevent some different value.

Suppose that the user send us an country (object of Country class) with its list filled (and nested too).

How to get all Distinct IdSeaLevel inserted value by the user?

I was thinking like:

List<decimal> AllowedIdSeaLevellist = new List<decimal>(new decimal[] { 0, 1, 2 });

Now, I get a Distict inserted Values

HashSet<decimal> SentIdSeaLevelSet = country.States
                .Select(s => s.Cities.IdSeaLevel).ToHashSet();

Check

bool badRequest= SentIdSeaLevelSet
    .Where(s => AllowedIdSeaLevellist.All(a => a != s)).Any();

Solution

  • .SelectMany will map List of lists into single list (flattened)

    var allSeaLevels = country.States
       .SelectMany(s => s.Cities)
       .Select(city => city.SeaLevelId)
       .ToHashSet();
    

    To get "invalid" sea levels you can alternatively to gather them while looping through sealevels.

    var validSeaLevels = new[] { 0, 1, 2 }.ToHashSet();
    
    var invalidSeaLevels = country.States
       .SelectMany(s => s.Cities)
       .Select(city => city.SeaLevelId)
       .Where(level => validSeaLevels.Contains(level) == false)
       .ToArray();
    
    if (invalidSeaLevels.Any())
    {
        return BadRequest(invalidSeaLevels);
    }