Can I use Linq's Except() with a lambda expression comparer? - c#

I know I can call linq's Except and specify a custom IEqualityComparer, but implementing a new Comparer class for each data type seems like an overkill for this purpose. Can I use a lambda expression to provide the equality function, like when I use Where or other LINQ functions?
If I can't, is there an alternative?

For any one still looking; here's another way of implementing a custom lambda comparer.
public class LambdaComparer<T> : IEqualityComparer<T>
{
private readonly Func<T, T, bool> _expression;
public LambdaComparer(Func<T, T, bool> lambda)
{
_expression = lambda;
}
public bool Equals(T x, T y)
{
return _expression(x, y);
}
public int GetHashCode(T obj)
{
/*
If you just return 0 for the hash the Equals comparer will kick in.
The underlying evaluation checks the hash and then short circuits the evaluation if it is false.
Otherwise, it checks the Equals. If you force the hash to be true (by assuming 0 for both objects),
you will always fall through to the Equals check which is what we are always going for.
*/
return 0;
}
}
you can then create an extension for the linq Except an Intersect that take in lambda's
/// <summary>
/// Returns all items in the first collection except the ones in the second collection that match the lambda condition
/// </summary>
/// <typeparam name="T">The type</typeparam>
/// <param name="listA">The first list</param>
/// <param name="listB">The second list</param>
/// <param name="lambda">The filter expression</param>
/// <returns>The filtered list</returns>
public static IEnumerable<T> Except<T>(this IEnumerable<T> listA, IEnumerable<T> listB, Func<T, T, bool> lambda)
{
return listA.Except(listB, new LambdaComparer<T>(lambda));
}
/// <summary>
/// Returns all items in the first collection that intersect the ones in the second collection that match the lambda condition
/// </summary>
/// <typeparam name="T">The type</typeparam>
/// <param name="listA">The first list</param>
/// <param name="listB">The second list</param>
/// <param name="lambda">The filter expression</param>
/// <returns>The filtered list</returns>
public static IEnumerable<T> Intersect<T>(this IEnumerable<T> listA, IEnumerable<T> listB, Func<T, T, bool> lambda)
{
return listA.Intersect(listB, new LambdaComparer<T>(lambda));
}
Usage:
var availableItems = allItems.Except(filterItems, (p, p1) => p.Id== p1.Id);

Can you not use a .Where with a lambda that filters out your required values?
Example as requested:
static void Main(string[] args)
{
var firstCustomers = new[] { new Customer { Id = 1, Name = "Bob" }, new Customer { Id = 2, Name = "Steve" } };
var secondCustomers = new[] { new Customer { Id = 2, Name = "Steve" }, new Customer { Id = 3, Name = "John" } };
var customers = secondCustomers.Where(c => !firstCustomers.Select(fc => fc.Id).Contains(c.Id));
}
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
}

I don't think you can directly with the basic LINQ interfaces, but I've seen people implement a LambdaComparer class with extension methods which will help you do it.
Here's an example

Here's something simple I whipped up:
public class CustomComparer<TSource, TCompareType> : IEqualityComparer<TSource> where TSource : class
{
private readonly Func<TSource, TCompareType> getComparisonObject;
public CustomComparer(Func<TSource,TCompareType> getComparisonObject)
{
if (getComparisonObject == null) throw new ArgumentNullException("getComparisonObject");
this.getComparisonObject = getComparisonObject;
}
/// <summary>
/// Determines whether the specified objects are equal.
/// </summary>
/// <returns>
/// true if the specified objects are equal; otherwise, false.
/// </returns>
/// <param name="x">The first object of type <paramref name="T"/> to compare.
/// </param><param name="y">The second object of type <paramref name="T"/> to compare.
/// </param>
public bool Equals(TSource x, TSource y)
{
if (x == null)
{
return (y == null);
}
else if (y == null)
{
return false;
}
return EqualityComparer<TCompareType>.Default.Equals(getComparisonObject(x), getComparisonObject(y));
}
/// <summary>
/// Returns a hash code for the specified object.
/// </summary>
/// <returns>
/// A hash code for the specified object.
/// </returns>
/// <param name="obj">The <see cref="T:System.Object"/> for which a hash code is to be returned.
/// </param><exception cref="T:System.ArgumentNullException">The type of <paramref name="obj"/> is a reference type and <paramref name="obj"/> is null.
/// </exception>
public int GetHashCode(TSource obj)
{
return EqualityComparer<TCompareType>.Default.GetHashCode(getComparisonObject(obj));
}
}
Usage:
var myItems = allItems.Except(theirItems, new CustomComparer(item => item.Name));

Use an extension!
public static IEnumerable<T> Except<T, TKey>(this IEnumerable<T> items, IEnumerable<T> other,
Func<T, TKey> getKey)
{
return from item in items
join otherItem in other on getKey(item)
equals getKey(otherItem) into tempItems
from temp in tempItems.DefaultIfEmpty()
where ReferenceEquals(null, temp) || temp.Equals(default(T))
select item;
}
Source

Here' is l except r solution, based on a LINQ outer join technique:
from l in new[] { 1, 2, 3 }
join r in new[] { 2, 4, 5 }
on l equals r
into rr
where !rr.Any()
select l
Will yield: 1, 3

But you may try this. This is so much easy, I think code contains errors. Of course, the code is for small amounts, without LINQ translates to db etc.
public static IEnumerable<TSource> ExceptPredicate<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second, Func<TSource, TSource, bool> compare) {
foreach (var itmFirst in first) {
if (!second.Any(itmsecond => compare(itmFirst, itmsecond))) {
yield return itmFirst;
}
}
yield break;
}

Related

There are some way to select all the element recursively? [duplicate]

