Some of my column data in CSV was out of column name order. Enumerating the data Dictionary as KeyValuePair did not return data in any particular order, and using System.Collection.Specialized's OrderedDictionary was cumbersome. So, I created my own orderly dictionary called ListDictionary which is discussed in this tip.
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
Michael Balloni is a manager of software development at a cybersecurity software and services provider.
Check out https://www.michaelballoni.com for all the programming fun he's done over the years.
He has been developing software since 1994, back when Mosaic was the web browser of choice. IE 4.0 changed the world, and Michael rode that wave for five years at a .com that was a cloud storage system before the term "cloud" meant anything. He moved on to a medical imaging gig for seven years, working up and down the architecture of a million-lines-code C++ system.
Michael has been at his current cybersecurity gig since then, making his way into management. He still loves to code, so he sneaks in as much as he can at work and at home.