I'm out of square brackets, but I do have some extensions methods if it's of any interest.
They can traverse endlessly and get an object or set a generic value to the end of a key chain of dictionary. The Get() returns an object, but that's easily fixable to a generic type if necessary.
Since the keys are based on params you could throw in a dynamic arrays of keys if you have a very dynamic environment.
Works like this:
var exists1 = info.Exists("Gen", "name", "One key too much"); // false
var exists2 = info.Exists("Gen", "chapters"); // true
var value1 = info.Get("Gen", "name", "One key too much"); // null
var value2 = info.Get("Gen", "chapters"); // "50"
var valueToSet = "myNewValue";
var successSet1 = info.TrySet(valueToSet, "Gen", "before", "One key too much"); // false
var successSet2 = info.TrySet(valueToSet, "Gen", "after"); // true | "Gen" -> "after" set to "myNewValue"
var dictionary1 = info.FindSubDictionary("Gen", "name", "One key too much"); // null
var dictionary2 = info.FindSubDictionary("Gen", "chapters"); // "Gen" sub dictionary
I haven't tested it much, nor given much thought into what it should return when, but if you find it useful, it could be something to start to tinker around with.
4 extension methods mostly working together, offering Exists, Get, TrySet and (what should be) an internal one to resolve the dictionary tree.
TrySet:
/// <summary>
/// Tries to set a value to any dictionary found at the end of the params keys, or returns false
/// </summary>
public static bool TrySet<T>(this System.Collections.IDictionary dictionary, T value, params string[] keys)
{
// Get the deepest sub dictionary, set if available
var subDictionary = dictionary.FindSubDictionary(keys);
if (subDictionary == null) return false;
subDictionary[keys.Last()] = value;
return true;
}
Get:
/// <summary>
/// Returns a value from the last key, assuming there is a dictionary available for every key but last
/// </summary>
public static object Get(this System.Collections.IDictionary dictionary, params string[] keys)
{
var subDictionary = dictionary.FindSubDictionary(keys);
if (subDictionary == null) return null; // Or throw
return subDictionary[keys.Last()];
}
Exists:
/// <summary>
/// Returns whether the param list of keys has dictionaries all the way down to the final key
/// </summary>
public static bool Exists(this System.Collections.IDictionary dictionary, params string[] keys)
{
// If we have no keys, we have traversed all the keys, and should have dictionaries all the way down.
// (needs a fix for initial empty key params though)
if (keys.Count() == 0) return true;
// If the dictionary contains the first key in the param list, and the value is another dictionary,
// return that dictionary with first key removed (recursing down)
if (dictionary.Contains(keys.First()) && dictionary[keys.First()] is System.Collections.IDictionary subDictionary)
return subDictionary.Exists(keys.Skip(1).ToArray());
// If we didn't have a dictionary, but we have multiple keys left, there are not enough dictionaries for all keys
if (keys.Count() > 1) return false;
// If we get here, we have 1 key, and we have a dictionary, we simply check whether the last value exists,
// thus completing our recursion
return dictionary.Contains(keys.First());
}
FindSubDictionary:
/// <summary>
/// Returns the possible dictionary that exists for all keys but last. (should eventually be set to private)
/// </summary>
public static System.Collections.IDictionary FindSubDictionary(this System.Collections.IDictionary dictionary, params string[] keys)
{
// If it doesn't exist, don't bother
if (!dictionary.Exists(keys)) return null; // Or throw
// If we reached end of keys, or got 0 keys, return
if (keys.Count() == 0) return null; // Or throw
// Look in the current dictionary if the first key is another dictionary.
return dictionary[keys.First()] is System.Collections.IDictionary subDictionary
? subDictionary.FindSubDictionary(keys.Skip(1).ToArray()) // If it is, follow the subdictionary down after removing the key just used
: keys.Count() == 1 // If we only have one key remaining, the last key should be for a value in the current dictionary.
? dictionary // Return the current dictionary as it's the proper last one
: null; // (or throw). If we didn't find a dictionary and we have remaining keys, the dictionary tree is invalid
}
Edit:
Noticed you wanted CSV-keys. It should be easy to just construct from above, but I made a quick example using a single csv-key with a bonus optional custom separator.
var csvResult1 = info.Get("Gen, chapters, One key too much"); // null
var csvResult2 = info.Get("Gen, chapters"); // "50"
// With custom separator
var csvResult3 = info.Get("Gen;chapters;One key too much", separator: ";");
var csvResult4 = info.Get("Gen; chapters", separator: ";");
Code:
/// <summary>
/// Returns a value from the last key of a csv string with keys, assuming there is a dictionary available for every key but the last.
/// </summary>
public static object Get(this System.Collections.IDictionary dictionary, string csvKeys, string separator = ",")
{
if (String.IsNullOrEmpty(csvKeys)) throw new ArgumentNullException("Csv key input parameters is not allowed to be null or empty", nameof(csvKeys));
var keys = csvKeys.Split(separator).Select(k => k.Trim());
var subDictionary = dictionary.FindSubDictionary(keys.ToArray());
if (subDictionary == null) return null; // Or throw
return subDictionary[keys.Last()];
}
You better test that last one though, I didn't run it. Should run fine though... I just can't give any guarantees it will :)