65.9K
CodeProject is changing. Read more.
Home

ListDictionary: An Improved OrderedDictionary

starIconstarIconstarIconstarIconstarIcon

5.00/5 (8 votes)

May 20, 2020

CPOL

1 min read

viewsIcon

10285

downloadIcon

130

ListDictionary provided Dictionary-like functionality with ordered enumeration

Introduction

I've been on a CSV kick, slinging List<string>s of column names and Dictionary<string, object>s of column data. I realized that some of my column data was out of column name order. Turns out that enumerating the data Dictionary as KeyValuePair does not return data in any particular order. So I needed an orderly dictionary.

Online articles said use System.Collection.Specialized's OrderedDictionary. Great, it's ordered! But it's not "generic", it's object-to-object, needing casting all over the place for even light duty. Yuck!

So I said, "Screw it!" and decided to write something to do the job. It would have a Dictionary inside for quick lookups, and a List inside to keep things in order. I call it ListDictionary. Not designed for large amounts of data, focused on orderly behavior, I hope it serves you well.

using System;
using System.Collections.Generic;
using System.Linq;

namespace metastrings
{
   public class ListDictionary<K, V> where K : IComparable<K>
   {
     public List<KeyValuePair<K, V>> Entries => m_list;
     public IEnumerable<K> Keys => m_list.Select(kvp => kvp.Key);
     public IEnumerable<V> Values => m_list.Select(kvp => kvp.Value);

     public V this[K key]
     {
       get { return m_dict[key]; }
       set { Set(key, value); }
     }

     public int Count => m_list.Count;
     public bool ContainsKey(K key) => m_dict.ContainsKey(key);

     public K FirstKey => m_list[0].Key;

     public void Set(K key, V val)
     {
       bool added = false;
       if (m_dict.ContainsKey(key))
       {
         for (int i = 0; i < m_list.Count; ++i)
         {
           K curKey = m_list[i].Key;
           if (curKey.CompareTo(key) == 0)
           {
             m_list[i] = new KeyValuePair<K, V>(key, val);
             added = true;
             break;
           }
         }
       }

       if (!added)
         m_list.Add(new KeyValuePair<K, V>(key, val));

       m_dict[key] = val;
     }

     private List<KeyValuePair<K, V>> m_list = new List<KeyValuePair<K, V>>();
     private Dictionary<K, V> m_dict = new Dictionary<K, V>();
   }
}

Not a lot to it. Not fully fleshed out, I just added the stuff I needed, and some unit tests:

ListDictionary<string, int> dict = new ListDictionary<string, int>();

dict["foo"] = 1;
dict["bar"] = 2;

Assert.AreEqual(1, dict["foo"]);
Assert.AreEqual(2, dict["bar"]);

Assert.AreEqual(2, dict.Count);

Assert.AreEqual("foo", dict.FirstKey);

Assert.AreEqual("foo", dict.Entries.First().Key);
Assert.AreEqual(1, dict.Entries.First().Value);

Assert.AreEqual("bar", dict.Entries.Last().Key);
Assert.AreEqual(2, dict.Entries.Last().Value);

Assert.IsTrue(dict.Keys.Contains("foo"));
Assert.IsTrue(dict.Values.Contains(1));

Assert.IsTrue(dict.Keys.Contains("bar"));
Assert.IsTrue(dict.Values.Contains(2));

Assert.IsTrue(dict.ContainsKey("foo"));
Assert.IsTrue(dict.ContainsKey("bar"));
Assert.IsTrue(!dict.ContainsKey("blet"));

dict["foo"] = 42;
Assert.AreEqual(42, dict["foo"]);

Points of Interest

Got to use indexers with get and set. Got to use IComparable. Used OrderedDictionary enough to hate it. What did people do before .NET 2.0?

Attached is the ListDictionary class, and a few other juicy morsels. Enjoy!

History

  • 20th May, 2020: Initial version