I am using Entity Framework (version 6) to map to a recursive hierarchy and it maps nicely.
My issue is that I want to recursively get ALL child nodes of a particular node in the hierarchy.
I get the child nodes quite easily using Linq:
var recursiveList = db.ProcessHierarchyItems
.Where(x => x.id == id)
.SelectMany(x => x.Children);
Does anybody know of a clean implementation, that will recursively get all children?
While it is possible to use a recursive method here, you can traverse this tree structure using an explicit stack instead to avoid using the stack space, which isn't always sufficient for large tree structures. Such a method is also very nice as an iterator block, and iterator blocks are much less expensive when recursive than regular methods, so this will perform better as well:
public static IEnumerable<T> Traverse<T>(this IEnumerable<T> items,
Func<T, IEnumerable<T>> childSelector)
{
var stack = new Stack<T>(items);
while(stack.Any())
{
var next = stack.Pop();
yield return next;
foreach(var child in childSelector(next))
stack.Push(child);
}
}
Thanks Servy, I expanded on your code a bit to allow for iterating single items, as well as collections. I came across when looking for a way to find out if an exception or any inner exceptions were of a certain type, but this will have many uses.
Here is a fiddle with examples, test cases, etc.
dotnetfiddle LinqTraversal
Just the helpers:
public static class LinqRecursiveHelper
{
/// <summary>
/// Return item and all children recursively.
/// </summary>
/// <typeparam name="T">Type of item.</typeparam>
/// <param name="item">The item to be traversed.</param>
/// <param name="childSelector">Child property selector.</param>
/// <returns></returns>
public static IEnumerable<T> Traverse<T>(this T item, Func<T, T> childSelector)
{
var stack = new Stack<T>(new T[] { item });
while (stack.Any())
{
var next = stack.Pop();
if (next != null)
{
yield return next;
stack.Push(childSelector(next));
}
}
}
/// <summary>
/// Return item and all children recursively.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="item"></param>
/// <param name="childSelector"></param>
/// <returns></returns>
public static IEnumerable<T> Traverse<T>(this T item, Func<T, IEnumerable<T>> childSelector)
{
var stack = new Stack<T>(new T[] { item });
while (stack.Any())
{
var next = stack.Pop();
//if(next != null)
//{
yield return next;
foreach (var child in childSelector(next))
{
stack.Push(child);
}
//}
}
}
/// <summary>
/// Return item and all children recursively.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="items"></param>
/// <param name="childSelector"></param>
/// <returns></returns>
public static IEnumerable<T> Traverse<T>(this IEnumerable<T> items,
Func<T, IEnumerable<T>> childSelector)
{
var stack = new Stack<T>(items);
while (stack.Any())
{
var next = stack.Pop();
yield return next;
foreach (var child in childSelector(next))
stack.Push(child);
}
}
}
I like more the linq way to do recursive.
public static IEnumerable<TReturn> Recursive<TItem, TReturn>(this TItem item, Func<TItem, IEnumerable<TReturn>> select, Func<TItem, IEnumerable<TItem>> recurrence)
{
return select(item).Union(recurrence(item).Recursive(select, recurrence));
}
Try this. While other answers enumerate the enumerable while building the enumerable, we should consider building it without enumerating it.
public static IEnumerable<T> SelectRecursively<T>(this T source, Func<T, IEnumerable<T>> selector)
{
return selector(source).SelectMany(x => x.SelectRecursively(selector).Prepend(x));
}
The simplest solution seems to introduce a recursive method. You can't get recursive just by LINQ itself:
IEnumerable<X> GetChildren(X x)
{
foreach (var rChild in x.Children.SelectMany(child => GetChildren(child)))
{
yield return rChild;
}
}
If you have lazy loading, then this should work:
var recursiveList = db.ProcessHierarchyItems
.Where(x => x.id == id)
.AsEnumerable()
.SelectMany(x => GetChildren(x));

Issue with remapping inner Lambda in current search LINQ

