2016-05-04 75 views
1

我有一個列表,我想基於KeyValuePair列表進行過濾。 KeyValuePair中的所有鍵都存在於對象中。基於KeyValuePairs的過濾器列表

所以我們可以說我有這個類對象的列表:

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" 

是否有可能產生一些不同於那種LINQ謂詞KeyValuePair可用作list.Where(predicate),謂詞將與我寫list.Where(c => c.Name == "test" && c.Country == "SE")時相同?

或者我該如何處理?

+0

您是否嘗試了反射? – qxg

+0

不知道我會如何使用反射這個 –

+0

'var keySet = new Dictionary {{「Name」,「test」}}; Predicate condition =(c)=> c.Name == keySet [「Name」] && c.Country == keySet [「Country」]; list.Where(condition);'這是你感興趣的嗎? – CarbineCoder

回答

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

這使您可以有任意多個過濾器。

對於list中的每個itemAll謂詞將檢查每個過濾器的有效性。 item.GetType()獲取itemType(即關於您班級的信息)。 GetProperty(f.Key)獲取特定屬性的信息,以當前過濾器fKey命名。 GetValue(item)獲取當前item的屬性值。?是c#6的新功能,即它是對null的內聯檢查,即如果未找到該屬性,則不會嘗試執行GetValue - 這將引發NullReferenceException - 但會返回null。然後您必須將屬性值轉換爲string並將其與當前過濾器的Value進行比較。你也可以使用String :: Compare(或者你喜歡的其他比較方法)。

All只返回true如果滿足所有過濾器,否則返回false。所以這個查詢的結果將包含滿足所有過濾器在你的字典中的所有元素

+0

謝謝,完美的作品 –

+0

請注意,使用這種方法在大型列表中效果不佳。出於好奇,我將這個解決方案與我提出的解決方案(使用編譯後的表達式)進行了比較,並使用三個過濾器過濾了一個包含一百萬個條目的列表,這種方法需要**〜400 ms **,而我的方法需要**〜30 ms **。但是,對於小列表,這個解決方案會更好地工作(因爲編譯表達式需要一些時間)。 – fknx

+0

@fknx是的,當然,這種即時方法並不像預編譯謂詞那麼好。選擇哪一個取決於具體的用例。這只是表明,可以用單線解決問題, – derpirscher

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"); 
+0

我不想寫i => i.Name + i.Country,這應該由我的KeyValuePair生成。不過,我知道我不能簡單地使用字符串值來獲得lambda字段,如c => c.StringValue == StringValue –

+0

嗯...所以你想包裝謂詞在擴展方法? ...有幫助嗎? – War

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

0

你想從KeyValuePair,這是IEnumerable<KeyValuePair<string, string>>產生Predicate<filters>。所以這是

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

你可以使用這樣的方法:

void Main() 
{ 
    var keyValuePairs = new List<KeyValuePair> 
    { 
     new KeyValuePair {Key = "Name", Value = "Test"}, 
     new KeyValuePair {Key = "Age", Value = "42"}, 
     new KeyValuePair {Key = "Country", Value = "USA"} 
    }; 

    var list = new List<Filter>(); 
    list.Add(new Filter { Name = "Test", Age = "42", Country = "USA" }); 

    list.Where(keyValuePairs.ToPredicate<Filter>()).Dump(); 
} 

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

public class KeyValuePair 
{ 
    public string Key { get; set; } 

    public string Value { get; set; } 
} 

public static class KeyValuePairExtensions 
{ 
    public static Func<T, bool> ToPredicate<T>(this IEnumerable<KeyValuePair> keyValuePairs) 
    { 
     if (keyValuePairs == null || !keyValuePairs.Any()) 
      return t => false; // default value in case the enumerable is empty 

     var parameter = Expression.Parameter(typeof(T)); 

     var equalExpressions = new List<BinaryExpression>(); 

     foreach (var keyValuePair in keyValuePairs) 
     { 
      var propertyInfo = typeof(T).GetProperty(keyValuePair.Key); 

      var property = Expression.Property(parameter, propertyInfo); 

      var value = Expression.Constant(keyValuePair.Value, propertyInfo.PropertyType); 

      var equalExpression = Expression.Equal(property, value); 
      equalExpressions.Add(equalExpression); 
     } 

     var expression = equalExpressions.First(); 

     if (equalExpressions.Count > 1) 
     { 
      // combine expression with and 
      expression = Expression.AndAlso(equalExpressions[0], equalExpressions[1]); 

      for (var i = 2; i < equalExpressions.Count; i++) 
      { 
       expression = Expression.AndAlso(expression, equalExpressions[i]); 
      } 
     } 

     var lambda = (Func<T, bool>)Expression.Lambda(expression, parameter).Compile(); 
     return lambda; 
    } 
} 

您還可以擴展ToPredicate方法將KeyValuePairs的東西結合起來不是and