There are two scenarios where you have a list of data you want to index in a map. Either the data is a flat list of values and a computation of the keys is necessary, or you have a list of meta data with keys and value of each node. The list constructor and the From() methods, explained herein, are used when the number of values is known beforehand.

Metadata ready

Let's review the better scenario where all the data is readily available in some metadata form. Here we have a list of Tuples holding all the keys and values we need. We just want to index that data.

var list = new List<Tuple<int, int>>() { Tuple.Create(1,2) };

var map1k = Map<int, int>.From(
  list, // a list of all the meta data
  kv => kv.Item1, // extract the key from each meta data element
  kv => item.Item2); // extract the value from each meta data element

Note that the first parameter to the From() method group is an IEnumerable<T>, and each following parameter gives you each T element. The method doesn't care if you are using tuples or anything else.

There is a From() method for each Map class. Here is the method signature for the 3 key From() method

public static Map<K1, K2, K3, V> From<T>(IEnumerable<T> list, Func<T, K1> key1Gen, Func<T, K2> key2Gen, Func<T, K3> key3Gen, Func<T, V> valueGen);

And here is an example for 3 key maps.

var list = new List<Tuple<int, string, decimal, double>>() { Tuple.Create(1,"1", 1.42m, 2.401) };

var map1k = Map<int, string, decimal, double>.From(
  list, // a list of all the meta data
  kv => kv.Item1, // extract key 1
  kv => kv.Item2, // extract key 2
  kv => kv.Item3, // extract key 3
  kv => kv.Item4); // extract value

Value lists

You may not have a tuple-like grouping ready to go. Your data may be separate.

A very simple scenario would be where the key of each value can be uniquely identified by value alone. Take for example a program that has a list of strings of unique lengths. The program will later want to access each string by specifying length only.

var list = new List<string>() { "a", "ab", "abc" };
var map = new Map<int, string>(list, v => v.Length);

Perhaps your list of strings is not unique by length, and instead you want a list of each by length. In other words, you want a map of type:

var map = new Map<int, List<string>>();

We could set the DefaultGeneration to build a List<string> to help:

map.DefaultGeneration = k => new List<string>();

And then loop on our strings:

foreach (var text in list)
{
  map[text.Length].Add(text);
}

We can see that this scenario used a solution without the list constructor and without the From() methods. The reason being that we do not know the number of values beforehand. The length of the list is the number of strings, but the number of values in our map needed to be the number of unique lengths. That took some more computation. To make this scenario fit, we'd have to package the data first.

var list = new List<string>() { "a", "b", "ab", "abc" };
var map = Map<int, List<string>>.From(list.GroupBy(t => t.Length), g => g.Key, g => g.ToList());

The preceding example is nice if you want to later count the number of strings of a given length which was not indexed.

map.DefaultGeneration = _ => new List<string>();
int count100Length = map[100].Count;

I could have used DefautlValue so the empty list wouldn't be stored at all. That is an option to the reader.

Value list on multikey maps

No value list constructor was given to 2 and 3 key maps because calculating the unique keys from just a value seemed very unlikely with more than one key (even rare in the case of one key). Thus, the only supported constructor-style workflow to convert a list of data into an index mapping of that data where there is more than one key - is to use the From() methods to build each element. Revisit the top of this page for those examples.

Last edited Feb 2, 2012 at 1:03 AM by payonel, version 4

Comments

No comments yet.