I'm using Automapper and three-layered architecture in my application. I have a method which takes Linq query (LambdaExpression) as a parameter.
To pass this parameter in another layer I need to remap it. So, I created an ExpressionExtension class, which is responsible for returning remapped Expression:
/// <summary>
/// A class which contains extension methods for <see cref="Expression"/> and <see cref="Expression{TDelegate}"/> instances.
/// </summary>
public static class ExpressionExtensions
{
/// <summary>
/// Remaps all property access from type <typeparamref name="TSource"/> to <typeparamref name="TDestination"/> in <paramref name="expression"/>.
/// </summary>
/// <typeparam name="TSource">The type of the source element.</typeparam>
/// <typeparam name="TDestination">The type of the destination element.</typeparam>
/// <typeparam name="TResult">The type of the result from the lambda expression.</typeparam>
/// <param name="expression">The <see cref="Expression{TDelegate}"/> to remap the property access in.</param>
/// <returns>An <see cref="Expression{TDelegate}"/> equivalent to <paramref name="expression"/>, but applying to elements of type <typeparamref name="TDestination"/> instead of <typeparamref name="TSource"/>.</returns>
public static Expression<Func<TDestination, TResult>> RemapForType<TSource, TDestination, TResult>(
this Expression<Func<TSource, TResult>> expression)
{
Contract.Requires(expression != null);
Contract.Ensures(Contract.Result<Expression<Func<TDestination, TResult>>>() != null);
var newParameter = Expression.Parameter(typeof(TDestination));
Contract.Assume(newParameter != null);
var visitor = new AutoMapVisitor<TSource, TDestination>(newParameter);
var remappedBody = visitor.Visit(expression.Body);
if (remappedBody == null)
{
throw new InvalidOperationException("Unable to remap expression");
}
return Expression.Lambda<Func<TDestination, TResult>>(remappedBody, newParameter);
}
}
And code for AutoMapVisitor:
/// <summary>
/// An <see cref="ExpressionVisitor"/> implementation which uses <see href="http://automapper.org">AutoMapper</see> to remap property access from elements of type <typeparamref name="TSource"/> to elements of type <typeparamref name="TDestination"/>.
/// </summary>
/// <typeparam name="TSource">The type of the source element.</typeparam>
/// <typeparam name="TDestination">The type of the destination element.</typeparam>
public class AutoMapVisitor<TSource, TDestination> : ExpressionVisitor
{
private readonly ParameterExpression _newParameter;
private readonly TypeMap _typeMap = Mapper.FindTypeMapFor<TSource, TDestination>();
/// <summary>
/// Initialises a new instance of the <see cref="AutoMapVisitor{TSource, TDestination}"/> class.
/// </summary>
/// <param name="newParameter">The new <see cref="ParameterExpression"/> to access.</param>
public AutoMapVisitor(ParameterExpression newParameter)
{
_newParameter = newParameter;
}
/// <summary>
/// Visits the children of the <see cref="T:System.Linq.Expressions.MemberExpression"/>.
/// </summary>
/// <returns>
/// The modified expression, if it or any subexpression was modified; otherwise, returns the original expression.
/// </returns>
/// <param name="node">The expression to visit.</param>
protected override Expression VisitMember(MemberExpression node)
{
var propertyMaps = _typeMap.GetPropertyMaps();
// Find any mapping for this member
var propertyMap = propertyMaps.SingleOrDefault(map => map.SourceMember == node.Member);
if (propertyMap == null)
{
return base.VisitMember(node);
}
var destinationProperty = propertyMap.DestinationProperty;
var destinationMember = destinationProperty.MemberInfo;
// Check the new member is a property too
var property = destinationMember as PropertyInfo;
if (property == null)
{
return base.VisitMember(node);
}
// Access the new property
var newPropertyAccess = Expression.Property(_newParameter, property);
return base.VisitMember(newPropertyAccess);
}
/// <summary>
///
/// </summary>
/// <typeparam name="T">Func of type TIn and result is TResult</typeparam>
/// <param name="node">Expression of Func with certain input and result types</param>
/// <returns></returns>
protected override Expression VisitLambda<T>(Expression<T> node)
{
//predicate.RemapForType<PersonViewModel, PersonDTO, bool>()
// считаем, что будет 1 параметр(объект) операции
var source = node.Parameters.FirstOrDefault();
// берем первый найденный маппинг источника
TypeMap typeMap = source == null
? null
: Mapper.GetAllTypeMaps().FirstOrDefault(tMap => tMap.SourceType == source.Type);
if (typeMap!=null)
{
var ts = typeMap.SourceType;
var td = typeMap.DestinationType;
var res = node.ReturnType;
return VisitLambda(node, ts, td, res);
//return node.RemapForType<ts,td,res>();
}
return base.VisitLambda<T>(node);
}
private Expression<Func<TOut,TResult>> VisitLambda<T,TIn, TOut, TResult>(Expression<T> node, TIn source, TOut destination, TResult result)
{
ParameterExpression pe = Expression.Parameter(typeof (TIn), "innerParam");
var value = typeof(T) as Func<TIn, TResult>;
Expression<Func<TIn, TResult>> input = Expression.Lambda<Func<TIn, TResult>>(Expression.Invoke(node.Body,node.Parameters)); // tried also without Expression.Invoke
return input.RemapForType<TIn, TOut, TResult>();
}
}
I understand that I need to override base Lambda Visitor and to call ExpressionExtension.RemapForType method once again to remap inner lambda. But how can I pass the right parameters to it? So, I decided to create generic method but nothing works right.
Could you help me to get the right solution.
E.g. something like that:
Expression<Func<TSource,TResult>> a = val => val.FullName == "ABC" && val.SomeEnumerableProperty.Any(inner => inner.City == "Moscow");
// Here we will get remapped lambda with TDestination class as a parameter and TDestination class properties remapped by AutoMapper as you saw in my posted code
Expression<Func<TDestination, TResult>> b = a.RemapForType<TSource,TDestination,TResult>();
And in I don't know which classes will be used as Source and Destination in inner Lambda because it is runtime. So the example is simply for you to understand what I want to do.
EDITED
So, I changed my Visitor in this variant:
/// <summary>
/// An <see cref="ExpressionVisitor"/> implementation which uses <see href="http://automapper.org">AutoMapper</see> to remap property access from elements of type <typeparamref name="TSource"/> to elements of type <typeparamref name="TDestination"/>.
/// </summary>
/// <typeparam name="TSource">The type of the source element.</typeparam>
/// <typeparam name="TDestination">The type of the destination element.</typeparam>
public class AutoMapVisitor<TSource, TDestination> : ExpressionVisitor
{
private readonly TypeMap _typeMap;
private readonly List<TypeMap> _typeMaps = new List<TypeMap>();
private readonly Dictionary<Type, ParameterExpression> parameterMap = new Dictionary<Type, ParameterExpression>();
private Dictionary<MemberInfo, Expression> memberMap = new Dictionary<MemberInfo, Expression>();
/// <summary>
/// Initialises a new instance of the <see cref="AutoMapVisitor{TSource, TDestination}"/> class.
/// </summary>
/// <param name="newParameter">The new <see cref="ParameterExpression"/> to access.</param>
public AutoMapVisitor(ParameterExpression newParameter)
{
_typeMap = Mapper.FindTypeMapFor<TSource, TDestination>();
Contract.Assume(_typeMap != null);
_typeMaps.Add(_typeMap);
_typeMaps.AddRange(FillNestedTypeMaps(_typeMap));
parameterMap.Add(newParameter.Type, newParameter); // main parameter which we don't need to recreate
foreach (TypeMap map in _typeMaps)
{
if (parameterMap.ContainsKey(map.DestinationType)) continue;
parameterMap.Add(map.DestinationType, Expression.Parameter(map.DestinationType, map.DestinationType.Name.ToLower()));
}
}
/// <summary>
/// находим все возможные маппинги текущей конфигурации вниз по иерархии свойств класса источника
/// </summary>
/// <param name="tMap">Объект конфигурации, содержащий информацию о типе источника и типе назначения</param>
private IEnumerable<TypeMap> FillNestedTypeMaps(TypeMap tMap)
{
List<TypeMap> result = new List<TypeMap>();
// 1 where: маппинг только между классами
// 2 where: маппинг не равен входящему и ReverseMap значению, например на входе: A -> B, тогда отпадут значения A -> B и B -> A из проверки
// 3 where: берем те свойства, тип которых совпадает с входящим типом источника или, если это обобщенный тип, с его аргументом и тоже самое для типа назначения
IEnumerable<PropertyMap> pMaps = tMap.GetPropertyMaps();
var list = Mapper.GetAllTypeMaps()
.Where(
map => map.SourceType.IsClass && map.DestinationType.IsClass)
.Where(map =>
!(map.Equals(tMap) ||
(map.SourceType == tMap.DestinationType && map.DestinationType == tMap.SourceType)))
.Where(
map =>
pMaps
.Any(
pi =>
{
var pis = pi.SourceMember as PropertyInfo;
if (pis == null) return false;
bool forSource = pis.PropertyType == map.SourceType ||
(pis.PropertyType.IsGenericType &&
pis.PropertyType.GetGenericArguments()[0] == map.SourceType);
bool forDestination = pi.DestinationPropertyType == map.DestinationType ||
(pi.DestinationPropertyType.IsGenericType &&
pi.DestinationPropertyType.GetGenericArguments()[0] == map.DestinationType);
return forSource && forDestination;
}))
.ToList();
if (list.Count > 0)
{
result.AddRange(list);
foreach (TypeMap typeMap in list)
{
result.AddRange(FillNestedTypeMaps(typeMap));
}
}
return result;
}
private Type Map(Type type)
{
var tMap = _typeMaps.FirstOrDefault(map => map.SourceType == type);
Contract.Assume(tMap != null);
return tMap.DestinationType;
}
private ParameterExpression Map(ParameterExpression parameter)
{
var mappedType = Map(parameter.Type);
ParameterExpression mappedParameter;
if (!parameterMap.TryGetValue(mappedType, out mappedParameter))
parameterMap.Add(mappedType, mappedParameter = Expression.Parameter(mappedType, parameter.Name));
return mappedParameter;
}
private Expression Map(MemberInfo mi, Expression exp)
{
Expression val;
if (!memberMap.TryGetValue(mi, out val))
{
foreach (PropertyMap propertyMap in
_typeMaps.Select(map => map.GetPropertyMaps().SingleOrDefault(m => m.SourceMember == mi))
.Where(propertyMap => propertyMap != null))
{
memberMap.Add(mi, val = Expression.PropertyOrField(exp, propertyMap.DestinationProperty.MemberInfo.Name));
break;
}
}
return val;
}
/// <summary>
/// Visits the children of the <see cref="T:System.Linq.Expressions.MemberExpression"/>.
/// </summary>
/// <returns>
/// The modified expression, if it or any subexpression was modified; otherwise, returns the original expression.
/// </returns>
/// <param name="node">The expression to visit.</param>
protected override Expression VisitMember(MemberExpression node)
{
var expression = Visit(node.Expression);
if (expression == node.Expression)
return node;
return Map(node.Member, expression);
}
protected override Expression VisitLambda<T>(Expression<T> node)
{
return Expression.Lambda(Visit(node.Body), node.Parameters.Select(Map));
}
protected override Expression VisitParameter(ParameterExpression node)
{
return Map(node);
}
protected override Expression VisitMethodCall(MethodCallExpression node)
{
// if static object and generic method
if (node.Object == null && node.Method.IsGenericMethod)
{
// Static generic method
var args = Visit(node.Arguments);
var genericArgs = node.Method.GetGenericArguments().Select(Map).ToArray();
var method = node.Method.GetGenericMethodDefinition().MakeGenericMethod(genericArgs);
return Expression.Call(method, args);
}
return base.VisitMethodCall(node);
}
}
And now after remapping it throws an Exception:
variable 'person' of type 'Reestr.DAL.Entities.Person' referenced from scope '', but it is not defined
It is interesting that this Exception is thrown only when we have an inner Lambda (e.g. method Any). But if we didn't include it, using only simple search conditions, everything works fine! Seems like there is missing reference on Enumerable property of mapped class or there is newly created parameter somewhere and I can't understand where it can be.
Could you give me any suggestions, please, how can I solve this issue! Thanks!
EDITED
As #MBoros mentioned Automapper supports such mapping out of the box (of course if there are no hard code sections). The problem is in ServiceStack.OrmLite!!! I've tried simply passing manually created query with inner subquery and it failed with an exception I've posted earlier...

