I have a Dictionary which I want to filter by different conditions, e.g.
IDictionary<string, string> result = collection.Where(r => r.Value == null).ToDictionary(r => r.Key, r => r.Value);
I would like to pass the Where clause as a parameter to a method that performs the actual filtering, e.g.
private static IDictionary<T1, T2> Filter<T1, T2>(Func<IDictionary<T1, T2>, IDictionary<T1, T2>> exp, IDictionary<T1, T2> col)
{
return col.Where(exp).ToDictionary<T1, T2>(r => r.Key, r => r.Value);
}
This does not compile, though.
I have tried to call this method by using
Func<IDictionary<string, string>, IDictionary<string, string>> expression = r => r.Value == null;
var result = Filter<string, string>(expression, collection);
What am I doing wrong?
Where wants a Func<TSource, bool>, in your case Func<KeyValuePair<TKey, TValue>, bool>.
Furthermore, your return type of the method is incorrect. It should use T1 and T2 instead of string. Additionally, it is better to use descriptive names for the generic parameters. Instead of T1 and T2 I use the same names as the dictionary - TKey and TValue:
private static IDictionary<TKey, TValue> Filter<TKey, TValue>(
Func<KeyValuePair<TKey, TValue>, bool> exp, IDictionary<TKey, TValue> col)
{
return col.Where(exp).ToDictionary(r => r.Key, r => r.Value);
}
If you look at the constructor for the Where extension method you will see
Func<KeyValuePair<string, string>, bool>
So this is what you need to filter by, try this extension method.
public static class Extensions
{
public static IDictionairy<TKey, TValue> Filter<TKey, TValue>(this IDictionary<TKey, TValue> source, Func<KeyValuePair<TKey, TValue>, bool> filterDelegate)
{
return source.Where(filterDelegate).ToDictionary(x => x.Key, x => x.Value);
}
}
Call as
IDictionary<string, string> dictionairy = new Dictionary<string, string>();
var result = dictionairy.Filter((x => x.Key == "YourValue"));
Related
I want to create a new class that will wrap the current .net ConcurrentDictionary so that the Add delegate of the GetOrAdd\AddOrUpdate will be ensured to be called only once. I've seen a couple of solutions on the net and the main one is to wrap the TValue with lazy so that many lazy items may be added but only one will survive and invoke it's value factory.
here is what I come up with:
public class LazyConcurrentDictionary<TKey, TValue>
{
private readonly ConcurrentDictionary<TKey, Lazy<TValue>> concurrentDictionary;
public LazyConcurrentDictionary()
{
this.concurrentDictionary = new ConcurrentDictionary<TKey, Lazy<TValue>>();
}
public TValue GetOrAdd(TKey key, Func<TKey, TValue> valueFactory)
{
var lazyResult = this.concurrentDictionary.GetOrAdd(key, k => new Lazy<TValue>(() => valueFactory(k), LazyThreadSafetyMode.ExecutionAndPublication));
return lazyResult.Value;
}
public TValue AddOrUpdate(TKey key, Func<TKey, TValue> addFactory, Func<TKey, TValue> updateFactory)
{
// this one fails with "Cannot convert lambda expression to type 'System.Lazy' because it is not a delegate type"
var lazyResult = this.concurrentDictionary.AddOrUpdate(key, (k) => new Lazy<TValue>( () => addFactory(k), LazyThreadSafetyMode.ExecutionAndPublication), updateFactory);
return lazyResult.Value;
}
}
My issue is with the AddOrUpdate signature, I get "Cannot convert lambda expression to type 'System.Lazy' because it is not a delegate type"
What am I doing wrong?
I think you missunderstood meaning of the updateFactory for this function. It is function from TKey,TValue to TValue, not from TKey to TValue, it should calculate an updated value from an old one.
So right syntax should perhaps be like:
public TValue AddOrUpdate(TKey key, Func<TKey, TValue> addFactory,
Func<TValue, TValue> updateFactory)
{
var lazyResult = this.concurrentDictionary.AddOrUpdate(key,
(k) => new Lazy<TValue>(() => addFactory(k),
LazyThreadSafetyMode.ExecutionAndPublication),
(k,v)=>new Lazy<TValue>(()=>updateFactory(v.Value)))
);
return lazyResult.Value;
}
Or even (depending on how do you wnat to use it):
public TValue AddOrUpdate(TKey key, Func<TKey, TValue> addFactory,
Func<TKey, TValue, TValue> updateFactory)
{
var lazyResult = this.concurrentDictionary.AddOrUpdate(key,
(k) => new Lazy<TValue>(() => addFactory(k), LazyThreadSafetyMode.ExecutionAndPublication),
(k,v)=>new Lazy<TValue>(()=>updateFactory(k, v.Value))
);
return lazyResult.Value;
}
I would like to create an extension method that will allow me to call ToSerializableDictionary(p => p.ID) instead of .ToDictionary(p => p.ID) in the following LINQ context. Though I'm not sure what class i'm supposed to be making an extension method for to replace ToDictionary<T>.
response.attributes = (
from granuleGroup in groups
let granuleRow = granuleGroup.First().First()
select new USDAttributes()
{
id = (int)granuleRow["id"],
...
attributes =
(
...
).ToDictionary(p => p.ID) <--** LINE IN QUESTION **
}
).ToList();
My SerializableDictionary class taken from here is so that I may serialize dictionary objects in my webservice to return hash tables that play nice with JSON.
Initially I was creating an extension method for IDictionary so I can do something like this: ...).ToDictionary(p => p.ID).ToSerializableDictionary(); But this has been a complete failure because it's my first time creating extension methods and I don't know what I'm doing.
public static class CollectionExtensions
{
public static SerializableDictionary<string, object> ToSerializableDictionary(this IDictionary<string,object> sequence)
{
SerializableDictionary<string, object> sDic = new SerializableDictionary<string, object>();
foreach (var item in sequence)
{
}
return sDic;
}
}
public static SerializableDictionary<TKey, T> ToSerializableDictionary<TKey, T>(this IEnumerable<T> seq, Func<T, TKey> keySelector)
{
var dict = new SerializableDictionary<TKey, T>();
foreach(T item in seq)
{
TKey key = keySelector(item);
dict.Add(key, item);
}
return dict;
}
Actually the class you provided has a handy constructor for doing this, so you can actually do
attributes = new SerializableDictionary( (
...
).ToDictionary(p => p.ID) );
But here you go with the extension method (again using that constructor):
public static partial class Extension {
public static SerializableDictionary<T, Q> ToSerializableDictionary(
this IDictionary<T, Q> d) {
return new SerializableDictionary(d);
}
}
Lee's response is the correct answer but just to offer another approach you could try this slightly terser version:
public static SerializableDictionary<TKey, T> ToSerializableDictionary<TKey, T>(this IEnumerable<T> seq, Func<T, TKey> keySelector)
{
var dict = seq.ToDictionary(keySelector);
//since SerializableDictionary can accept an IDictionary
return new SerializableDictionary<TKey, T>(dict);
}
Personally however I'd consider an even simpler approach and use JSON.Net for this task - it works perfectly, is ridiculously simple to use and is incredibly fast. I believe Microsoft have even switched to using JSON.Net in MVC3 (or perhaps 4?) for these reasons. Heartily recommended
I have attempted to create an extension method that looks like this...
public static IEnumerable<T> Distinct<T>(this IEnumerable<T> value, IEnumerable<T> compareTo, Func<T, object> compareFieldPredicate)
{
return value.Where(o => !compareTo.Exists(p => compareFieldPredicate.Invoke(p) == compareFieldPredicate.Invoke(o)));
}
The idea is that I would be able to do something like this...
IEnumerable<MyCollection> distinctValues = MyCollection.Distinct(MyOtherCollection, o => o.ID); //Note that o.ID is a guid
Now at this point I would have expected to have only my distinct items returned to me but what I found is that this was never the case.
Upon further research breaking down this method using the following code.
Guid guid1 = Guid.NewGuid();
Guid guid2 = new Guid(guid1.ToString());
Func<MyObject, object> myFunction = o => o.ID;
Func<MyObject, object> myFunction1 = o => o.ID;
bool result = myFunction(MyObject) == myFunction1(MyObject);
//result = false
I have found that infact even if the Guids are the same the comparison will always return false.
What is the cause of this?
Your problem is that you're boxing the Guids into Objects before you compare them. Consider this code:
Guid g1 = Guid.NewGuid();
var g2 = g1;
Console.WriteLine(g1 == g2);
object o1 = g1;
object o2 = g2;
Console.WriteLine(o1 == o2);
That actually outputs:
true
false
Since "o1" and "o2", while equal to the same Guid, are not the same object.
If you truly want your "Distinct" extension method to not be tied to a specific type (like Guid), you can do this:
public static IEnumerable<TItem> Distinct<TItem, TProp>(this IEnumerable<TItem> value, IEnumerable<TItem> compareTo, Func<TItem, TProp> compareFieldPredicate)
where TProp : IEquatable<TProp>
{
return value.Where(o => !compareTo.Any(p => compareFieldPredicate(p).Equals(compareFieldPredicate(o))));
}
bool result = (guid1==guid2); //result -> true
You can try to change return type Object to GUID in myfunction and myfunction1
Func<MyObject, Guid> myFunction = o => o.ID;
Func<MyObject, Guid> myFunction1 = o => o.ID;
Otherwise, the return value (true) is boxed to Object, and Reference equality is checked, which is false.
Change to use
Func<MyObject, Guid> myFunction = o => o.ID;
Func<MyObject, Guid> myFunction1 = o => o.ID;
It's because your function was defined as
Func<MyObject, object>
The Guid returned by myFunction and myFunction1 will be boxed in two different obejcts. See here for boxing and unboxing feature in .NET
Therefore, when the comparison was done, two different objects are compared.
The default implementation of Equals in object is doing reference equality check. It's not checking the boxed values. See here for more details on how object.Equals implemented.
As others have said, your compareFieldPredicate returns an object and its operator == uses object.ReferenceEquals, rather than object.Equals, so your code always checks for object identity rather than equality.
One solution to this would be to use object.Equals method instead of operator ==:
public static IEnumerable<T> Distinct<T>(
this IEnumerable<T> value,
IEnumerable<T> compareTo,
Func<T, object> compareFieldPredicate
)
{
return value.Where(o => !compareTo.Exists(
p => object.Equals(compareFieldPredicate(p), compareFieldPredicate(o))
));
}
A better solution uses the default comparer for the actual key type, eliminating boxing if the type implements IEquatable interface for itself:
public static IEnumerable<T> Distinct<T, TKey>(
this IEnumerable<T> value,
IEnumerable<T> compareTo,
Func<T, TKey> compareFieldPredicate
)
{
return value.Where(o => !compareTo.Exists(
p => EqualityComparer<TKey>.Default.Equals(compareFieldPredicate(p), compareFieldPredicate(o))
));
}
However, most of the functionality of your Distinct method is already implemented by
Enumerable.Except LINQ method.
You can rewrite your implementation in terms of Enumerable.Except by providing an implementation of IEqualityComparer:
private class KeyEqualityComparer<T, TKey> : IEqualityComparer<T>
{
private readonly Func<T, TKey> _keySelector;
public KeyEqualityComparer(Func<T, TKey> keySelector)
{ _keySelector = keySelector; }
public int GetHashCode(T item)
{ return _keySelector(item).GetHashCode(); }
public bool Equals(T x, T y)
{ return EqualityComparer<TKey>.Default.Equals(_keySelector(x), _keySelector(y)); }
}
public static IEnumerable<T> ExceptBy<T, TKey>(
this IEnumerable<T> first,
IEnumerable<T> second,
Func<T, TKey> keySelector
)
{
return first.Except(second, new KeyEqualityComparer<T, TKey>(keySelector));
}
If you change the lambda to return a Guid, then it works:
Func<MyObject, Guid> myFunction = o => o.ID;
Func<MyObject, Guid> myFunction1 = o => o.ID;
Given the following code, I am having trouble returning a Dictionary.
[JsonProperty]
public virtual IDictionary<Product, int> JsonProducts
{
get
{
return Products.ToDictionary<Product, int>(x => x.Key, v => v.Value);
}
}
public virtual IDictionary<Product, int> Products { get; set; }
I get the following errors..
'System.Collections.Generic.IDictionary' does not contain a definition for 'ToDictionary' and the best extension method overload 'System.Linq.Enumerable.ToDictionary(System.Collections.Generic.IEnumerable, System.Func, System.Collections.Generic.IEqualityComparer)' has some invalid arguments
cannot convert from 'lambda expression' to 'System.Func'
cannot convert from 'lambda expression' to 'System.Collections.Generic.IEqualityComparer
There is nothing special about the Product class. it is simply defined as
class Product
{
public virtual int Id { get; set; }
public virtual String Name { get; set; }
}
Why do you use
Products.ToDictionary<Product, int>(x => x.Key, v => v.Value)
instead of just
Products.ToDictionary(x => x.Key, v => v.Value)
?
That's because
public static Dictionary<TKey, TElement> ToDictionary<TSource, TKey, TElement>(
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector,
Func<TSource, TElement> elementSelector
);
Take a look to the number (3) and types of generic type parameters (Func).
which means that you need to call it:
Products.ToDictionary<KeyValuePair<Product, int>, Product, int>(x => x.Key, v => v.Value);
Don't specify the generic type parameters explicitly. The types in ToDictionary<T1, T2> are not T1 = TKey and T2 = TValue (where TKey is the type of the key of the resulting dictionary and TValue is the type of the resulting value in the dictionary).
The overload of ToDictionary that accepts two generic type parameters has T = TSource and V = TKey. Here, TSource = KeyValuePair<Product, int>. Further, you are invoking the overload of ToDictionary that has two parameters. The first parameter is a map from T1 -> T2 and the second is an IEqualityComparer<T2>. But x => x.Key is not a map from KeyValuePair<Product, int> to int and v => v.Value is not an IEqualityComparer<int>.
When you don't specify the generic type parameters explicitly, the compiler inspects the types of x => x.Key and v => v.Value and looks at the various overloads of ToDictionary. There are four
ToDictionary<TSource, TKey>(IEnumerable<TSource>, Func<TSource, TKey>)
ToDictionary<TSource, TKey>(IEnumerable<TSource>, Func<TSource, TKey>, IEqualityComparer<TKey>)
ToDictionary<TSource, TKey, TElement>(IEnumerable<TSource>, Func<TSource, TKey>, Func<TSource, TElement>)
ToDictionary<TSource, TKey, TElement>(IEnumerable<TSource>, Func<TSource, TKey>, Func<TSource, TElement>, IEqualityComparer<TKey>)
Note that it can immediately rule out 1. and 4. because they have the wrong number of parameters (2 and 4 respectively whereas you are invoking an overload that needs three parameters (the third is the hidden first parameter because you are invoking an extension method)). It can rule out 2. because the last parameter can not be converted to an IEqualityComparer<T> for any T. This leaves the last overload. It is able to deduce that x => x.Key is a Func<KeyValuePair<Product, int>, Product>, that v => v.Value is a Func<KeyValuePair<Product, int>, int> and therefore that you are invoking
ToDictionary<KeyValuePair<Product, int>, Product, int>(
IEnumerable<KeyValuePair<Product, int>>,
Func<KeyValuePair<Product, int>, Product>,
Func<KeyValuePair<Product, int>, int>
)
If you wanted to specify the type parameters explicitly you would have to say
Products.ToDictionary<KeyValuePair<Product, int>, Product, int>(
x => x.Key,
v => v.Value
);
If you're not actually cloning the Product instances, you can just do:
public virtual IDictionary<Product, int> JsonProducts
{
get
{
return new Dictionary(Products);
}
}
My scenario,
how to convert List<KeyValuePair<string, string>> into IDictionary<string, string>?
Very, very simply with LINQ:
IDictionary<string, string> dictionary =
list.ToDictionary(pair => pair.Key, pair => pair.Value);
Note that this will fail if there are any duplicate keys - I assume that's okay?
Or you can use this extension method to simplify your code:
public static class Extensions
{
public static IDictionary<TKey, TValue> ToDictionary<TKey, TValue>(
this IEnumerable<KeyValuePair<TKey, TValue>> list)
{
return list.ToDictionary(x => x.Key, x => x.Value);
}
}
Use ToDictionary() extension method of the Enumerable class.
You can also use the constructor overload of Dictionary<TKey,TValue> that takes an IEnumerable<KeyValuePair<TKey,TValue>> as parameter.
var list = new List<KeyValuePair<int, string>>();
var dictionary = new Dictionary<int, string>(list);