How to implement a multi-index dictionary?
Asked Answered
L

12

34

Basically I want something like Dictionary<Tkey1, TKey2, TValue>, but not (as I've seen here in other question) with the keys in AND, but in OR. To better explain: I want to be able to find an element in the dictionary providing just one of the keys, not both.

I also think we should consider thread-safety and the ability to easily scale to a Dictionary<Tkey1, TKey2, TKeyN, TValue> solution...

Logogram answered 1/10, 2009 at 14:56 Comment(10)
Perhaps if you could show us the code you have written so far? People don't generally like to just write your code for you...Emperor
I haven't wrote any line, but I don't want someone write it for me. I just want to discuss the implementation the develop the ideas this will bring.Logogram
Closing as "Not a real question" ? Come on.Cuman
@Henk, seems you didn't understand the question...Logogram
Mitch, in this case, he's not asking for code, just input into the right algorithm. I don't believe closing this question would be appropriate.Backstitch
@MatteoSp, I wasn't clear. There are 2 close-votes on this question, you can't see them. I was questioning those votes, not your post.Cuman
So, did you find a correct solution? How did you implement a structure that @tster described below?Takamatsu
To me, this is a valid question and was closed. This is why I don't feel like posting questions here any more!Hypostasize
possible duplicate of Multi-key dictionaries (of another kind) in C#?Presumption
Multi-Key Dictionary in C#Virulence
U
22

I would implement a data structure with these two dictionaries

Dictionary<TKey1, KeyValuePair<TKey2, TValue>> dict1;
Dictionary<TKey2, KeyValuePair<TKey1, TValue>> dict2;

That way if you are given 1 key you have both the value and the other key as well for easy deletes and updates.

Undistinguished answered 1/10, 2009 at 15:11 Comment(5)
This is a reasonable solution.Backstitch
Does this guarantee that the insider key is unique? Doesn't seem to me it does...Bahaism
assume that you are going to search where val <= key1 and val >= key2 so how would this solution work ?Pennypennyaliner
@MonsterMMORPG, dictionaries are not for doing inequality searches like that. It sounds to me like you would be best off to use a database, but if you want help you should post a separate question.Undistinguished
It is a confusion with c++ multi_index which allows most of sensible indexes (sorted, unsorted, unique, sequence)Baltimore
S
16

So you want a multi-index dictionary, supports lookups based on any key, and supports extensions to multiple keys?

Maybe you're thinking of the wrong data structure, try a KD-Tree instead. An immutable KD-tree would satisfy the thread-safety requirement.

KD-trees have some major advantages over the naive Dictionary{Key1, Dictionary{Key2, Value}} approach, namely that you can search for all fields based on Key2 without knowing Key1. Additionally, KD-trees allow you to search for keys which are near some other key. For example, dating sites classify people into dozens of groups (smoker/non-smoker, gender, religion, lifestyle, age, height), then return nearest neighbors based on your query.

Here's a C# and Java implementation:

http://home.wlu.edu/~levys/software/kd/ (broken link, archived at https://web.archive.org/web/20190609084214/http://home.wlu.edu/~levys/software/kd/)

Sensationalism answered 1/10, 2009 at 15:9 Comment(3)
That's certainly interesting, but I thought KD-Trees were mostly useful in graphics algorithms.Backstitch
KD-trees are certainly useful for partitioning 2D points into planes (makes it easy to test for nearest neighbors), but you the dimensions of your tree can be as abstract as you want. The canonical example is a dating site which matches people up on multiple "levels of compatibility": smoker/non-smoker, preferred gender, preferred religion, vegetarian/non-vegetarian, preferred age range, preferred height, political orientation, etc. Naive algorithms in SQL can find matches, but the natural way to detect nearest neighbors (i.e. compatible spouses) uses a kd-tree.Sensationalism
Very Intersting. Note, though, that the c# version is only implemented for double keys.Clarethaclaretta
E
5

I'm going to go out on a limb here and appear stupid, but you could just roll your own Dictionary based on two dictionaries. It wouldn't be too terribly difficult to write (even with locking mechanisms to ensure thread safety). I mean, there's plenty of examples out there where you can use an index or a key to access a collection. (Such as Session)

Conversely, if your multiple indexes are of the same type, you could just add the same item multiple times.

Dictionary will support something with a GUID index, as well as a simple name index "Joe" - you have to remember to add the item twice.

Eliseo answered 1/10, 2009 at 15:4 Comment(3)
I've rolled my own before. .Net does not have bimaps so rolling my own was the easiest way.Businessman
You need to considere deletes and countLogogram
Count is only a ?challenge? for the second implementation where you're forced to halve it to obtain the real number. ... Deleting would be inefficient in either scenario, as you would be forced to traverse the Values collection to find the right entry to remove.Eliseo
D
2

You could just use a regular dictionary for this and insert the value twice, once for each key. To delete you remove both keys.

Upsides:

  • Can search for both keys with one lookup.
  • No new code needed.
  • Scales to any number of keys.

Downsides:

  • Lose type safety if keys are of different types.
  • Can't iterate over (key1, key2, value) tuples.
  • Values appear twice so size() is doubled.
Disturbed answered 1/10, 2009 at 15:8 Comment(3)
You too, need to consider deletes and countLogogram
If both keys are the same type, this method is not terrible. For different types, it's going to have issues.Backstitch
The biggest issue this approach might pose is that it doesn't enforce the caller adding a second key (for the same value). While this may or may not be advantageous, it makes it a totally different beast.Presumption
G
2

Maybe an option:

Do as John Kugelman suggests, just add an item twice in the same dictionary. Then you keep a second dictionary that maps values to sets of keys.

When you add a key,value pair, you just add it as normal to the first dictionary and then retrieve the key set belonging to that value from the second dictionary and add the key.

Dictionary<TKey, TValue> dict1;
Dictionary<TValue, ICollection<TKey>> dict2;

Removing a value is done by retrieving the key set from dict2 and removing them one by one from dict1.

The number of keys is dict1.Count and the number of values is dict2.Count

Globose answered 18/11, 2009 at 10:46 Comment(0)
S
1

Did you consider holding two dictionaries, one for each key? Adding an item would add it to both.

Removal means removing from both dictionaries too, and requires both keys. To remove with just one key, the item would have to store both keys. If it doesn't already hold both keys, you could wrap it in a container object that holds both keys and the item.

Sayre answered 1/10, 2009 at 15:5 Comment(1)
You wouldn't need to remove with two keys. Once you have the value, you could look thru the second dictionary for that value and remove the corresponding entry.Eliseo
E
0

Two dictionaries, but don't duplicate the items in each.

You'll have a value dictionary, and a key dictionary.

When you call your add method, you'll generate a GUID and add it and the key to the keys dictionary.

Then, use the GUID as a key to the values dictionary.

If you want to add two keys, you'll add another item to the keys dictionary with the same GUID.

Of course this means every lookup requires two checks for the data, but never more, even if you have 50 keys for the same value.

Lookup the guid based on the key from the keys table, then lookup the data based on that guid in the values table.

Edwinaedwine answered 1/10, 2009 at 15:12 Comment(6)
And a few simple linq queries will allow for easy retrieval of all keys for a value, all values, all keys, etc.Edwinaedwine
I don't see any advantage to not keeping the value in both dictionaries. It's most likely going to either be a small value type or a reference to a shared instance.Backstitch
I prefer tster's method, as it guarantees lookups in one try, not two, yet allows easy deletion.Backstitch
Well if your objects are small and you don't want to scale beyond two keys, then yes. Personally, I find duplicated data and the inability to scale a drawback.Edwinaedwine
Chad, the data isn't actually duplicated. As for not being able to scale, nothing stops you from chaining them in a loop, allowing an arbitrary number of key types. <A,<B,O>>, <B,<C,O>>, <C,<D,O>>, <D,<A,O>>Backstitch
This is good thinking if you would have a lot many keys, but it only saves typing; doesnt help performance. Think about deletion nightmare. And for most common scenarios, tster's accepted answer is better.Presumption
F
0

I have written such a dictionary and posted it on my blog. It will give you a nice API like this:

DoubleKeyDictionary<int, string, string> books = new DoubleKeyDictionary<string, string, string>();
bookListEx.Add(1, “21/12/2009″, “Lord of the Rings - Fellowship of the Ring”); 

You can also do "Equals" on two dictionaries and for-each over it.

Please note that there are at least one bug in the code (as discovered in the comments) and no unit tests etc. When (yeah!) I get some time I'll update the code with unit tests...

Finnish answered 18/12, 2009 at 14:18 Comment(0)
G
0

What about a Multi Index Container inspired by boost??

Take a look at CodeProject.

Gluttony answered 5/3, 2010 at 13:28 Comment(0)
Q
0

Create simple class to store Tkey1, TKey2, TValue, make List of them and use LINQ to query such structure would be an option.

Quirita answered 9/8, 2010 at 16:30 Comment(0)
V
0

Check out this article on CodeProject: http://www.codeproject.com/KB/recipes/multikey-dictionary.aspx

This is clearly the right way to do a multi-key dictionary ;)

Virulence answered 24/5, 2011 at 6:49 Comment(0)
C
0

If your indices are of different types you might want to consider a combination of a SortedList and a Dictionary:

    public SortedList<MyIndexType, MyDataType> PrimaryIndex = new SortedList<MyIndexType, MyDataType>();
    public Dictionary<string, MyDataType> OtherIndex = new Dictionary<string, MyDataType>();

MyIndexType can then implement IComparable:

    class MyIndexType: IComparable
    {
        public DateTime EventTime { get; set; }
        public string Code { get; set; }
        public int CompareTo(object obj)
        {
            MyIndexType otherObject = (MyIndexType)obj;
            if (otherObject.EventTime > this.EventTime)
            { return -1; } //This object preceeds the otherObject
            else if (otherObject.EventTime < this.EventTime)
            { return 1; } //This object is after the otherObject
            else if (otherObject.Code.CompareTo(this.Code) > 0)
            { return -1; } //otherObjecthas code later than me
            else
            { return 1; } //if the Code is the same or greater, put the other one after me
        }
    }
Christology answered 19/8, 2021 at 4:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.