views:

1811

answers:

12

I've been using a Hashtable, but by nature, hashtables are not ordered, and I need to keep everything in order as I add them (because I want to pull them out in the same order). Forexample if I do:

pages["date"] = new FreeDateControl("Date:", false, true, false);
pages["plaintiff"] = new FreeTextboxControl("Primary Plaintiff:", true, true, false);
pages["loaned"] = new FreeTextboxControl("Amount Loaned:", true, true, false);
pages["witness"] = new FreeTextboxControl("EKFG Witness:", true, true, false);

And when I do a foreach I want to be able to get it in the order of:

pages["date"]  
pages["plaintiff"]  
pages["loaned"]  
pages["witness"] 

How can I do this?

A: 

Use a SortedList or SortedDictionary

Ray
Those sort in key order, not insertion order.
Jon Skeet
@Jon Skeet: You can implement a custom `IComparer` that is aware of the insertion order. As you've already suggested having a separate `List` to maintain insertion order, this is not extra work.
Jason
@Jason: If you've already got the separate list which you'd use for the insertion order, why would you bother using SortedList/SortedDictionary and implementing the comparer? Not to mention the fact that your answer doesn't even mention any of this...
Jon Skeet
oops - I didn't read the question carefully enough - @Jon, your answer (keeping 2 lists) is a good one
Ray
@Jon Skeet: First, I did not provide an answer to this question. Second, I was merely pointing out that it is possible with a `SortedDictionary`.
Jason
@Jason: Yes, sorry, I missed that it wasn't your answer. I still think it's pretty pointless to use a SortedList/SortedDictionary when you've already got the items ordered though... yes, it's *possible* - but it's a very roundabout way of doing things.
Jon Skeet
@Jon Skeet: I agree with you. Again, was merely responding to your comment "Those sort in key order, not insertion order" to point out that they can be made to sort in insertion order. That's all. Very sorry for the confusion.
Jason
+10  A: 

EDIT: LBushkin is right - OrderedDictionary looks like it does the trick, albeit in a non-generic way. It's funny how many specialized collections there are which don't have generic equivalents :( (It would make sense for Malfist to change the accepted answer to LBushkin's.)

(I thought that...) .NET doesn't have anything built-in to do this.

Basically you'll need to keep a List<string> as well as a Dictionary<string,FreeTextboxControl>. When you add to the dictionary, add the key to the list. Then you can iterate through the list and find the keys in insertion order. You'll need to be careful when you remove or replace items though.

Jon Skeet
A Dictionary keeps it in order, plus its a generic! w00t. Item's won't be removed, or replaced.
Malfist
@Malfist: That's not true. A Dictionary is a hash table and won't keep them in order at all.
mquander
As Ray pointed out, the SortedDictionary is sorted on the key:Represents a collection of key/value pairs that are sorted on the key.http://msdn.microsoft.com/en-us/library/f7fta44c.aspx
expedient
@Malfist: `Dictionary<TKey,TValue>` may look like it keeps the order, but it's not guaranteed to. In particular, it doesn't if you remove entries and then add more. If you *just* add entries, it tends to keep the insertion order IIRC - but it's certainly not guaranteed.
Jon Skeet
Oops, sorry. Foot, mouth, kismet.
expedient
would in not be more straightforward and efficient to use a generic SortedList, with an incremented key? http://stackoverflow.com/questions/1915347/c-associative-array/1915403#1915403
Patrick Karcher
http://stackoverflow.com/questions/154307/why-are-entries-in-addition-order-in-a-net-dictionary/976871#976871 For now at least, if you never remove items you will get them back in addition order. You shouldn't depend on this behavior in your code since it may change without notice in future versions of the .net library.
Dolphin
@Patrick: I don't believe your answer maintains the property of being an associative array - i.e. one which you can access by the original key.
Jon Skeet
It might be faster to use a `LinkedList<T>` rather than a `List<T>`, if a lot of insertions/removals are being done, especially if you store the linked list node in the dictionary rather than the data value. Then insertions/removals are O(1)
thecoop
@thecoop: Yes, that's a good idea. It becomes a little bit more complicated (`LinkedList<KeyValuePair<string,FreeTextboxControl>>` and `Dictionary<LinkedListNode<KeyValuePair<string,FreeTextboxControl>>>`!) but it would indeed be more efficient.
Jon Skeet
I believe that .NET offers the OrderedDictionary non-generic class just for this reason. Check out: http://msdn.microsoft.com/en-us/library/system.collections.specialized.ordereddictionary.aspx
LBushkin
@Jon: The documentation is misleading - but if you write a sample you will see that it does indeed behave the way you would intuitively expect - and D will in fact be the last item, not the first.
LBushkin
@LBushkin: Yup, I tried it and removed my comment - looks like we overlapped :)
Jon Skeet
+7  A: 

Why not use a List<T> of KeyValuePair<Tkey, Tvalue>?

Andrew Song
I like this idea
David Brunelle
That's a very good alternative for the situation where there aren't many values, yes.
Jon Skeet
A: 

look at sorted list http://msdn.microsoft.com/en-us/library/system.collections.sortedlist.aspx

ram
See the other sorted list suggestion.
Andrew Song
thanks for correcting me. Thats why I like SO :)
ram
A: 

