ToDictionary not working as expected - c#

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

Related

Create an extension method derived from ToDictionary()

I want to create an extension method derived from ToDictionary(). Currently to reach the desired result, I do this:
ObjectAttributes = model.ObjectAttributes.ToDictionary(
oa => oa.Attribute.Name, oa => oa.ToWrapper<ObjectAttributeWrapper>());
So I use the following ToDictionary signature:
public static Dictionary<TKey, TElement> ToDictionary<TSource, TKey, TElement>(
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector,
Func<TSource, TElement> elementSelector);
I wish to know if it is possible to do this?
ObjectAttributes = model.ObjectAttributes.ToDictionaryWrapper<ObjectAttributeWrapper>(
oa => oa.Attribute.Name);
here is the current implementation but it does not work obviously:
public static Dictionary<TKey, TWrapper> ToDictionaryWrapper<TWrapper, TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector) where TSource : BaseModel where TWrapper : IBaseWrapper
{
return source.ToDictionary(keySelector, model => model.ToWrapper<TWrapper>());
}
EDIT :
Implementation of ToWrapper():
public static TWrapper ToWrapper<TWrapper>(this BaseModel model) where TWrapper : IBaseWrapper
{
if (model == null)
return default;
var type = typeof(TWrapper);
if (_wrapperParents.ContainsKey(type) && _wrapperParents[type].Id == model.Id)
return (TWrapper)_wrapperParents[type];
else
return (TWrapper)GetConstructor<TWrapper>().Invoke(new object[] { model });
}
public static IEnumerable<TWrapper> ToListWrapper<TWrapper>(this IEnumerable models) where TWrapper : IBaseWrapper
{
var _models = models as IEnumerable<BaseModel>;
if (_models == null)
return default;
return _models.Select(model => model.ToWrapper<TWrapper>());
}
Generally, for a single list of generic constraints, you can only provide all arguments explicitely or have all arguments resolved automatically. You can't have partially provided constraints.
So your options are the following (I assume TWrapper would have a property WrappedObject):
Provide all parameters as is
Reduce the number of generic parameters (you can even make the key type non-generic if its always the same)
// signature
Dictionary<TKey, TWrapper> ToDictionaryWrapper<TWrapper, TKey>(
this IEnumerable<BaseModel> source,
Func<BaseModel, TKey> keySelector)
where TWrapper : IBaseWrapper;
// call
model.ObjectAttributes
.ToDictionaryWrapper<ObjectAttributeWrapper, string>(oa => oa.Attribute.Name);
Split your function calls in two parts, where one part is explicit and the other implicit
// signature
IEnumerable<TWrapper> Wrap<TWrapper>(this IEnumerable<BaseModel> source)
where TWrapper : IBaseWrapper;
Dictionary<TKey, TWrapper> ToDictionaryWrapper<TWrapper, TKey>(
this IEnumerable<TWrapper> source,
Func<BaseModel, TKey> keySelector)
where TWrapper : IBaseWrapper;
// call
model.ObjectAttributes
.Wrap<ObjectAttributeWrapper>()
.ToDictionaryWrapper(oa => oa.Attribute.Name);
Don't bother with the custom ToDictionaryWrapper, just use the Wrap-Function and the Framework ToDictionary
// call (signature for Wrap same as above)
model.ObjectAttributes
.Wrap<ObjectAttributeWrapper>()
.ToDictionary(w => w.WrappedObject.Attribute.Name);

Passing an expression as a parameter

I'm trying to build a generic GroupBy Method, I guess it should be something like this
var result = GenericGroupBy<List<DTO>>(dataList, g=>g.Id);
public object GenericGroupBy<T>(object data, Func<T, bool> groupByExpression)
{
return ((List<T>)data).GroupBy(groupByExpression);
}
But I can not make it work.
How to pass expression like g=>g.Id?
Currently there are two problems:
Your method expects a Func<T, bool> and I suspect g => g.Id fails that because your Id property isn't a bool
You're currently specifying List<DTO> as the type argument, when I suspect you really want just DTO.
Given your comments, this will work:
var result = GenericGroupBy<DTO>(dataList, g => g.Id);
public object GenericGroupBy<T>(object data, Func<T, int> groupByExpression)
{
return ((List<T>)data).GroupBy(groupByExpression);
}
... but I'd make it a bit more general unless you always want to group by int:
var result = GenericGroupBy<DTO, int>(dataList, g => g.Id);
public object GenericGroupBy<TElement, TKey>
(object data, Func<TElement, TKey> groupByExpression)
{
return ((IEnumerable<TElement>)data).GroupBy(groupByExpression);
}
Note how I've also changed the cast from List<T> to IEnumerable<T> - you don't need it to be a List<T>, so why cast to that?

How to use Dictionary in a delegate

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

Actual type cannot be inferred for the method accepting Expression<Func>

I'm writing a small library for parsing resultsets of stored procedures (basically, very specific kind of ORM).
I have class
class ParserSetting<T> // T - type corresponding to particular resultset
{
...
public ParserSettings<TChild> IncludeList<TChild, TKey>(
Expression<Func<T, TChild[]>> listProp,
Func<T, TKey> foreignKey,
Func<TChild, TKey> primaryKey,
int resultSetIdx)
{ ... }
}
Here method IncludeList specifies that result set no. resultSetIdx should be parsed as if it consisted of TChild objects and assigned to property defined by listProp expression (as an array).
I'm using it as follows:
class Parent
{
public int ParentId {get;set;}
...
public Child[] Children{get;set;}
}
class Child
{
public int ParentId {get;set;}
...
}
ParserSettings<Parent> parentSettings = ...;
parentSettings.IncludeList(p => p.Children, p=> p.ParentId, c => c.ParentId, 1);
This method works as a charm. So far, so good.
I want to support different types of collections in addition to arrays. So, I'm trying to add following method:
public ParserSettings<TChild> IncludeList<TChild, TListChild, TKey>(
Expression<Func<T, TListChild>> listProp,
Func<T, TKey> foreignKey,
Func<TChild, TKey> primaryKey,
int resultSetIdx)
where TListChild: ICollection<TChild>, new()
{ ... }
However, when I'm trying to use it as follows:
class Parent
{
public int ParentId {get;set;}
...
public List<Child> Children{get;set;}
}
class Child
{
public int ParentId {get;set;}
...
}
ParserSettings<Parent> parentSettings = ...;
parentSettings.IncludeList(p => p.Children, p=> p.ParentId, c => c.ParentId, 1);
C# compiler issues error message `"The type arguments for method ParserSettings.IncludeList(...) cannot be inferred".
It works if I specify types explicitly:
parentSettings.IncludeList<Child, List<Child>, int>(
p => p.Children, p=> p.ParentId, c => c.ParentId, 1);
but that's somewhat defeats the purpose making call too complex.
Is there a way to achieve type inference for this scenario?
I also noticed that the capability of the C# compiler to infer types does not work "around corners".
In your case, you do not need any additional method, just rewrite Child[] as ICollection<TChild> and the signature will match arrays, lists, etc:
public ParserSettings<TChild> IncludeList<TChild, TKey>(
Expression<Func<T, ICollection<TChild>>> listProp,
Func<T, TKey> foreignKey,
Func<TChild, TKey> primaryKey,
int resultSetIdx) {
...
}

Unexpected behaviour when comparing GUIDs in .NET

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;

Categories