Codibre.DictionaryChain 1.1.10

dotnet add package Codibre.DictionaryChain --version 1.1.10                
NuGet\Install-Package Codibre.DictionaryChain -Version 1.1.10                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Codibre.DictionaryChain" Version="1.1.10" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Codibre.DictionaryChain --version 1.1.10                
#r "nuget: Codibre.DictionaryChain, 1.1.10"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install Codibre.DictionaryChain as a Cake Addin
#addin nuget:?package=Codibre.DictionaryChain&version=1.1.10

// Install Codibre.DictionaryChain as a Cake Tool
#tool nuget:?package=Codibre.DictionaryChain&version=1.1.10                

Actions Status Actions Status Actions Status benchmark Test Coverage Maintainability

DictionaryChain extension for any .net project

Why

Some highly accessed lists can benefit from a chained dictionary structure. Let's say you have three fields where you apply a equality comparison to choose between the items of the list. This operation, unless you do a binary search, will have a processing complexity of O(N). However, if you create an instance of chained dictionaries with depth 3, where the keys for each level are the values for each field, you can have the same result in O(1), as long as you have stablished uniqueness for the fields used to build this instance. If not, you can have a list with every list item that matches that path, and now you'll have to traverse much fewer items than before, as the items are distributed between the nodes!

Long story short, you can better the performance of you application by caching those chained dictionaries on strategic points, making much less list traversals throughout your application execution!

How to use it

First, import the application namespace:

using Codibre.DictionaryChain

Now, you'll have the method ToDictionaryChain in every IEnumerable<> instance, and you just have to build it. Let's say you have a list with items that look like this:

struct Person {
    public string Name;
    public string City;
    public string State;
    public string Country;
}

If so, you may build a chain of dictionaries using the field country, state and city

var chain = list.ToDictionaryChain(x => x.Country, x => x.State, x => x.City);

The object chain will have a type like this:

IDictionary<string, IDictionary<string, IDictionary<string, List<Person>>>

So, if you want to find every listed person that lives in the country US, state of Florida, in the city of Tallahassee, you just have to do:

var persons = chain['US']['Florida']['Tallahassee'];

Or

if (!chain.TryGetValue('US', out var stateChain)
 || !stateChain.TryGetValue('Florida', out var cityChain)
 || !cityChain.TryGetValue('Tallahassee', out var persons)
) return null;

return persons;

Custom Leaf Values

Let's say you don't care about each individual value, but just one of them, or even some transformation based on the list of values that goes on each leaf. In that case, you can use the ToDictionaryChain variant where you specify the leaf construction:

var chain = list.ToDictionaryChain(
    () => 0, // Initializes the leaf value
    (acc, newItem) => acc + (newItem.Name.StartsWith("A") ? 1 : 0), // Transforms the leaf value based on the value of a new item
    x => x.Country, x => x.State, x.City)

That way, each leaf will have the number of people in each city with a name starting with "A"!

Limitation

This package, for now, can only infer a strong chain of Dictionary types up to depth 7, which we hope will be enough for most cases. If you need more than that, though, the result object will be an instance of ChainedDictionary. If you use it directly, you'll need to do some type checking at each level to check (or cast) wether it is another level, or already the leaf, as this object doesn't have the type of every level strongly defined. The best way to deal with this situation is to use the method MakeDictionary, just after creating it, like this:

var chain list.ToDictionary(
    x => x.Country,
    x => x.State,
    x => x.City,
    x => x.BirthCountry,
    x => x.BirthState,
    x => x.BirthCity,
    x => x.Age,
    x => x.Sex,
).MakeDictionary<Dictionary<string, Dictionary<string,
    Dictionary<string,
        Dictionary<string,
            Dictionary<string,
                Dictionary<string,
                    Dictionary<int,
                        Dictionary<SexEnum, Person>
                    >
                >
            >
        >
    >
>>>()

MakeDictionary will do some reflection operations to convert the ChainedDictionary into an instance of the chain of Dictionaries provided as type argument. It does have an overload so, the idea is not to use it every time, but to load it in memory once, then letting it being memoized for some time, to avoid repeating the operation and optimizing the query. Does it look good? No, it does not, but it is the better we could do with the current C# design, and it'll still speed up your application!

License

Licensed under MIT.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .NETStandard 2.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
1.1.10 210 12/11/2023
1.1.9 122 12/11/2023
1.1.8 138 12/11/2023
1.1.7 127 12/11/2023
1.1.6 130 12/11/2023
1.1.5 121 12/11/2023
1.1.4 119 12/11/2023
1.1.3 140 12/11/2023
1.1.2 136 12/10/2023
1.1.1 114 12/10/2023
1.1.0 132 12/10/2023
1.0.4 135 12/9/2023
1.0.3 136 12/7/2023