There's no perfect solution before .NET 4.0. In < 3.5 You can:

Use a generic SortedList with integer key-type, and value type of the most-derived common type of your items. Define an integer value (i, let's say) and as you add each item to the SortedList, make the key i++, incrementing it's value as you go. Later, iterate over the GetValueList property of the sorted list. This IList property will yield your objects in the order you put them in, because they will be sorted by the key you used.

This is not lightening-fast, but pretty good, and generic. If you want to also access by key, you need to do something else, but I don't see that in your requirements. If you don't new to retrieve by key, and you add items in key order so the collection doesn't actually have to do its sorting, this is it.

In .NET 4.0 you'll have the generic SortedSet Of T, which will be absolutely perfect for you. No tradeoffs.

Patrick Karcher
How is that kind of SortedList better than just using a List of key/value pairs to start with?
Jon Skeet
(a.) it's very straightforward, (b.) A sorted list is optimal as long as the order remains as the items were inserted. It loses it's efficiency advantage if the key necessitates reordering, but in this scenerio that's not happening.
Patrick Karcher
How is it straightforward or optimal? You still need to be able to fetch by the original key, which is going to be tricky if you've decided to use the insertion order as the key to the SortedList/SortedDictionary. Maybe if you could provide a full example which allows efficient access by original key ("date", "plaintiff" etc) *and* access in order, that would clarify things...
Jon Skeet
Quoting from the question, he wants to:(a) "keep everything in order as he adds them"(b) "because I want to pull them out in the same order"(c) "when I do a foreach"I took these as the requirements. I was not attempting to allow for access by the key. If he wants to also access by key, that would certainly change things.
Patrick Karcher
+3  A: 

I think PowerCollections has classes that fit your need (look for OrderedSet or OrderedDictionary.

Goran
A: 

use sorted list i think it will solve your problem becuase SortedList object internally maintains two arrays to store the elements of the list; that is, one array for the keys and another array for the associated values. Each element is a key/value pair that can be accessed as a DictionaryEntry object

SortedList sl = new SortedList();

foreach(DictionaryEntry x in sl) {}

peacmaker
A: 

Use the KeyedCollection

Its underlying base is a List but provides a dictionary lookup based on key. In this case your key is the strings. So as long as you aren't adding the same key twice you are fine.

http://msdn.microsoft.com/en-us/library/ms132438.aspx

HaxElit
A: 

Could get a way with having an enum say

public enum PageValues : uint {
    Date = 0,
    Plantiff,
    ...
}

and then access your collection as a

SortedDictionary<PageValue, FreeDateControl>

and access it as

values[PageValues.Date] = new FreeDateControl("Date:", false, true, false);

and iterate over it (in order) as

foreach(PageValue value in values.Keys)
    doSomething(values[value]);

Of course, this only holds true if you always want things to come out in a certain order. Seems like a cleaner solution than magic strings.

lewellen
+12  A: 

I believe that .NET has the OrderedDictionary class to deal with this. It is not generic, but it can serve as a decent Hashtable substitute - if you don't care about strict type safety.

I've written a generic wrapper around this class, which I would be willing to share.

http://msdn.microsoft.com/en-us/library/system.collections.specialized.ordereddictionary.aspx

LBushkin
Good catch - I wasn't aware about that. It's a shame there isn't a generic version. And the implementation is... a list and a hashtable, basically as per my answer, but provided in the framework :)
Jon Skeet
Thanks. The lack of a generic implementation is unfortunate, but not insurmountable. I plan on posting my implementation as free code on my blog. I hope MS provides generic versions of all collection some day (including those in System.Collections.Specialized and System.ComponentModel) - but I'm not holding my breath.
LBushkin
A: 

Cadenza contains a generic OrderedDictionary<TKey, TValue> type which supports your requirements. You might consider that as well.

jonp
A: 

As Haxelit suggests, you might derive from KeyedCollection<TKey, TValue>. It actually uses a List underneath until you hit a certain threshold value, and then it maintains both a List and a Dictionary. If you can use a function to derive one of your keys from one of your values, then this is an easy solution. If not, then it gets pretty messy.

Neil Whitaker