Say, I have an string[]
of undetermined length, and a Dictionary<string, object>
, which more Dictionaries of its exact type (or other types, which doesn't matter here).
Now I want to change a value in a Dictionary
in a Dictionary
... in my Dictionary depending on my string array.
Here's a little Visualisation:
string[]: { "Human", "Legs", "Walking" }
Dictionary:
↳ Key "Human":
↳ Key "Legs":
↳ Key "Walking"
I wanna set the value of the key "Walking" in this example (lets just say to true
)
Now my question is: How do i achive this in C#?
Any answers will be appreciated, please excuse me if i am overthinking this.
CodePudding user response:
Because you're using Object
for TValue
you'll need to use the x is T v
operator to test if an Object
is another dictionary, e.g. if( x is Dictionary<String,Object> dict )
.
The difficulty here is that you can't use x is T v
with a strongly-typed T
because IReadOnlyDictionary<K,V>
is not covariant over V
, even when you know what K
is (as C# and .NET do not support partially-closed generic types).
...but if you don't need/want exactly-typed types and provided that all dictionary objects in your graph are always exactly Dictionary<String,Object>
(and not Dictionary<String,Int32>
or Dictionary<String,String>
) then you can do this:
String[] dictPath = new[] { "Human", "Legs", "Walking" };
Dictionary<String,Object> dictGraph = ...
Object humanLegsWalkingValue = GetValueAtPath( dictGraph, dictPath );
//
static Object? GetValueAtPath( Dictionary<String,Object> root, String[] dictPath )
{
List<String> breadcrumbs = new List<String>(); // For reporting errors.
Dictionary<String,Object> current = root;
foreach( String name in dictPath )
{
breadcrumbs.Add( name );
if( current.TryGetValue( name, out Object? value ) )
{
if( value is Dictionary<String,Object> dict )
{
current = dict;
}
else
{
if( breadcrumbs.Count == dictPath.Length )
{
return value;
}
else
{
String valueType = value is null ? "null" : value.GetType().FullName;
const String MSG_FMT = "Value at \"{0}\" is not a dictionary but is actually {1}, therefore \"{2}\" is unreachable."
throw new InvalidOperationException( String.Format( MSG_FMT, String.Join( ".", breadcrumbs ), valueType, String.Join( ".", dictPath ) ) );
}
}
}
else
{
const String MSG_FMT = "Could not find name \"{0}\" at \"{1}\" therefore \"{2}\" is unreachable."
throw new KeyNotFoundException( String.Format( MSG_FMT, name, String.Join( ".", breadcrumbs ), String.Join( ".", dictPath ) ) );
}
}
}
To set a value, it uses almost the exact same logic to walk the dictionary-graph, except it stops 1-step short of the end of dictPath
and uses that to set an entry in current
:
static void SetValueAtPath( Dictionary<String,Object> root, String[] dictPath, Object newValue )
{
List<String> breadcrumbs = new List<String>(); // For reporting errors.
Dictionary<String,Object> current = root;
foreach( ( String name, Int32 idx ) in dictPath.Select( ( n, i ) => ( n, i ) ) )
{
breadcrumbs.Add( name );
if( idx == dictPath.Length - 1 )
{
current[ name ] = newValue;
return;
}
else if( current.TryGetValue( name, out Object? value ) )
{
if( value is Dictionary<String,Object> dict )
{
current = dict;
}
else
{
String valueType = value is null ? "null" : value.GetType().FullName;
const String MSG_FMT = "Value at \"{0}\" is not a dictionary but is actually {1}, therefore {2} is unreachable."
throw new InvalidOperationException( String.Format( MSG_FMT, String.Join( ".", breadcrumbs ), valueType, String.Join( ".", dictPath ) );
}
}
else
{
const String MSG_FMT = "Could not find name \"{0}\" at \"{1}\" therefore \"{2}\" is unreachable."
throw new KeyNotFoundException( String.Format( MSG_FMT, name, String.Join( ".", breadcrumbs ), String.Join( ".", dictPath ) ) );
}
}
}
CodePudding user response:
This might not be a (direct) answer to your question:
string[] strs = new string[] { "Human", "Legs", "Walking" };
Dictionary<string,string> dict = new Dictionary<string, string>();
for(int i=0; i<strs.Length; i ) {
dict.Add(strs[i],(i>0?strs[i-1]:""));
}
string find = "Human";
if(dict.ContainsKey(find)) {
System.Console.WriteLine($"Hello: {find}");
foreach (var item in dict.Where(x=>x.Value==find))
{
System.Console.WriteLine($"You have: {item.Key}");
foreach (var item2 in dict.Where(x=>x.Value==item.Key))
{
System.Console.WriteLine($"Which can make you: {item2.Key}");
}
}
}
else
{
System.Console.WriteLine($"Could not find: {find}");
}
output:
Hello: Human
You have: Legs
Which can make you: Walking
NOTE: It's pretty unclear why the title of your question has Dictionary
and you start defining an array (string[]
).