c# - 如何使用 linq 找到存在特定字符串的列表?
问题描述
我有一个包含两个列表的类
public class SchemaView
{
public int Version { get; set; }
public IEnumerable<EntityView> Entities { get; set; }
public IEnumerable<RelationView> Relations { get; set; }
}
当我有多个版本时,我将有一个带有升序版本号的 IEnumerable。
我正在寻找 linq 查询,它可以找到我具有属性 ==“某物”的实体或关系?
但是我将如何选择,两者都在哪里?
目前我有这样的东西,它只查看一个列表。
.Select(x => x.Entities )
.Where(x => x.InternalName == entityName)
.SelectMany(x => x.Attributes)
.Select(x => x.InternalName)
.ToList();
可以在某处添加或条件?以便它在关系中搜索一个名为某事物的内部名称?
上面的在关系列表中搜索失败,但是这个搜索不能包含在同一个 linq 中,还是我需要为此创建一个单独的 linq,或者可能是有条件的。
它可以做得更有效吗?
.Entities.Any(x => x.InternalName == entityName) ? .Entities
.FirstOrDefault(x => x.InternalName == entityName).Attributes .Select(x => x.InternalName) : .Relations.FirstOrDefault(x => x.InternalName == entityName).Attributes.Select(x => x.InternalName);
解决方案
所以你有一个序列SchemaViews
,每个 SchemaView 都有零个或多个Entities
,零个或多个Relations
。
你写了:
我正在寻找 linq 查询,它可以找到具有属性 ==“某物”的实体或关系?
是否只有一个实体/与属性的关系 == 某物?或者您的 SchemaViews 是否可以具有多个具有此属性值的实体和关系。您是在寻找实体和关系中的任何属性,还是寻找某些特定属性,例如属性InternalName
?并且只有实体有这个属性,还是关系也有一个InternalName
?或者您想为关系使用不同的属性?
一旦你找到了这样一个实体或关系,你想要完整的实体/关系,还是你想选择它的一些属性?
这么少的文字里有这么多的问题。考虑编辑您的问题并编写一个明确的要求。
我想你的意思是:
要求
给定一个 SchemaView 序列,其中每个 SchemaView 有零个或多个实体和零个或多个关系,每个实体都有一个 TKey 类型的属性 PropertyA,每个关系都有一个相同类型 TKey 的属性 PropertyB。还给定一个 TKey 类型的对象
value
,给我所有 SchemaViews 的所有实体和关系的串联,这些 SchemaViews 的 PropertyA 或 PropertyB 等于value
。
问题当然是 Relation 不是 Entity,因此您不能将它们放在一个序列中,否则它将是 的序列objects
,这可能不是您想要的。显然你还需要一个elementSelectorA
选择实体的属性和一个elementSelectorB
选择关系的属性,并且两个选择的元素应该是相同的类型。
您当然可以使用Concat
, 来连接实体和关系:
IEnumerable<SchemaView> schemaViews = ...
IEnumerable<TResult> selectedEntities = schemaViews.SelectMany(...).Where(...)
IEnumerable<TResult> selectedRelations = schemaView.SeelctMany(...).Where(...)
IEnumerable<TResult> result = selectedEntities.Concat(selectedRelations);
你是对的,如果你要SelectMany
枚举所有实体,并SelectMany
枚举所有关系,你将不得不枚举schemaViews
两次。
如果您创建自己的扩展方法,则可以使所有序列仅枚举一次。使用扩展方法,您可以像使用标准现有 LINQ 方法一样使用它。
如果您不熟悉扩展方法,请参阅扩展方法揭秘
有两种可能:
- 使用 SchemaViews 专门针对此问题制作扩展方法,
- 或者制作一个通用方法,您可以将其用于具有两个子序列的每个序列,这使其成为
SelectMany
两个子类的某种形式。
如果您认为这是一个非常具体的问题,您只会在 SchemaVies 中看到,请选择第一个解决方案。如果您认为您还必须为其他课程解决此问题,请选择第二个解决方案
SchemaView 解决方案
以下解决方案将最多枚举每个 SchemaView / Entity / Relation:
public static IEnumerable<MyResultClass> SelectDoubleMany(
this IEnumerable<SchemaView> schemaViews
string internalName)
{
foreach (var schemaView in schemaViews)
{
foreach (var entity in schemaView.Entities)
{
if (entity.PropertyA == internalName)
{
// create a MyResultClass, using properties from entity
MyResultClass result = new MyResultClass
{
Id = entity.Id,
Name = entity.Name,
...
};
yield return result;
}
}
foreach (var relation in schemaView.Relations)
{
if (relation.PropertyB == internalName)
{
// create a MyResultClass, using properties from relation
MyResultClass result = new MyResultClass
{
Id = relation.Id,
Name = relation.Name,
...
};
yield return result;
}
}
}
}
用法:
string internalName = ...
IEnumerable<SchemaView> schemaViews = ...
IEnumerable<MyResultClass> results = schemaViews.SelectDoubleMany(internalName);
您甚至可以将它与其他 LINQ 语句交织在一起:
var results = schemaVies.Where(schamaView => schemaView.Id == 42)
.SelectDoubleMany(internalName)
.GroupBy(myResult => myResult.Id)
.FirstOrDefault();
一旦找到一个实体或关系,这将停止枚举
为了简单起见,我没有使用任何 LINQ。我认为这是最有效的方法(嗯,除了低级 GetEnumerator / MoveNext)。仅枚举您在最终结果中实际使用的元素。
通用解决方案
如果你想要它作为通用方法,你将需要很多参数
- 输入 `IEnumerable 源。
- 每个 TSource 都有两个类型的属性:
IEnumerable<TSub1>
和 `IEnumerable. 你需要两个 collectionSelector,就像在 SelectMany - 每个 TSub1 都有一个类型的属性
TKey
;每个 TSub2 都有一个相同类型的属性TKey
,你需要一个 keySelector 就像在Join
- 您只需要 TKey 值等于某个输入值的项目。
- 您需要一个参数来将每个剩余的 TSub1 转换为 TResult
- 您需要一个参数来将每个剩余的 TSub2 转换为 TResult
- 最后:对于完整的通用:你需要一个
IEqualityComparer<TKey>
我的,这是一个相当长的参数列表。但是随后您可以使用它来 SelectDoubleMany 每个带有两个子集合的集合。
这些参数类似于 SelectMany 与 Where 的组合,只是一切都是双重的。第一个没有EqualityComparer,和很多LINQ方法一样,只会调用另一个重载。
public static IEnumerable<TResult> SelectDoubleMany<TSource, Tsub1, Tsub2, TKey, TResult>(
IEnumerable<TSource> source,
// Two subcollection selectors, like in SelectMany:
Func<TSource, TSub1> collectionSelector1,
Func<STrouce, TSub2> CollectionSelector2,
// the two KeySelectors, like in Join:
Func<Tsub1, TKey> key1Selector,
Func<Tsub2, TKey> key2Selector,
// the "InternalName" that you want
TKey value,
// Two resultSelectors that convert Tsub1 and Tsub2 into TResult
Func<Tsub1, TResult> resultSelector1,
Func<Tsub1, TResult> resultSelector2)
{
// call the overload with an equalitycomparer:
return source.SelectDoubleMany(
collectionSelector1,
collectionSelector2,
keySelector1,
keySelector2,
value,
resultSelector1,
resultSelector2,
null); // <== null equalityComparer
}
做真正事情的过载。如果相等比较器为空,则使用默认相等比较器
public static IEnumerable<TResult> SelectDoubleMany<TSource, Tsub1, Tsub2, TKey, TResult>(
IEnumerable<TSource> source,
// Two subcollection selectors, like in SelectMany:
Func<TSource, IEnumerable<Tsub1>> collectionSelector1,
Func<TSource, IEnumerable<Tsub2>> CollectionSelector2,
// the two KeySelectors, like in Join:
Func<Tsub1, TKey> key1Selector,
Func<Tsub2, TKey> key2Selector,
// the "InternalName" that you want
TKey value,
// Two resultSelectors that convert Tsub1 and Tsub2 into TResult
Func<Tsub1, TResult> resultSelector1,
Func<Tsub1, TResult> resultSelector2,
IEqualityComparer<TKey> comparer)
{
// If the equality comparer is null, use the default comparer:
if (comparer == null)
{
comparer = EqualityComparer<TKey>.Default;
}
// code is similar to the code above, only generic:
foreach (TSource sourceElement in source)
{
IEnumerable<Tsub1> subCollection1 = collectionSelector1(sourceElement);
foreach(Tsub1 subCollectionElement in subCollection1)
{
TKey key = keySelector1(subCollectionElement);
if (comparer.Equals(key, value))
{
TResult result = result1Selector(subCollectionElement);
yield return result;
}
}
IEnumerable<Tsub2> subCollection2 = collectionSelector2(sourceElement);
foreach(Tsub2 subCollectionElement in subCollection2)
{
TKey key = keySelector1(subCollectionElement);
if (comparer.Equals(key, value))
{
TResult result = result1Selector(subCollectionElement);
yield return result;
}
}
}
}
虽然这非常有效:每个元素将最多枚举一次,只枚举您需要的项目,我不确定您是否会重用这样的通用方法。
推荐阅读
- mysql - 如何使用子查询更新 Mysql 查询
- python-3.x - Authlib:json.decoder.JSONDecodeError:期望值:第 1 行第 1 列(字符 0)
- spring-boot - Spring Boot Hibernate JPA @OneToOne 双向 StackOverflowError
- python - Asyncio 协程返回 <_GatheringFuture pending> 而不是实际值
- reactjs - 嵌套路由器中的链接无法导航到父路由器的路由
- algorithm - 排序点算法
- python - 为什么当我打开一个文本文件时会这样说,“参数应该是整数或无,而不是'str'”
- python - 我收到一条错误消息,上面写着 AttributeError: 'str' object has no attribute 'read' 而且我不知道如何修复它
- laravel - Laravel Nova 限制资源查看
- ruby-on-rails - 发生概率为 x% 的最佳算法?