过滤列表基于KeyValuePairs

时间:2016-05-04 12:46:48

标签: c# lambda

我有一个列表,我想根据KeyValuePairs列表进行过滤。 KeyValuePair中的所有键都存在于对象中。

所以,让我说我有一个来自这个类的对象列表:

public class filters
{
    public string Name { get; set; }
    public string Age { get; set; }
    public string Country { get; set; }
}

我有一个KeyValuePair:

Key: "Name", Value: "test"
Key: "Country", Value: "SE"

是否可以从KeyValuePair生成某种可用作list.Where(predicate)的LINQ谓词,谓词与我写list.Where(c => c.Name == "test" && c.Country == "SE")时的谓词相同?

或者我该如何处理?

6 个答案:

答案 0 :(得分:1)

作为一个单行:

var filters = new Dictionary<string, string>{{"Name", "test"}, {"Country", "SE"}};
var result = list.Where(item => filters.All(f => (string)(item.GetType().GetProperty(f.Key)?.GetValue(item)) == f.Value));

这使您可以拥有无​​限数量的过滤器。

对于item中的每个listAll谓词都会检查每个过滤器的有效性。 item.GetType()获取Type的{​​{1}}(即有关您班级的信息)。 item获取特定属性的信息,该信息以当前过滤器GetProperty(f.Key)的{​​{1}}命名。 Key获取当前f的属性值。 GetValue(item)是c#6的新功能,即它是item的内联检查,即如果找不到该属性,则不会尝试执行? - 这会引发null - 但返回GetValue。然后,您必须将属性值强制转换为NullReferenceException,并将其与当前过滤器的null进行比较。您还可以使用String :: Compare(或您喜欢的任何其他比较方法)。

如果满足所有过滤器,

string仅返回Value,否则返回false。因此,此查询的结果将包含符合词典中所有过滤器的所有元素

答案 1 :(得分:0)

我可能在这里误解了你,但这样做是你想要的:

// say I have a list of ilters like this 
// assume there are actually some filters in here though
var filterCollection = new List<filters>() 

// build dictionary of key values
var keyedSet = filterCollection.ToDictionary(i => i.Name + i.Country, i => i);

// query them using key ...
var filterItem = keyedSet["testSE"];

...或者您可以将谓词包装在扩展方法中......

public IEnumerable<filters> ByNameAndCountry(this IEnumerable<filters> collection, string name, string country)
{
     return collection.Where(i => i.Name == name && i => i.Country == country);
}

...完成后你可以像这样过滤原始列表......

var result = filterCollection.ByNameAndCountry("test", "ES");

答案 2 :(得分:0)

这样的东西?通过反思获得Propertyname并进行相等检查。

Func<filters, IEnumerable<KeyValuePair<string, string>>, bool> filter = (filters, pairs) =>
{
    foreach (var valuePair in pairs)
    {
        if (filters.GetType().GetProperty(valuePair.Key).GetValue(filters) != valuePair.Value)
        {
            return false;
        }
    }
    return true;
};

List<filters> list = new List<filters>();
list.Add(new filters() { Name = "Name1", Country = "DE"});
list.Add(new filters() { Name = "Name2", Country = "SE"});

var element = list.FirstOrDefault(x => filter(x, new List<KeyValuePair<string, string>>() {
    new KeyValuePair<string, string>("Name", "Name2"),
    new KeyValuePair<string, string>("Country", "SE"),
}));

Console.WriteLine(element.Name);

答案 3 :(得分:0)

这应该可以解决问题:

void Main()
{
    List<Filter> filters = new List<Filter>() {
        new Filter {Name = "Filter1", Age = 1, Country ="De"},
        new Filter {Name = "Filter2", Age = 2, Country ="Fr"},
        new Filter {Name = "Filter3", Age = 3, Country ="It"},
        new Filter {Name = "Filter4", Age = 4, Country ="Es"},
    };

    KeyValuePair<string, string> kvp = new KeyValuePair<string, string>("Filter1", "De");

    var result = filters.AsQueryable().Where (GetPredicate(kvp));
    result.Dump();
}
//Create the predicate as an expression, which takes a Filter as input and a kvp as a parameter
private static Expression<Func<Filter, bool>> GetPredicate(KeyValuePair<string,string> kvp)
{
        return (f) => f.Name == kvp.Key && f.Country == kvp.Value;
}

结果:

enter image description here

答案 4 :(得分:0)

您希望从KeyValuePair生成Predicate<filters>,即IEnumerable<KeyValuePair<string, string>>。所以它是

Func<IEnumerable<KeyValuePair<string, string>>, Predicate<filters>>

使用反射枚举KeyValuePair.Key中列出的All属性,并检查每个属性值是否与KeyValuePair.Value匹配。完整代码就像

var lists = new List<filters> { new filters { Name = "abc", Country = "def" } };

Func<IEnumerable<KeyValuePair<string, string>>, Predicate<filters>> predicateBuilder =
( keyValueParis ) => filter => ( from kp in keyValueParis
                                 let pi = typeof( filters ).GetProperty( kp.Key )
                                 select pi.GetValue( filter ) == kp.Value )
                              .All( r => r );

var predicates = new List<KeyValuePair<string, string>>
{
    new KeyValuePair<string, string>("Name", "abc" ),
    new KeyValuePair<string, string>("Country", "def")
};
Predicate<filters> predicate = predicateBuilder( predicates );

Console.WriteLine( lists.FindAll(predicate).Count);

答案 5 :(得分:0)

你可以使用这样的方法:

ToPredicate

您还可以扩展KeyValuePairs方法,将and与{{1}}之外的其他内容合并。