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

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

Related

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.

A neater way to use Function delegate over Action delegate

I wish to iterate over a collection in a thread safe manner. I find it's convieniant to have one method called
/// <summary>
/// This visits all items and performs an action on them in a thread manner
/// </summary>
/// <param name="visitAction">The action to perform on the item</param>
public void VisitAllItems(Action<Item> visitAction) {
lock (listLock) {
foreach (Item item in this.ItemList) {
visitAction.Invoke(item);
}
}
}
An example of it's use could be
/// <summary>
/// Saves each item in the list to the database
/// </summary>
protected static void SaveListToDatabase() {
this.VisitAllItems(item => {
bool itemSavedSuccessfully = item.SaveToDB();
if(!itemSavedSuccessfully) {
//log error message
}
});
}
Another example of it's use would be
/// <summary>
/// Get the number of special items in the list.
/// </summary>
protected int GetNumberOfUnsynchronisedItems() {
int numberOfSpecialItems = 0;
this.VisitAllItems((item) => {
numberOfSpecialItems += item.IsItemSpecial() ? 1 : 0;
});
return numberOfSpecialItems;
}
But I'm sure there is a better way of writing the VisitAllItems method to use a Func<> rather than an Action<> delegate to return this value. I've tried several things but end up with compilation errors.
Can anyone see a neater way to implement this method?
Thanks,
Alasdair
It all depends on the usage, so it's hard to tell what exactly would be useful for you. But one way would be to return one result for each item in the collection, and then use LINQ to combine the results.
public IEnumerable<TResult> VisitAllItems<TResult>(
Func<Item, Result> visitfunction)
{
var result = new List<TResult>();
lock (listLock)
{
foreach (Item item in ItemList)
result.Add(visitfunction(item));
}
return result;
}
Normally, I would use yield return instead of manually creating the List<T>, but I think it's better this way here, because of the lock.
The usage would be like this:
protected int GetNumberOfUnsynchronisedItems()
{
return VisitAllItems(item => item.IsItemSpecial())
.Count(isSpecial => isSpecial);
}
You could follow the Aggregate model, but it wouldn't be terribly pleasant to use:
public TAccumulate VisitAllItems<TAccumulate>(TAccumulate seed,
Func<Item, TAccumulate, TAccumulate> visitor) {
TAccumulate current = seed;
lock (listLock) {
foreach (Item item in this.ItemList) {
current = visitor(current, item);
}
}
return current;
}
...
protected int GetNumberOfUnsynchronisedItems() {
return VisitAllItems(0,
(count, item) => count + item.IsItemSpecial() ? 1 : 0);
}
How many different aggregation functions do you actually need? For example, if most of the time you're just counting, you might want:
public void VisitAndCount<TAccumulate>(Func<Item, bool> visitor) {
int count = 0;
lock (listLock) {
foreach (Item item in this.ItemList) {
if (visitor(item)) {
count++;
}
}
}
return count;
}
then:
protected int GetNumberOfUnsynchronisedItems() {
return VisitAndCount(item => item.IsItemSpecial());
}

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

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

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