Recursive Hierarchy - Recursive Query using Linq

I am using Entity Framework (version 6) to map to a recursive hierarchy and it maps nicely.
My issue is that I want to recursively get ALL child nodes of a particular node in the hierarchy.
I get the child nodes quite easily using Linq:
var recursiveList = db.ProcessHierarchyItems
.Where(x => x.id == id)
.SelectMany(x => x.Children);
Does anybody know of a clean implementation, that will recursively get all children?
While it is possible to use a recursive method here, you can traverse this tree structure using an explicit stack instead to avoid using the stack space, which isn't always sufficient for large tree structures. Such a method is also very nice as an iterator block, and iterator blocks are much less expensive when recursive than regular methods, so this will perform better as well:
public static IEnumerable<T> Traverse<T>(this IEnumerable<T> items,
Func<T, IEnumerable<T>> childSelector)
{
var stack = new Stack<T>(items);
while(stack.Any())
{
var next = stack.Pop();
yield return next;
foreach(var child in childSelector(next))
stack.Push(child);
}
}
Thanks Servy, I expanded on your code a bit to allow for iterating single items, as well as collections. I came across when looking for a way to find out if an exception or any inner exceptions were of a certain type, but this will have many uses.
Here is a fiddle with examples, test cases, etc.
dotnetfiddle LinqTraversal
Just the helpers:
public static class LinqRecursiveHelper
{
/// <summary>
/// Return item and all children recursively.
/// </summary>
/// <typeparam name="T">Type of item.</typeparam>
/// <param name="item">The item to be traversed.</param>
/// <param name="childSelector">Child property selector.</param>
/// <returns></returns>
public static IEnumerable<T> Traverse<T>(this T item, Func<T, T> childSelector)
{
var stack = new Stack<T>(new T[] { item });
while (stack.Any())
{
var next = stack.Pop();
if (next != null)
{
yield return next;
stack.Push(childSelector(next));
}
}
}
/// <summary>
/// Return item and all children recursively.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="item"></param>
/// <param name="childSelector"></param>
/// <returns></returns>
public static IEnumerable<T> Traverse<T>(this T item, Func<T, IEnumerable<T>> childSelector)
{
var stack = new Stack<T>(new T[] { item });
while (stack.Any())
{
var next = stack.Pop();
//if(next != null)
//{
yield return next;
foreach (var child in childSelector(next))
{
stack.Push(child);
}
//}
}
}
/// <summary>
/// Return item and all children recursively.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="items"></param>
/// <param name="childSelector"></param>
/// <returns></returns>
public static IEnumerable<T> Traverse<T>(this IEnumerable<T> items,
Func<T, IEnumerable<T>> childSelector)
{
var stack = new Stack<T>(items);
while (stack.Any())
{
var next = stack.Pop();
yield return next;
foreach (var child in childSelector(next))
stack.Push(child);
}
}
}
Try this. While other answers enumerate the enumerable while building the enumerable, we should consider building it without enumerating it.
public static IEnumerable<T> SelectRecursively<T>(this T source, Func<T, IEnumerable<T>> selector)
{
return selector(source).SelectMany(x => x.SelectRecursively(selector).Prepend(x));
}
I like more the linq way to do recursive.
public static IEnumerable<TReturn> Recursive<TItem, TReturn>(this TItem item, Func<TItem, IEnumerable<TReturn>> select, Func<TItem, IEnumerable<TItem>> recurrence)
{
return select(item).Union(recurrence(item).Recursive(select, recurrence));
}
The simplest solution seems to introduce a recursive method. You can't get recursive just by LINQ itself:
IEnumerable<X> GetChildren(X x)
{
foreach (var rChild in x.Children.SelectMany(child => GetChildren(child)))
{
yield return rChild;
}
}
If you have lazy loading, then this should work:
var recursiveList = db.ProcessHierarchyItems
.Where(x => x.id == id)
.AsEnumerable()
.SelectMany(x => GetChildren(x));

