2

Is there a collection in C# working like dictionary with multiple keys allowing bidirectional access? For example:

Collection<int, string> a = new Collection<int, string>();
a.Add(1, "a");
a.Add(1, "b");
a.Add(2, "a");
a.Get(1);//returns ["a", "b"]
a.InverseGet("a"); //returns [1, 2]
Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
quarandoo
  • 369
  • 4
  • 9
  • You have LINQ for that. – SᴇM Sep 04 '18 at 08:25
  • A simple `.Where()` would do the job if you don't care about performance that much – Panagiotis Kanavos Sep 04 '18 at 08:26
  • Another option is the DataTable. It doesn't care about directions and can use indexes to improve performance – Panagiotis Kanavos Sep 04 '18 at 08:28
  • You should use a list of key value pairs. `var collection = new List>();` This you can query with LINQ easily. – ChristianMurschall Sep 04 '18 at 08:28
  • @Linuxrocks there's no need to use a KVP, an array of tuples will work just as well – Panagiotis Kanavos Sep 04 '18 at 08:29
  • @quarando what do you want to do? Why not just use an array of tuples? Are there any performance requirements? – Panagiotis Kanavos Sep 04 '18 at 08:31
  • try to use [`Lookup`](https://learn.microsoft.com/en-us/dotnet/api/system.linq.lookup-2?redirectedfrom=MSDN&view=netframework-4.7.2) and it's `Where` method – Sanjay Nishad Sep 04 '18 at 08:34
  • @PanagiotisKanavos: He wrote about keys and then I naturally think of KVP. Tuples are for equaly values. But I think you're right. Tuples are in his case probably better since they implement `IComparable`and `IStructuralEquatable` making it easy to compare two tuples. – ChristianMurschall Sep 04 '18 at 08:36
  • @SanjayNishad: He has not have unique keys. – ChristianMurschall Sep 04 '18 at 08:37
  • @linuxrocks ic, I found [this link](https://www.c-sharpcorner.com/UploadFile/b942f9/a-dictionary-class-which-permits-duplicate-keys/) may help him – Sanjay Nishad Sep 04 '18 at 08:51
  • For those reviewing my proposed edit, I later found the question it **should** be a duplicate of (with answer from @JonSkeet similar to Adam G's answer below), but don't know how to propose an edit to the duplicate target: https://stackoverflow.com/questions/255341/getting-multiple-keys-of-specified-value-of-a-generic-dictionary – Steve Feb 09 '21 at 09:48

2 Answers2

2

Here is a solution that gives you the performance of a dictionary lookup in both directions. I assume that you want to ignore duplicate tuples and the default comparer is sufficient. Implementing the other operations like Remove is left as an exercise for the reader

public class TwoWayCollection<A, B>
{
    private Dictionary<A, HashSet<B>> byADictionary = new Dictionary<A, HashSet<B>>();
    private Dictionary<B, HashSet<A>> byBDictionary = new Dictionary<B, HashSet<A>>();

    public IEnumerable<B> Get(A a)
    {
        return byADictionary[a];
    }

    public IEnumerable<A> InverseGet(B b)
    {
        return byBDictionary[b];
    }

    public void Add(A a, B b)
    {
        if (!byADictionary.ContainsKey(a))
        {
            byADictionary[a] = new HashSet<B>();
        }
        byADictionary[a].Add(b);
        if (!byBDictionary.ContainsKey(b))
        {
            byBDictionary[b] = new HashSet<A>();
        }
        byBDictionary[b].Add(a);
    }
}

Then to use it is literally your proposed code. Both Get and InverseGet approach O(1) as per dictionary

TwoWayCollection<int, string> a = new TwoWayCollection<int, string>();
a.Add(1, "a");
a.Add(1, "b");
a.Add(2, "a");
a.Get(1); //returns ["a", "b"]
a.InverseGet("a"); //returns [1, 2]
Adam G
  • 1,283
  • 1
  • 6
  • 15
1

Using Tuple List<Tuple<int,string>> to allow a collection with the same key and LINQ to make a query Where to your collection

For example,

List<Tuple<int,string>> list = new List<Tuple<int,string>>();
list.Add(Tuple.Create(23, "Foo"));
list.Add(Tuple.Create(23, "Bar"));
list.Add(Tuple.Create(25, "Bar"));

var keys = list.Where(x=> x.Item1 == 23).Select(x=> x.Item2); // FOO, BAR
var values = list.Where(x=> x.Item2 == "Bar").Select(x=> x.Item1); ; // 23, 25
Antoine V
  • 6,998
  • 2
  • 11
  • 34