In .NET / C# I have input data of type IEnumerable<T>
with T
having some properties I want to use for lookups.
How can I build a two-level (maybe three-level) lookup using LanguageExt without producing hard to read code like this:
var items = Range(1, 1000)
.Map(i => new
{
Number = i,
Section = (byte) (i % 10),
Text = $"Number is i"
}); // just some test data
HashMap<byte, HashMap<int, string>> lookup
= toHashMap(
from item in items
group item.Text by (item.Section, item.Number) into gInner
group gInner by gInner.Key.Section into gOuter
select ( gOuter.Key, toHashMap(gOuter.Map(_ => (_.Key.Number, _.Head()))) )
);
Expected output: lookup hashmap with Section
as outer key, Number
as inner key and Text
as value.
I prefer solutions using LINQ syntax (maybe making it easier to combine this with transforming / filtering / ordering ...).
Language-ext has built-in extension methods for dealing with nested HashMap
and Map
types (up to four nested levels deep):
So, to lookup a type:
HashMap<int, HashMap<string, DateTime>> lookup = ...;
var value = lookup.Find(123, "Hello");
You can also add:
lookup = lookup.AddOrUpdate(123, "Hello", value);
There's also Remove
, MapRemoveT
, MapT
, FilterT
, FilterRemoveT
, Exists
, ForAll
, SetItemT
, TrySetItemT
, and FoldT
.
So, to answer your specific question:
var map = items.Fold(
HashMap<byte, HashMap<int, string>>(),
(s, item) => s.AddOrUpdate(item.Section, item.Number, item.Text));
If you do this a lot, then you could generalise it into an extension of Seq<(A, B, C)>
public static HashMap<A, HashMap<B, C>> ToHashMap<A, B, C>(this Seq<(A, B, C)> items) =>
items.Fold(
HashMap<A, HashMap<B, C>>(),
(s, item) => s.AddOrUpdate(item.Item1, item.Item2, item.Item3));
I had the same requirements, that's why I added these to language-ext :)