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 的对象),其列表已填充(并且也嵌套)。
如何获取用户插入的所有Distinct
IdSeaLevel
值?
我在想:
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()
我们想投影 IdSeaLevel
但 Cities
是一个列表,所以在某些时候你需要内部 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 版本,特别是当我们比较列表时。
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 的对象),其列表已填充(并且也嵌套)。
如何获取用户插入的所有Distinct
IdSeaLevel
值?
我在想:
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()
我们想投影 IdSeaLevel
但 Cities
是一个列表,所以在某些时候你需要内部 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 版本,特别是当我们比较列表时。