List(Of T)
stores data indexed by integer
Dictionary(Of String, T)
stores data indexed via string
Is there a type or generic or specialized something that would let me access an array of T
by either an index or name?
List(Of T)
stores data indexed by integer
Dictionary(Of String, T)
stores data indexed via string
Is there a type or generic or specialized something that would let me access an array of T
by either an index or name?
I think System.Collections.Specialized.OrderedDictionary
is what you're looking for.
Is accessing a list of T by index (List<
List<
T>
>
) good enough?
List<List<foo>> list = new List<List<foo>>();
List<foo> firstList = list[0];
It sounds like you need a multimap, but unfortunately there is no general-purpose implementation of this in the BCL. As mentioned in another answer, System.Collections.Specialized.OrderedDictionary is a specific implementation that might cover your needs, although it doesn't use generics.
The Specialized version of OrderedDictionary is not generic.
You could implement a Generic Dictionary interface with a custom GenericOrderedDictionary class.
Have a private
List<TKey>
and private List<TValue>
.
Visual Studio can stub the interface methods for you.
The start of it would look like:
public class GenericOrderedDictionary< TKey, TValue >
: IDictionary<TKey, TValue>
{
private List<TKey> keys;
private List<TValue> values;
#region IDictionary<TKey,TValue> Members
void IDictionary<TKey, TValue>.Add( TKey key, TValue value )
{
keys.Add( key );
values.Add( value );
}
bool IDictionary<TKey, TValue>.ContainsKey( TKey key )
{
return keys.Contains( key );
}
ICollection<TKey> IDictionary<TKey, TValue>.Keys
{
get
{
return new List<TKey>( keys );
}
}
bool IDictionary<TKey, TValue>.Remove( TKey key )
{
int index = keys.IndexOf( key );
if ( index >= 0 )
{
keys.Remove( key );
values.RemoveAt( index );
}
}
If you have an array of T, you can generate multiple dictionaries from this array by calling ToDictionary and feeding in different properties of T.
Supposing T is customer:
Customer[] myCustomers = getArray();
Dictionary<int, Customer> byID = myCustomers
.ToDictionary(c => c.ID);
Dictionary<string, Customer> byName = myCustomers
.ToDictionary(c => c.Name);
Dictionary<int, Customer> byOriginalPosition = myCustomers
.Select( (c, i) => new {c, i})
.ToDictionary(x => x.i, x => x.c);
I think something like this is closest to what you want:
class IndexDictionary<TKey, TValue> : Dictionary<TKey, TValue>
{
public TValue this[int i]
{
get { return this[Keys.ElementAt(i)]; }
set { this[Keys.ElementAt(i)] = value; }
}
}
You're just taking a regular Dictionary<> and adding the ability to index by int as well.
Edit: Mehrdad raises a good point, that my IndexDictionary.Add(TKey, TValue)
method may result in an insert rather than an append. If that will cause problems in your situation, then I would suggest something like this:
class OrderedDictionary<TKey, TValue> : IDictionary<TKey, TValue>
{
private OrderedDictionary data = new OrderedDictionary();
public TValue this[int i]
{
get { return (TValue)data[i]; }
set { data[i] = value; }
}
//Implement IDictionary<TKey, TValue> using the methods of the OrderedDictionary
}
This gives you the order-preserving benefits of OrderedDictionary
with the type safety of Dictionary<TKey, TValue>
.
If your "names" are easily determined from your "T", I suggest KeyedCollection
.
It works like a List
, in that you can look up items by index. But it also works like a dictionary, in that it uses a Dictionary internally to map names (keys) to the appropriate index and provides an indexer for your key type.
You asked how it knows what to use for the key. KeyedCollection
is an abstract class that you have to inherit. Fortunately, it's easy to do. The only method you need to overload is GetKeyForItem()
. That method is the answer to your question. For example, take this simple class:
Public Class MyClass
Public UniqueID As Guid
Public OtherData As String
End Class
You could implement KeyedCollection like this:
Public Class MyClassCollection
Inherits KeyedCollection(Of Guid, MyClass)
Public Overrides Function GetKeyForItem(ByVal item As MyClass) As Guid
Return item.UniqueID
End Function
End Class
That's all there is to it. You now have a collection that will work like a dictionary or a list. It's even more powerful when you can use generics or other interfaces to avoid tying the class to a specific type.
This is what I'm testing now most of the functionality was autofilled in for me when I implemented IDictionary
Public Class bDictionary(Of TKey, TVAlue)
Implements IDictionary(Of TKey, TVAlue)
Private dictionary As New Dictionary(Of TKey, TVAlue)
Private list As List(Of TKey)
Default Public Property Item(ByVal which As TKey) As TVAlue Implements System.Collections.Generic.IDictionary(Of TKey, TVAlue).Item
Get
Return dictionary(which)
End Get
Set(ByVal value As TVAlue)
dictionary(which) = value
End Set
End Property
Default Public Property Item(ByVal index As Integer) As TVAlue
Get
Return dictionary(list(index))
End Get
Set(ByVal value As TVAlue)
dictionary(list(index)) = value
End Set
End Property
Public Sub Add(ByVal key As TKey, ByVal value As TVAlue) Implements System.Collections.Generic.IDictionary(Of TKey, TVAlue).Add
dictionary.Add(key, value)
list.Add(key)
End Sub
Public Sub Add(ByVal item As System.Collections.Generic.KeyValuePair(Of TKey, TVAlue)) Implements System.Collections.Generic.ICollection(Of System.Collections.Generic.KeyValuePair(Of TKey, TVAlue)).Add
Add(item.Key, item.Value)
End Sub
Public Sub Clear() Implements System.Collections.Generic.ICollection(Of System.Collections.Generic.KeyValuePair(Of TKey, TVAlue)).Clear
dictionary.Clear()
list.Clear()
End Sub
Public Function Contains(ByVal item As System.Collections.Generic.KeyValuePair(Of TKey, TVAlue)) As Boolean Implements System.Collections.Generic.ICollection(Of System.Collections.Generic.KeyValuePair(Of TKey, TVAlue)).Contains
If dictionary.ContainsKey(item.Key) AndAlso dictionary(item.Key).Equals(item.Value) Then
Return True
Else
Return False
End If
End Function
Public ReadOnly Property Count() As Integer Implements System.Collections.Generic.ICollection(Of System.Collections.Generic.KeyValuePair(Of TKey, TVAlue)).Count
Get
Return list.Count
End Get
End Property
Public ReadOnly Property IsReadOnly() As Boolean Implements System.Collections.Generic.ICollection(Of System.Collections.Generic.KeyValuePair(Of TKey, TVAlue)).IsReadOnly
Get
Return False
End Get
End Property
Public Function Remove(ByVal item As System.Collections.Generic.KeyValuePair(Of TKey, TVAlue)) As Boolean Implements System.Collections.Generic.ICollection(Of System.Collections.Generic.KeyValuePair(Of TKey, TVAlue)).Remove
Return Remove(item.Key)
End Function
Public Function ContainsKey(ByVal key As TKey) As Boolean Implements System.Collections.Generic.IDictionary(Of TKey, TVAlue).ContainsKey
Return list.Contains(key)
End Function
Public ReadOnly Property Keys() As System.Collections.Generic.ICollection(Of TKey) Implements System.Collections.Generic.IDictionary(Of TKey, TVAlue).Keys
Get
Return dictionary.Keys
End Get
End Property
Public Function Remove(ByVal key As TKey) As Boolean Implements System.Collections.Generic.IDictionary(Of TKey, TVAlue).Remove
If list.Contains(key) Then
list.Remove(key)
dictionary.Remove(key)
Return True
Else
Return False
End If
End Function
Public Function TryGetValue(ByVal key As TKey, ByRef value As TVAlue) As Boolean Implements System.Collections.Generic.IDictionary(Of TKey, TVAlue).TryGetValue
Return dictionary.TryGetValue(key, value)
End Function
Public ReadOnly Property Values() As System.Collections.Generic.ICollection(Of TVAlue) Implements System.Collections.Generic.IDictionary(Of TKey, TVAlue).Values
Get
Return dictionary.Values
End Get
End Property
Public Sub CopyTo(ByVal array() As System.Collections.Generic.KeyValuePair(Of TKey, TVAlue), ByVal arrayIndex As Integer) Implements System.Collections.Generic.ICollection(Of System.Collections.Generic.KeyValuePair(Of TKey, TVAlue)).CopyTo
For Each Item As TKey In dictionary.Keys
array.SetValue(New KeyValuePair(Of TKey, TVAlue)(Item, dictionary(Item)), arrayIndex)
arrayIndex += 1
Next
End Sub
Public Function GetEnumerator() As System.Collections.IEnumerator Implements System.Collections.IEnumerable.GetEnumerator
Return dictionary.GetEnumerator()
End Function
Public Function GetEnumerator1() As System.Collections.Generic.IEnumerator(Of System.Collections.Generic.KeyValuePair(Of TKey, TVAlue)) Implements System.Collections.Generic.IEnumerable(Of System.Collections.Generic.KeyValuePair(Of TKey, TVAlue)).GetEnumerator
Return dictionary.GetEnumerator
End Function
End Class