Duplicate Key Alternative for Generic Dictionary

S

sloan

I know a Dictionary < TKey, TValue > does not allow duplicate keys.

System.Collections.Generic.Dictionary<Guid, Guid> test = new
System.Collections.Generic.Dictionary<Guid, Guid>();
//duplicate key
test.Add(new Guid("00000000-0000-0000-0000-000000000001"), Guid.NewGuid());
test.Add(new Guid("00000000-0000-0000-0000-000000000001"), Guid.NewGuid());


Does anyone know of an alternative generic/collection that would allow the
operation above?

I'm thinking there is C5 option, but figured I'd ask since somebody would
probably know in about 2ms.



Thanks.
 
J

Jon Skeet [C# MVP]

sloan said:
I know a Dictionary < TKey, TValue > does not allow duplicate keys.

System.Collections.Generic.Dictionary<Guid, Guid> test = new
System.Collections.Generic.Dictionary<Guid, Guid>();
//duplicate key
test.Add(new Guid("00000000-0000-0000-0000-000000000001"), Guid.NewGuid());
test.Add(new Guid("00000000-0000-0000-0000-000000000001"), Guid.NewGuid());


Does anyone know of an alternative generic/collection that would allow the
operation above?

I'm thinking there is C5 option, but figured I'd ask since somebody would
probably know in about 2ms.

What would you want it to do with the new value? If you want it to
overwrite the old one, just use the indexer instead of the Add method.
If you want each key to effectively map to a list of values, you have
to implement that yourself, unfortunately (still using Dictionary).
 
P

Peter Duniho

I know a Dictionary < TKey, TValue > does not allow duplicate keys.

System.Collections.Generic.Dictionary<Guid, Guid> test = new
System.Collections.Generic.Dictionary<Guid, Guid>();
//duplicate key
test.Add(new Guid("00000000-0000-0000-0000-000000000001"),
Guid.NewGuid());
test.Add(new Guid("00000000-0000-0000-0000-000000000001"),
Guid.NewGuid());

Does anyone know of an alternative generic/collection that would allow
the
operation above?

I assume the point here is to have a collection that is efficient at
retrieving elements. Obviously you could just use a plain List<> or
ArrayList to store a two-Guid struct. Or even just have two lists
maintained synchronously.

I don't know of a built-in fast collection that allows duplicate keys.
But you certainly could use a Dictionary<Guid, List<Guid>> to maintain
multiple Guids values for the same key Guid. Just check to see if the key
is already in the dictionary; if it is, just add a new value to the list,
if it's not create a new list, add the value and set the list as the value
for the dictionary.

Pete
 
S

sloan

I guess pretend I have 3 departments, and 3 employees in each one.

That is the reason. I'm either going to have to wrap my own custom object
collection, or try and piggy back.


Guid dept1 = new Guid("00000000-0000-0000-0000-000000000001");
Guid dept2 = new Guid("00000000-0000-0000-0000-000000000002");
Guid dept3 = new Guid("00000000-0000-0000-0000-000000000003");

test.Add(dept1 , Guid.NewGuid());

test.Add(dept1 , Guid.NewGuid());
test.Add(dept1 , Guid.NewGuid());

test.Add(dept2 , Guid.NewGuid());
test.Add(dept2 , Guid.NewGuid());
test.Add(dept2 , Guid.NewGuid());

test.Add(dept3 , Guid.NewGuid());
test.Add(dept3 , Guid.NewGuid());
test.Add(dept3 , Guid.NewGuid());


I understand that if I were using this collection as-is and correctly for
its intended purpose...then I'd check and override the TValue for the
existing TKey.

But above kinda outlines my need.

I'd like to use the dept1 guid, to find the 3 employees under that dept1.



I'll check the C5 library...and also experiment with the
Dictionary<Guid, List<Guid>> provided idea.
 
M

Marc Gravell

Well, there's the (immutable) Lookup<TKey,TValue> class in 3.5, but I
can't find it in MSDN2 to find out what the "O" is for the indexer.

Marc

Guid dept1 = new
Guid("00000000-0000-0000-0000-000000000001");
Guid dept2 = new
Guid("00000000-0000-0000-0000-000000000002");
Guid dept3 = new
Guid("00000000-0000-0000-0000-000000000003");

var lookup = new[] {
new {Key=dept1, Value = Guid.NewGuid()},
new {Key=dept1, Value = Guid.NewGuid()},
new {Key=dept1, Value = Guid.NewGuid()},

new {Key=dept2, Value = Guid.NewGuid()},
new {Key=dept2, Value = Guid.NewGuid()},
new {Key=dept2, Value = Guid.NewGuid()},

new {Key=dept3, Value = Guid.NewGuid()},
new {Key=dept3, Value = Guid.NewGuid()},
new {Key=dept3, Value = Guid.NewGuid()}
}.ToLookup(x => x.Key);

foreach (var record in lookup[dept1])
{
Console.WriteLine("{0}: {1}", record.Key,
record.Value);
}
 
S

Samuel R. Neff

To me it sounds like you're trying to avoid creating a Department
object even though that is exactly what you need. This code would be
much easier to read and understand and manage and debug and on and on
and on..

var d1 = new Department();
var d2 = new Department();
var d3 = new Department();

var departments = new Dictionary<Guid, Department>();

departments.Add(d1.Guid, d1);
departments.Add(d2.Guid, d2);
departments.Add(d3.Guid, d3);

d1.Users.Add(new User());
d1.Users.Add(new User());
d1.Users.Add(new User());

d2.Users.Add(new User());
d2.Users.Add(new User());
d2.Users.Add(new User());

d3.Users.Add(new User());
d3.Users.Add(new User());
d3.Users.Add(new User());

3 departments in a dictionary, each with 3 users (employees). Not
fully working code (need class definitions), but hopefully this is
enough to explain.

HTH,

Sam
 
K

Kelly Herald

How about the PowerCollections library at
http://www.wintellect.com/PowerCollections.aspx. It is open source and you
can download the source code.
What you probably want to use is the MultiDictionary<TKey,TValue> class.

'Unlike an Dictionary, each key can have multiple values associated with
it.' - quoted from the class's documentation.
 
S

sloan

Thanks Kelly!

I know wintellect and guys like Jeff Prosise totally rock.

I'm checking it out.

I rolled my own about 1 hour ago, but will look at theirs as well.

I'll post my code after this one.
 
S

sloan

I rolled my own, but am now going over to check out the wintellect version.


Here is my code. (before I've looked at wintellect btw).
I have 2 tests as well.

You'll have to comment out the textBox1 lines to get it to run fyi.




private void TestItIntString()

{

try

{

DuplicateKeyDictionary<int, string> test = new DuplicateKeyDictionary<int,
string>();

int jobtitle1 = 123;

int jobtitle2 = 234;

int jobtitle3 = 345;

test.Add(jobtitle1, "Emp1");

test.Add(jobtitle1, "Emp2");

test.Add(jobtitle1, "Emp3");

test.Add(jobtitle2, "Emp4");

test.Add(jobtitle2, "Emp5");

test.Add(jobtitle2, "Emp6");



test.Add(jobtitle3, "Emp7");

test.Add(jobtitle3, "Emp8");

test.Add(jobtitle3, "Emp9");





List<string> items = test.FindAll(jobtitle1);

textBox1.Text += System.Environment.NewLine + "---------------------" +
System.Environment.NewLine + System.Environment.NewLine;

foreach (string g in items)

{

textBox1.Text += g + System.Environment.NewLine;

}



bool exists1 = test.Exists(jobtitle3, "Emp7");

textBox1.Text += exists1.ToString() + System.Environment.NewLine;

bool exists2 = test.Exists(jobtitle1, "Emp-------");

textBox1.Text += exists2.ToString() + System.Environment.NewLine;



//expected failure here

test.Add(123, "Emp1");



}

catch (Exception ex)

{

textBox1.Text += System.Environment.NewLine + System.Environment.NewLine +
ex.Message;

}

}

private void TestItGuidGuid()

{

try

{

DuplicateKeyDictionary<Guid, Guid> test = new DuplicateKeyDictionary<Guid,
Guid>();

Guid jobtitle1 = new Guid("00000000-0000-0000-0000-000000000001");

Guid jobtitle2 = new Guid("00000000-0000-0000-0000-000000000002");

Guid jobtitle3 = new Guid("00000000-0000-0000-0000-000000000003");

test.Add(jobtitle1, new Guid("10000000-0000-0000-0000-000000000001"));

test.Add(jobtitle1, new Guid("20000000-0000-0000-0000-000000000001"));

test.Add(jobtitle1, new Guid("30000000-0000-0000-0000-000000000001"));

test.Add(jobtitle2, new Guid("40000000-0000-0000-0000-000000000002"));

test.Add(jobtitle2, new Guid("50000000-0000-0000-0000-000000000002"));

test.Add(jobtitle2, new Guid("60000000-0000-0000-0000-000000000002"));

test.Add(jobtitle3, new Guid("70000000-0000-0000-0000-000000000003"));

test.Add(jobtitle3, new Guid("80000000-0000-0000-0000-000000000003"));

test.Add(jobtitle3, new Guid("90000000-0000-0000-0000-000000000003"));

List<Guid> items = test.FindAll(jobtitle1);

textBox1.Text = "";

foreach (Guid g in items)

{

textBox1.Text += g.ToString("N") + System.Environment.NewLine;

}



bool exists1 = test.Exists(jobtitle3, new
Guid("90000000-0000-0000-0000-000000000003"));

textBox1.Text += exists1.ToString() + System.Environment.NewLine;

bool exists2 = test.Exists(jobtitle1, System.Guid.NewGuid());

textBox1.Text += exists2.ToString() + System.Environment.NewLine;



//expected failure here

test.Add(new Guid("00000000-0000-0000-0000-000000000001"), new
Guid("10000000-0000-0000-0000-000000000001"));



}

catch (Exception ex)

{

textBox1.Text += System.Environment.NewLine + System.Environment.NewLine +
ex.Message;

}

}

internal class InternalHolder<K, T>

{

private K _key;

private T _item;

public K Key

{

get { return _key; }

}

public T Item

{

get { return _item; }

}

public InternalHolder(K key, T item)

{

_key = key;

_item = item;

}

}

public class DuplicateKeyDictionary<K, T>

{

List<InternalHolder<K, T>> _internalHolders = new List<InternalHolder<K,
T>>();

public DuplicateKeyDictionary()

{ }

public List<T> FindAll(K key)

{

List<T> returnItems = new List<T>();

foreach (InternalHolder<K, T> ih in _internalHolders)

{

if (ih.Key.Equals(key))

{

returnItems.Add(ih.Item);

}

}

return returnItems;

}

public bool Exists(K key, T item)

{

foreach (InternalHolder<K, T> ih in _internalHolders)

{

if (ih.Key.Equals(key))

{

if (ih.Item.Equals(item))

{

return true;

}

}

}

return false;

}

public void Add(K key, T item)

{

if (this.Exists(key, item))

{

throw new System.ArgumentException(string.Format("This key/item pair already
exists. ({0},{1})", key.ToString(), item.ToString()));

}

_internalHolders.Add(new InternalHolder<K, T>(key, item));

}

}
 
S

sloan

Obviously their's is like 1000X better than the little scrapper I wrote.

That was exactly what I was looking for Kelly, thanks again.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top