LINQ queries in a generic repository, LINQ to Entities does not recognize the method

I'm setting up a generic repository in my application and I'm having troubles with some LINQ queries.
With a non-generic one, this is what I would do:
private IObjectSet<T> _objectSet;
public List<int?> GetGroups()
{
List<object> objectGroups = new List<object>();
List<int?> intGroups = new List<int?>();
var r = (from n in _objectSet
select n.Group_ID).Distinct();
objectGroups = r.OrderBy(n => n.Value).ToList();
foreach (object value in objectGroups)
{
intGroups.Add((int?)value);
}
return intGroups;
}
Since this is a generic one of type T, when typing "n.", IntelliSense is not listing any options since the type of n is not explicitly defined (right?).
So here's what I have so far:
public interface IRepository<T> : IDisposable where T : class
{
IQueryable<T> Fetch();
IEnumerable<T> GetAll();
IEnumerable<T> GetAll(bool activeOnly);
IEnumerable<T> GetAll(string groupID, bool activeOnly);
IEnumerable<T> Find(Func<T, bool> predicate);
T Single(Func<T, bool> predicate);
T First(Func<T, bool> predicate);
List<int?> GetGroups();
int Add(T entity);
void Delete(T entity);
void Attach(T entity);
void SaveChanges();
void SaveChanges(SaveOptions options);
}
public class DataRepository<T> : IRepository<T> where T : class
{
/// <summary>
/// The context object for the database
/// </summary>
private ObjectContext _context;
private IEnumerable<T> _previousEntries;
private string _PKName;
/// <summary>
/// The IObjectSet that represents the current entity.
/// </summary>
private IObjectSet<T> _objectSet;
public DataRepository()
{
switch (typeof(T).Name)
{
case "CRM_Patient":
_context = new TheseEntities();
_PKName = "key_patient";
break;
case "CRM_Account":
_context = new ThoseEntities();
_PKName = "accountnumber";
break;
case "CRM_Supplier":
_context = new OtherEntities();
_PKName = "supplierid";
break;
default:
_context = new OtherEntities();
break;
}
_objectSet = _context.CreateObjectSet<T>();
_previousEntries = this.GetAll();
}
/// <summary>
/// Gets all records as an IQueryable
/// </summary>
/// <returns>An IQueryable object containing the results of the query</returns>
public IQueryable<T> Fetch()
{
return _objectSet;
}
/// <summary>
/// Gets all records as an IEnumberable
/// </summary>
/// <returns>An IEnumberable object containing the results of the query</returns>
public IEnumerable<T> GetAll()
{
return Fetch().AsEnumerable();
}
/// <summary>
/// Finds a record with the specified criteria
/// </summary>
/// <param name="predicate">Criteria to match on</param>
/// <returns>A collection containing the results of the query</returns>
public IEnumerable<T> Find(Func<T, bool> predicate)
{
return _objectSet.Where<T>(predicate);
}
public List<int?> GetGroups()
{
List<object> objectGroups = new List<object>();
List<int?> intGroups = new List<int?>();
//var r = (from n in _objectSet
// select n.GetType().GetProperty("Group_ID").GetValue(n, null)).Distinct();
var r = Fetch().Select(n => n.GetType().GetProperty("Group_ID").GetValue(n, null)).Distinct();
objectGroups = r.OrderBy(n => n.GetType().GetProperty("Value").GetValue(n, null)).ToList();
foreach (object value in r)
{
intGroups.Add((int?)value);
}
return intGroups;
}
/// <summary>
/// Gets a single record by the specified criteria (usually the unique identifier)
/// </summary>
/// <param name="predicate">Criteria to match on</param>
/// <returns>A single record that matches the specified criteria</returns>
public T Single(Func<T, bool> predicate)
{
return _objectSet.Single<T>(predicate);
}
/// <summary>
/// The first record matching the specified criteria
/// </summary>
/// <param name="predicate">Criteria to match on</param>
/// <returns>A single record containing the first record matching the specified criteria</returns>
public T First(Func<T, bool> predicate)
{
return _objectSet.First<T>(predicate);
}
/// <summary>
/// Deletes the specified entitiy
/// </summary>
/// <param name="entity">Entity to delete</param>
/// <exception cref="ArgumentNullException"> if <paramref name="entity"/> is null</exception>
public void Delete(T entity)
{
if (entity == null)
{
throw new ArgumentNullException("entity");
}
_objectSet.DeleteObject(entity);
}
/// <summary>
/// Deletes records matching the specified criteria
/// </summary>
/// <param name="predicate">Criteria to match on</param>
public void Delete(Func<T, bool> predicate)
{
IEnumerable<T> records = from x in _objectSet.Where<T>(predicate) select x;
foreach (T record in records)
{
_objectSet.DeleteObject(record);
}
}
/// <summary>
/// Dispose
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Dispose
/// </summary>
/// <param name="disposing">A boolean value indicating whether or not to dispose managed resources</param>
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (_context != null)
{
_context.Dispose();
_context = null;
}
}
}
}
Somehow, my reflection tricks seem to be executed in the LINQ statements, which obviously ends up throwing an exception when I assign a value for objectGroups.
LINQ to Entities does not recognize the method 'System.Object GetValue(System.Object, System.Object[])' method, and this method cannot be translated into a store expression.
Any ideas ? I really need to preserve the generic nature of the repository. All of the methods where I'm using Reflection are throwing exceptions...
Thanks much.
EDIT: Added most of the Repository's methods and the interface. Some methods might be missing from the class but done so to lighten reading :)
How about defining the Value and GroupID properties in an interface, and then making your entities implement this interface and adding this as a generic constraint?
Then your GetGroups method will be able to call these properties without reflection.

Getting multiple keys of specified value of a generic Dictionary?

