Better Dictionary? (serializable with more functionality)

So I’m looking for a dictionary that provides more features than the one that .NET provides. Specifically, I’m looking for extra stuff like insertion and removal of keys/values at a specified index. Also, it would be nice if it played nice with Unity’s serialization system. Any ideas?

[EDIT] Use this instead: http://forum.unity3d.com/threads/finally-a-serializable-dictionary-for-unity-extracted-from-system-collections-generic.335797/


You could use this implementation which uses two lists as keys and values - should provide the functionality you’re asking. If you want it to play well with Unity’s serialization system you have to subclass it, so:

[System.Serializable]
public class StrObjDict : KVPList<string, UnityEngine.Object> { }

and then use it normally, like:

public class Test : MonoBehaviour
{
   public StrObjDict dict; // if not public, then annotate with SerializeField
}

Code:

[Serializable]
public class KVPList<TKey, TValue> : IDictionary<TKey, TValue>
{
	[SerializeField]
	protected List<TKey> keys;

	[SerializeField]
	protected List<TValue> values;

	public List<TKey> Keys { get { return keys; } }

	public List<TValue> Values { get { return values; } }

	public int Count { get { return keys.Count; } }

	public KVPList() : this(new List<TKey>(), new List<TValue>())
	{
	}

	public KVPList(List<TKey> keys, List<TValue> values)
	{
		this.keys = keys; 
		this.values = values;
	}

	public TValue this[TKey key]
	{
		get
		{
			int index;
			if (!TryGetIndex(key, out index))
			{
				throw new KeyNotFoundException(key.ToString());
			}
			return values[index];
		}
		set
		{
			int index;
			if (!TryGetIndex(key, out index))
			{
				Add(key, value);
			}
			else values[index] = value;
		}
	}

	public void SetKeyAt(int i, TKey value)
	{
		AssertIndexInBounds(i);
		if (value != null && !value.Equals(keys*))*
  •   		AssertUniqueKey(value);*
    

_ keys = value;_
* }*
* public TKey GetKeyAt(int i)*
* {*
* AssertIndexInBounds(i);*
_ return keys*;
}
public void SetValueAt(int i, TValue value)
{
AssertIndexInBounds(i);
values = value;
}
public TValue GetValueAt(int i)
{
AssertIndexInBounds(i);
return values;
}
public KeyValuePair<TKey, TValue> GetPairAt(int i)
{
AssertIndexInBounds(i);
return new KeyValuePair<TKey, TValue>(keys, values);
}
private void AssertIndexInBounds(int i)
{
if (!keys.InBounds(i))
throw new IndexOutOfRangeException(“i”);
}
public void Clear()
{
keys.Clear();
values.Clear();
}
public void Insert(int i, TKey key, TValue value)
{
AssertUniqueKey(key);
Assert.ArgumentNotNull(key, “Dictionary key cannot be null”);
keys.Insert(i, key);
values.Insert(i, value);
}
private void AssertUniqueKey(TKey key)
{
if (ContainsKey(key))
throw new ArgumentException(string.Format(“There’s already a key {0} defined in the dictionary”, key.ToString()));
}
public void Insert(TKey key, TValue value)
{
Insert(0, key, value);
}
public void Add(TKey key, TValue value)
{
Insert(Count, key, value);
}
public bool Remove(TKey key)
{
int index;
if (TryGetIndex(key, out index))
{
keys.RemoveAt(index);
values.RemoveAt(index);
return true;
}
return false;
}
public void RemoveAt(int i)
{
AssertIndexInBounds(i);
keys.RemoveAt(i);
values.RemoveAt(i);
}
public void RemoveLast()
{
RemoveAt(Count - 1);
}
public void RemoveFirst()
{
RemoveAt(0);
}
public bool TryGetValue(TKey key, out TValue result)
{
int index;
if (!TryGetIndex(key, out index))
{
result = default(TValue);
return false;
}
result = values[index];
return true;
}
public bool ContainsValue(TValue value)
{
return values.Contains(value);
}
public bool ContainsKey(TKey key)
{
return keys.Contains(key);
}
private bool TryGetIndex(TKey key, out int index)
{
return (index = keys.IndexOf(key)) != -1;
}
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
for (int i = 0; i < Count; i++)
yield return new KeyValuePair<TKey, TValue>(keys, values);
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
ICollection IDictionary<TKey, TValue>.Keys*

* {
get { return keys; }
}
bool IDictionary<TKey, TValue>.Remove(TKey key)
{
return Remove(key);
}
ICollection IDictionary<TKey, TValue>.Values*

* {
get { return values; }
}
public void Add(KeyValuePair<TKey, TValue> item)
{
keys.Add(item.Key);
values.Add(item.Value);
}
public bool Contains(KeyValuePair<TKey, TValue> item)
{
return ContainsKey(item.Key);
}
public void CopyTo(KeyValuePair<TKey, TValue> array, int arrayIndex)
{
for (int i = arrayIndex; i < array.Length; i++)
{
array = new KeyValuePair<TKey, TValue>(keys, values);
}
}
public bool IsReadOnly*

* {
get { return false; }
}
public bool Remove(KeyValuePair<TKey, TValue> item)
{
return Remove(item.Key);
}
}
public static class KVPDictionaryExtensions*

* {
public static Dictionary<TKey, TValue> ToDictionary<TKey, TValue>(this KVPList<TKey, TValue> d)
{
return RTHelper.CreateDictionary(d.Keys, d.Values);
}
public static KVPList<TKey, TValue> ToKVPList<TKey, TValue>(this IDictionary<TKey, TValue> d)
{
return new KVPList<TKey, TValue>(d.Keys.ToList(), d.Values.ToList());
}
}
Edit:
Sorry forgot to mention about RTHelper, it’s just a static utility class I use:
public static class RTHelper
{
///


/// Creates a dictionary out of the passed keys and values*
* ///

public static Dictionary<T, U> CreateDictionary<T, U>(IEnumerable keys, IList values)
{
return keys.Select((k, i) => new { k, v = values }).ToDictionary(x => x.k, x => x.v);
}
}
InBounds is just an extension method:
public static class IListExtensions
{
public static bool InBounds(this IList list, int i)
{
return i > -1 && i < list.Count;
}
}*
You could replace the code if you don’t want to add a helper and extensions static classes.
Don’t forget your usings:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;_