C#,Linq,过滤一个列表,无论其属性是否出现在另一个列表中
如果一个列表的元素属性出现在另一个列表中,我将尝试筛选该列表。 以下是我的例子:C#,Linq,过滤一个列表,无论其属性是否出现在另一个列表中,c#,linq,C#,Linq,如果一个列表的元素属性出现在另一个列表中,我将尝试筛选该列表。 以下是我的例子: public class Detail { public int Id { get; set; } public string Material { get; set; } public string Title { get; set; } public string Duration { get; set; } publi
public class Detail
{
public int Id { get; set; }
public string Material { get; set; }
public string Title { get; set; }
public string Duration { get; set; }
public string FileName { get; set; }
public string FileLocation{ get; set; }
}
我有一个dataList=List()
adisplayList=[“Title”、“Duration”、“FileName”]
和一个filterString=“test”
对于正常Linq,我们有:
dataList.Where(x x=> x.Title.Contains(filterString) || x.Duration.Contains(filterString) ||x.FileName.Contains(filterString))
但我的任务是以更编程的方式对其进行过滤,如果细节属性出现在displayList上,我想过滤dataList。有什么办法吗
如果显示列表
不是很优雅,但很直截了当:
dataList.Where(x =>
displayList.Contains(nameof(Detail.Title)) && x.Title.Contains(filterString) ||
displayList.Contains(nameof(Detail.Duration)) && x.Duration.Contains(filterString) ||
...
)
下面的解决方案需要更多的编码,但是可以对每个类和要检查的每个类型重用它 我们不会只针对类细节,也不会只针对过滤器字符串, 我将使其成为
IEnumerable
的通用扩展方法,这样您就可以将其与其他LINQ方法相结合。看
首先我会给出签名,然后我写代码:
就像大多数LINQ函数一样:您可以提供比较器,但不必提供;在这种情况下,将使用默认比较器:
public static IEnumerable<TSource> Filter<TSource, TKey>(
this IEnumerable<TSource> source,
IEnumerable<string> propertyNames,
TKey filterValue)
{
// only call the overload with a null comparer:
return Filter(source, propertyNames, filterValue, null);
}
public static IEnumerable<TSource> Filter<TSource, TKey>(
this IEnumerable<TSource> source,
IEnumerable<string> propertyNames,
TKey filterValue,
IEqualityComparer comparer)
{
// TODO: exceptions if input null
if (comparer == null) comparer = EqualityComparer<TKey>.Default;
// TODO: implement
}
有些人不喜欢收益,您也可以继续LINQ语句:
// From every PropertyInfo, make a keySelector, similar to GroupBy
IEnumerable<Func<TSource, TKey>> keySelectors = propertyInfos
.Select<PropertyInfo, Func<TSource, TKey>> (propertyInfo =>
x => (TKey)propertyInfo.GetValue(x));
var result = dbLibraryContext.Books
.Where(book => ...)
.Select(book => new Detail() {....})
.Filter(propertyNames, filterValue)
.GroupBy(detail => detail.Material);
您是否在任何属性中搜索
“test”
,该属性的名称在displayList
中指定(linq中缺少此位)?是@Sinatr,您有什么解决方案吗?谢谢您的评论,但是因为displayList可能会更改,所以我想要一个类似于``dataList.Where(x=>displayList.ForEach(y=>)`` displayList的循环-这不是问题。您只需像我一样对所有属性执行可能出现在displayList
中的操作。如前所述,这并不优雅。其他人可能会用委托字典或其他东西提供更好的答案。您可以使用反射来获取属性列表并获取它们的值,但这会很慢。尽管这也是一个问题因为你还没有说任何关于性能的事情,所以请不要回答。
// From every PropertyInfo, make a keySelector, similar to GroupBy
IEnumerable<Func<TSource, TKey>> keySelectors = propertyInfos
.Select<PropertyInfo, Func<TSource, TKey>> (propertyInfo =>
x => (TKey)propertyInfo.GetValue(x));
// From every KeySelector and the filterValue, create a Predicate, like in Where
IEnumerable<Func<TSource, bool>> predicates = keySelectors
.Select<Func<TSource, TKey>, Func<TSource, bool>>(
keySelector => sourceItem => comparer.Equals(keySelector(sourceItem), filterValue));
IEnumerable<TSource> filteredValues = source.Where(sourceItem =>
predicates.All(predicate => predicate(sourceItem);
return filteredValues;
string filterValue = "Test";
IEnumerable<string> propertyNames = new string[] {"Title", "FileName"};
IEnumerable<Detail> details = ...
IEnumerable<Detail> detailsWithTitleFileNameEqualsTest = details.Filter(
propertyNames,
filterValue);
IEnumerable<Detail> detailsWithTitleFileNameEqualsTest = details.Filter(
propertyNames,
filterValue,
StringComparer.OrdinalIgnoreCase);
var result = dbLibraryContext.Books
.Where(book => ...)
.Select(book => new Detail() {....})
.Filter(propertyNames, filterValue)
.GroupBy(detail => detail.Material);