It's easy to get the value of a key from a .NET generic Dictionary:
Dictionary<int, string> greek = new Dictionary<int, string>();
greek.Add(1, "Alpha");
greek.Add(2, "Beta");
string secondGreek = greek[2]; // Beta
But trying to get the keys given a value is not as straightforward because there could be multiple keys:
int[] betaKeys = greek.WhatDoIPutHere("Beta"); // expecting single 2
Okay, here's the multiple bidirectional version:
using System;
using System.Collections.Generic;
using System.Text;
class BiDictionary<TFirst, TSecond>
{
IDictionary<TFirst, IList<TSecond>> firstToSecond = new Dictionary<TFirst, IList<TSecond>>();
IDictionary<TSecond, IList<TFirst>> secondToFirst = new Dictionary<TSecond, IList<TFirst>>();
private static IList<TFirst> EmptyFirstList = new TFirst[0];
private static IList<TSecond> EmptySecondList = new TSecond[0];
public void Add(TFirst first, TSecond second)
{
IList<TFirst> firsts;
IList<TSecond> seconds;
if (!firstToSecond.TryGetValue(first, out seconds))
{
seconds = new List<TSecond>();
firstToSecond[first] = seconds;
}
if (!secondToFirst.TryGetValue(second, out firsts))
{
firsts = new List<TFirst>();
secondToFirst[second] = firsts;
}
seconds.Add(second);
firsts.Add(first);
}
// Note potential ambiguity using indexers (e.g. mapping from int to int)
// Hence the methods as well...
public IList<TSecond> this[TFirst first]
{
get { return GetByFirst(first); }
}
public IList<TFirst> this[TSecond second]
{
get { return GetBySecond(second); }
}
public IList<TSecond> GetByFirst(TFirst first)
{
IList<TSecond> list;
if (!firstToSecond.TryGetValue(first, out list))
{
return EmptySecondList;
}
return new List<TSecond>(list); // Create a copy for sanity
}
public IList<TFirst> GetBySecond(TSecond second)
{
IList<TFirst> list;
if (!secondToFirst.TryGetValue(second, out list))
{
return EmptyFirstList;
}
return new List<TFirst>(list); // Create a copy for sanity
}
}
class Test
{
static void Main()
{
BiDictionary<int, string> greek = new BiDictionary<int, string>();
greek.Add(1, "Alpha");
greek.Add(2, "Beta");
greek.Add(5, "Beta");
ShowEntries(greek, "Alpha");
ShowEntries(greek, "Beta");
ShowEntries(greek, "Gamma");
}
static void ShowEntries(BiDictionary<int, string> dict, string key)
{
IList<int> values = dict[key];
StringBuilder builder = new StringBuilder();
foreach (int value in values)
{
if (builder.Length != 0)
{
builder.Append(", ");
}
builder.Append(value);
}
Console.WriteLine("{0}: [{1}]", key, builder);
}
}
As everyone else has said, there's no mapping within a dictionary from value to key.
I've just noticed you wanted to map to from value to multiple keys - I'm leaving this solution here for the single value version, but I'll then add another answer for a multi-entry bidirectional map.
The normal approach to take here is to have two dictionaries - one mapping one way and one the other. Encapsulate them in a separate class, and work out what you want to do when you have duplicate key or value (e.g. throw an exception, overwrite the existing entry, or ignore the new entry). Personally I'd probably go for throwing an exception - it makes the success behaviour easier to define. Something like this:
using System;
using System.Collections.Generic;
class BiDictionary<TFirst, TSecond>
{
IDictionary<TFirst, TSecond> firstToSecond = new Dictionary<TFirst, TSecond>();
IDictionary<TSecond, TFirst> secondToFirst = new Dictionary<TSecond, TFirst>();
public void Add(TFirst first, TSecond second)
{
if (firstToSecond.ContainsKey(first) ||
secondToFirst.ContainsKey(second))
{
throw new ArgumentException("Duplicate first or second");
}
firstToSecond.Add(first, second);
secondToFirst.Add(second, first);
}
public bool TryGetByFirst(TFirst first, out TSecond second)
{
return firstToSecond.TryGetValue(first, out second);
}
public bool TryGetBySecond(TSecond second, out TFirst first)
{
return secondToFirst.TryGetValue(second, out first);
}
}
class Test
{
static void Main()
{
BiDictionary<int, string> greek = new BiDictionary<int, string>();
greek.Add(1, "Alpha");
greek.Add(2, "Beta");
int x;
greek.TryGetBySecond("Beta", out x);
Console.WriteLine(x);
}
}
Dictionaries aren't really meant to work like this, because while uniqueness of keys is guaranteed, uniqueness of values isn't. So e.g. if you had
var greek = new Dictionary<int, string> { { 1, "Alpha" }, { 2, "Alpha" } };
What would you expect to get for greek.WhatDoIPutHere("Alpha")?
Therefore you can't expect something like this to be rolled into the framework. You'd need your own method for your own unique uses---do you want to return an array (or IEnumerable<T>)? Do you want to throw an exception if there are multiple keys with the given value? What about if there are none?
Personally I'd go for an enumerable, like so:
IEnumerable<TKey> KeysFromValue<TKey, TValue>(this Dictionary<TKey, TValue> dict, TValue val)
{
if (dict == null)
{
throw new ArgumentNullException("dict");
}
return dict.Keys.Where(k => dict[k] == val);
}
var keys = greek.KeysFromValue("Beta");
int exceptionIfNotExactlyOne = greek.KeysFromValue("Beta").Single();
Maybe the easiest way to do it, without Linq, can be to loop over the pairs:
int betaKey;
foreach (KeyValuePair<int, string> pair in lookup)
{
if (pair.Value == value)
{
betaKey = pair.Key; // Found
break;
}
}
betaKey = -1; // Not found
If you had Linq, it could have done easily this way:
int betaKey = greek.SingleOrDefault(x => x.Value == "Beta").Key;
A dictionary doesn't keep an hash of the values, only the keys, so any search over it using a value is going to take at least linear time. Your best bet is to simply iterate over the elements in the dictionary and keep track of the matching keys or switch to a different data structure, perhaps maintain two dictionary mapping key->value and value->List_of_keys. If you do the latter you will trade storage for look up speed. It wouldn't take much to turn #Cybis example into such a data structure.
As I wanted a full fledged BiDirectional Dictionary (and not only a Map), I added the missing functions to make it an IDictionary compatible class. This is based on the version with unique Key-Value Pairs. Here's the file if desired (Most work was the XMLDoc through):
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Common
{
/// <summary>Represents a bidirectional collection of keys and values.</summary>
/// <typeparam name="TFirst">The type of the keys in the dictionary</typeparam>
/// <typeparam name="TSecond">The type of the values in the dictionary</typeparam>
[System.Runtime.InteropServices.ComVisible(false)]
[System.Diagnostics.DebuggerDisplay("Count = {Count}")]
//[System.Diagnostics.DebuggerTypeProxy(typeof(System.Collections.Generic.Mscorlib_DictionaryDebugView<,>))]
//[System.Reflection.DefaultMember("Item")]
public class BiDictionary<TFirst, TSecond> : Dictionary<TFirst, TSecond>
{
IDictionary<TSecond, TFirst> _ValueKey = new Dictionary<TSecond, TFirst>();
/// <summary> PropertyAccessor for Iterator over KeyValue-Relation </summary>
public IDictionary<TFirst, TSecond> KeyValue => this;
/// <summary> PropertyAccessor for Iterator over ValueKey-Relation </summary>
public IDictionary<TSecond, TFirst> ValueKey => _ValueKey;
#region Implemented members
/// <Summary>Gets or sets the value associated with the specified key.</Summary>
/// <param name="key">The key of the value to get or set.</param>
/// <Returns>The value associated with the specified key. If the specified key is not found,
/// a get operation throws a <see cref="KeyNotFoundException"/>, and
/// a set operation creates a new element with the specified key.</Returns>
/// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is null.</exception>
/// <exception cref="T:System.Collections.Generic.KeyNotFoundException">
/// The property is retrieved and <paramref name="key"/> does not exist in the collection.</exception>
/// <exception cref="T:System.ArgumentException"> An element with the same key already
/// exists in the <see cref="ValueKey"/> <see cref="Dictionary<TFirst,TSecond>"/>.</exception>
public new TSecond this[TFirst key]
{
get { return base[key]; }
set { _ValueKey.Remove(base[key]); base[key] = value; _ValueKey.Add(value, key); }
}
/// <Summary>Gets or sets the key associated with the specified value.</Summary>
/// <param name="val">The value of the key to get or set.</param>
/// <Returns>The key associated with the specified value. If the specified value is not found,
/// a get operation throws a <see cref="KeyNotFoundException"/>, and
/// a set operation creates a new element with the specified value.</Returns>
/// <exception cref="T:System.ArgumentNullException"><paramref name="val"/> is null.</exception>
/// <exception cref="T:System.Collections.Generic.KeyNotFoundException">
/// The property is retrieved and <paramref name="val"/> does not exist in the collection.</exception>
/// <exception cref="T:System.ArgumentException"> An element with the same value already
/// exists in the <see cref="KeyValue"/> <see cref="Dictionary<TFirst,TSecond>"/>.</exception>
public TFirst this[TSecond val]
{
get { return _ValueKey[val]; }
set { base.Remove(_ValueKey[val]); _ValueKey[val] = value; base.Add(value, val); }
}
/// <Summary>Adds the specified key and value to the dictionary.</Summary>
/// <param name="key">The key of the element to add.</param>
/// <param name="value">The value of the element to add.</param>
/// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> or <paramref name="value"/> is null.</exception>
/// <exception cref="T:System.ArgumentException">An element with the same key or value already exists in the <see cref="Dictionary<TFirst,TSecond>"/>.</exception>
public new void Add(TFirst key, TSecond value) {
base.Add(key, value);
_ValueKey.Add(value, key);
}
/// <Summary>Removes all keys and values from the <see cref="Dictionary<TFirst,TSecond>"/>.</Summary>
public new void Clear() { base.Clear(); _ValueKey.Clear(); }
/// <Summary>Determines whether the <see cref="Dictionary<TFirst,TSecond>"/> contains the specified
/// KeyValuePair.</Summary>
/// <param name="item">The KeyValuePair to locate in the <see cref="Dictionary<TFirst,TSecond>"/>.</param>
/// <Returns>true if the <see cref="Dictionary<TFirst,TSecond>"/> contains an element with
/// the specified key which links to the specified value; otherwise, false.</Returns>
/// <exception cref="T:System.ArgumentNullException"><paramref name="item"/> is null.</exception>
public bool Contains(KeyValuePair<TFirst, TSecond> item) => base.ContainsKey(item.Key) & _ValueKey.ContainsKey(item.Value);
/// <Summary>Removes the specified KeyValuePair from the <see cref="Dictionary<TFirst,TSecond>"/>.</Summary>
/// <param name="item">The KeyValuePair to remove.</param>
/// <Returns>true if the KeyValuePair is successfully found and removed; otherwise, false. This
/// method returns false if <paramref name="item"/> is not found in the <see cref="Dictionary<TFirst,TSecond>"/>.</Returns>
/// <exception cref="T:System.ArgumentNullException"><paramref name="item"/> is null.</exception>
public bool Remove(KeyValuePair<TFirst, TSecond> item) => base.Remove(item.Key) & _ValueKey.Remove(item.Value);
/// <Summary>Removes the value with the specified key from the <see cref="Dictionary<TFirst,TSecond>"/>.</Summary>
/// <param name="key">The key of the element to remove.</param>
/// <Returns>true if the element is successfully found and removed; otherwise, false. This
/// method returns false if <paramref name="key"/> is not found in the <see cref="Dictionary<TFirst,TSecond>"/>.</Returns>
/// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is null.</exception>
public new bool Remove(TFirst key) => _ValueKey.Remove(base[key]) & base.Remove(key);
/// <Summary>Gets the key associated with the specified value.</Summary>
/// <param name="value">The value of the key to get.</param>
/// <param name="key">When this method returns, contains the key associated with the specified value,
/// if the value is found; otherwise, the default value for the type of the key parameter.
/// This parameter is passed uninitialized.</param>
/// <Returns>true if <see cref="ValueKey"/> contains an element with the specified value;
/// otherwise, false.</Returns>
/// <exception cref="T:System.ArgumentNullException"><paramref name="value"/> is null.</exception>
public bool TryGetValue(TSecond value, out TFirst key) => _ValueKey.TryGetValue(value, out key);
#endregion
}
}
revised: okay to have some kind of find you would need something other than dictionary, since if you think about it dictionary are one way keys. that is, the values might not be unique
that said it looks like you're using c#3.0 so you might not have to resort to looping and could use something like:
var key = (from k in yourDictionary where string.Compare(k.Value, "yourValue", true) == 0 select k.Key).FirstOrDefault();
Dictionary class is not optimized for this case, but if you really wanted to do it (in C# 2.0), you can do:
public List<TKey> GetKeysFromValue<TKey, TVal>(Dictionary<TKey, TVal> dict, TVal val)
{
List<TKey> ks = new List<TKey>();
foreach(TKey k in dict.Keys)
{
if (dict[k] == val) { ks.Add(k); }
}
return ks;
}
I prefer the LINQ solution for elegance, but this is the 2.0 way.
Can't you create a subclass of Dictionary which has that functionality?
public class MyDict &lt TKey, TValue &gt : Dictionary &lt TKey, TValue &gt
{
private Dictionary &lt TValue, TKey &gt _keys;
public TValue this[TKey key]
{
get
{
return base[key];
}
set
{
base[key] = value;
_keys[value] = key;
}
}
public MyDict()
{
_keys = new Dictionary &lt TValue, TKey &gt();
}
public TKey GetKeyFromValue(TValue value)
{
return _keys[value];
}
}
EDIT: Sorry, didn't get code right first time.
The "simple" bidirectional dictionary solution proposed here is complex and may be be difficult to understand, maintain or extend. Also the original question asked for "the key for a value", but clearly there could be multiple keys (I've since edited the question). The whole approach is rather suspicious.
Software changes. Writing code that is easy to maintain should be given priority other "clever" complex workarounds. The way to get keys back from values in a dictionary is to loop. A dictionary isn't designed to be bidirectional.
Use LINQ to do a reverse Dictionary<K, V> lookup. But keep in mind that the values in your Dictionary<K, V> values may not be distinct.
Demonstration:
using System;
using System.Collections.Generic;
using System.Linq;
class ReverseDictionaryLookupDemo
{
static void Main()
{
var dict = new Dictionary<int, string>();
dict.Add(4, "Four");
dict.Add(5, "Five");
dict.Add(1, "One");
dict.Add(11, "One"); // duplicate!
dict.Add(3, "Three");
dict.Add(2, "Two");
dict.Add(44, "Four"); // duplicate!
Console.WriteLine("\n== Enumerating Distinct Values ==");
foreach (string value in dict.Values.Distinct())
{
string valueString =
String.Join(", ", GetKeysFromValue(dict, value));
Console.WriteLine("{0} => [{1}]", value, valueString);
}
}
static List<int> GetKeysFromValue(Dictionary<int, string> dict, string value)
{
// Use LINQ to do a reverse dictionary lookup.
// Returns a 'List<T>' to account for the possibility
// of duplicate values.
return
(from item in dict
where item.Value.Equals(value)
select item.Key).ToList();
}
}
Expected Output:
== Enumerating Distinct Values ==
Four => [4, 44]
Five => [5]
One => [1, 11]
Three => [3]
Two => [2]
Dictionary<string, string> dic = new Dictionary<string, string>();
dic["A"] = "Ahmed";
dic["B"] = "Boys";
foreach (string mk in dic.Keys)
{
if(dic[mk] == "Ahmed")
{
Console.WriteLine("The key that contains \"Ahmed\" is " + mk);
}
}
Many of these answers are now outdated, here is a modern C# approach, using LINQ
Since values aren't necessarily unique, you may get multiple results. You can return an IEnumerable<KeyValuePair<int, string>>:
var betaKeys = greek.Where(x => x.Value == "beta");
To transform this into an IEnumerable<int> type, just use .Select():
var betaKeys = greek.Where(x => x.Value == "beta").Select(x => x.Key);
As a twist of the accepted answer (https://stackoverflow.com/a/255638/986160) assuming that the keys will be associated with signle values in the dictionary. Similar to (https://stackoverflow.com/a/255630/986160) but a bit more elegant. The novelty is in that the consuming class can be used as an enumeration alternative (but for strings too) and that the dictionary implements IEnumerable.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
namespace MyApp.Dictionaries
{
class BiDictionary<TFirst, TSecond> : IEnumerable
{
IDictionary<TFirst, TSecond> firstToSecond = new Dictionary<TFirst, TSecond>();
IDictionary<TSecond, TFirst> secondToFirst = new Dictionary<TSecond, TFirst>();
public void Add(TFirst first, TSecond second)
{
firstToSecond.Add(first, second);
secondToFirst.Add(second, first);
}
public TSecond this[TFirst first]
{
get { return GetByFirst(first); }
}
public TFirst this[TSecond second]
{
get { return GetBySecond(second); }
}
public TSecond GetByFirst(TFirst first)
{
return firstToSecond[first];
}
public TFirst GetBySecond(TSecond second)
{
return secondToFirst[second];
}
public IEnumerator GetEnumerator()
{
return GetFirstEnumerator();
}
public IEnumerator GetFirstEnumerator()
{
return firstToSecond.GetEnumerator();
}
public IEnumerator GetSecondEnumerator()
{
return secondToFirst.GetEnumerator();
}
}
}
And as a consuming class you could have
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MyApp.Dictionaries
{
class Greek
{
public static readonly string Alpha = "Alpha";
public static readonly string Beta = "Beta";
public static readonly string Gamma = "Gamma";
public static readonly string Delta = "Delta";
private static readonly BiDictionary<int, string> Dictionary = new BiDictionary<int, string>();
static Greek() {
Dictionary.Add(1, Alpha);
Dictionary.Add(2, Beta);
Dictionary.Add(3, Gamma);
Dictionary.Add(4, Delta);
}
public static string getById(int id){
return Dictionary.GetByFirst(id);
}
public static int getByValue(string value)
{
return Dictionary.GetBySecond(value);
}
}
}
Then layman's solution
A function similar to the one below could be written to make such a dictionary:
public Dictionary<TValue, TKey> Invert(Dictionary<TKey, TValue> dict) {
Dictionary<TValue, TKey> ret = new Dictionary<TValue, TKey>();
foreach (var kvp in dict) {ret[kvp.value] = kvp.key;} return ret; }
Probably, you need a bidirectional dictionary. In my mind, BidirectionalDictionary is the best realization of a bidirectional dictionary. It just provides access to an inverse O(1) dictionary.
var biDictionary = new BidirectionalDictionary<T1,T2> { ... };
This realization, for example, has no indexations problems when TKey equals TValue:
var capital = countryCapitalDictionary["Italy"]; // "Rome"
var country = countryCapitalDictionary.Inverse["Rome"]; // "Italy"
Changing the dictionary causes the reverse dictionary to be changed safely (as well as vice versa):
countryCapitalDictionary.Clear(); // equals countryCapitalDictionary.Inverse.Clear();
var containsCapital = countryCapitalDictionary.ContainsKey("Italy"); // false
var containsCountry = countryCapitalDictionary.Inverse.ContainsKey("Rome"); // false
Also, this library supports a read-only bidirectional dictionary:
var readOnlyBiDictionary = new ReadOnlyBidurectionalDictionary<T1, T2>(biDictionary);
You can use it via the package.

Categories

Resources