C# 中嵌套列表的 Enumerable.Select 不同

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 具有以下可能的预期值:0, 1, 2.

然后需要检查用户插入的所有值以防止出现一些不同的值。

假设用户向我们发送了一个国家(Country class 的对象),其列表已填充(并且也嵌套)。

如何获取用户插入的所有DistinctIdSeaLevel值?

我在想:

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

现在,我得到一个 Distict 插入值

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

检查

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

.SelectMany 将列表列表映射到单个列表(扁平化)

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

要获得“无效”海平面,您可以选择在循环通过海平面时收集它们。

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

这种深度链接是 SelectMany<T> 有用的地方:

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

我们想投影 IdSeaLevelCities 是一个列表,所以在某些时候你需要内部 Cities.Select() 但它可以在 [=17= 内部或之后] 这有效地展平了层次结构,以便所有嵌套的 Cities 成为一个列表,以下内容也有效:

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

我更喜欢在 SelectMany 中首先使用投影,因为我们永远不需要 City 对象的任何其他属性(第一个示例),但不同的应用程序和结构可能决定第二个表达式执行更好。

对于最后的比较,您的逻辑看起来没问题,另一种比较列表的方法是使用 except:

bool badRequest= SentIdSeaLevelSet
    .Except(AllowedIdSeaLevellist).Any();

这在功能上等同于您之前的比较并且有效,因为被比较的集合类型相同,运行时间可能稍微快一些,但在这个级别,您的决定基于代码可读性,这是一个主观主题它自己的,但我更喜欢 except 版本,特别是当我们比较列表时。