I've seen lots of questions about this, but I haven't seen any about doing it recursively. I've created some extension methods that do a pretty good job of pretty printing at a depth of 1. It looks like this:
public static string PrettyToString<K, V>(this Dictionary<K, V> dictionary)
{
string result = "{";
foreach (var kvp in dictionary)
{
result += $"({kvp.Key}, {kvp.Value}) ";
}
result += "}";
return result;
}
public static string PrettyToString<T>(this List<T> list)
{
string result = "{";
foreach (var element in list)
{
result += $"{element}, ";
}
result += "}";
return result;
}
public static string PrettyToString<T>(this T [] list)
{
string result = "{";
foreach (var element in list)
{
result += $"{element}, ";
}
result += "}";
return result;
}
But, what if K, V, or T is another collection like a List or a Dictionary? I want to recursively pretty print, but I'm not sure how to do that. As a result, my output looks like this:
{(foo, System.Collections.Generic.Dictionary`2[System.String,System.Boolean])...
I want it to look like this instead:
{(foo, {(bar, true)})...
I'm looking for methods that print recursively regardless of the nested types:
var x = new List<Dictionary<string, string>>();
var y = new Dictionary<Dictionary<string, string>, string>>();
var z = new Dictionary<Dictionary<string, string>, List<string>>>();
...
x.PrettyToString();
y.PrettyToString();
z.PrettyToString();
Should all recursively print out the contents. How do I achieve that?
I changed the signature of your methods and made them non-generic.
The trick is to determine the type before converting.
Look at the sample code below. I hope it helps.
Please look at the Ext class at the bottom of the source code.
Try it online
using System;
using System.Collections;
using System.Collections.Generic;
namespace ConsoleApp8
{
class Program
{
static void Main(string[] args)
{
var Dic = new Dictionary<int, string> { { 1, "Ali" }, { 2, "B" } };
Console.WriteLine(Dic.PrettyToString());
var Dic1 = new Dictionary<string, float> { { "Ali", 12.5f }, { "B", 99.9f } };
Console.WriteLine(Dic1.PrettyToString());
var Dic2 = new Dictionary<List<int>, string>
{
{ new List<int> { 1, 2, 3 }, "A" },
{ new List<int> { 4, 5, 6 }, "B" }
};
Console.WriteLine(Dic2.PrettyToString());
var Dic3 = new Dictionary<Dictionary<string, string>, string>
{
{ new Dictionary<string, string> { { "a", "A" }, { "b", "B" } }, "Capital" },
{ new Dictionary<string, string> { { "1", "1" }, { "2", "4" }, { "4", "16" } }, "Power" }
};
Console.WriteLine(Dic3.PrettyToString());
var Dic4 = new Dictionary<Dictionary<string, string>, List<string>>
{
{ new Dictionary<string, string> { { "a", "A" }, { "b", "B" } }, new List<string> { "A", "B" } },
{ new Dictionary<string, string> { { "1", "1" }, { "2", "4" }, { "4", "16" } }, new List<string> { "1", "2", "4" } }
};
Console.WriteLine(Dic4.PrettyToString());
var L = new List<List<int>>
{
new List<int> { 1, 2, 3 },
new List<int> { 4, 5, 6 }
};
Console.WriteLine(L.PrettyToString());
Console.ReadKey();
}
}
static class Ext
{
public static string PrettyToString(this IDictionary dictionary)
{
string result = "{";
foreach (var Key in dictionary.Keys)
{
result += string.Format("({0}, {1}) ", PrettyToString(Key), PrettyToString(dictionary[Key]));
}
result += "}";
return result;
}
public static string PrettyToString(this IEnumerable list)
{
string result = "{";
foreach (var element in list)
{
result += string.Format("{0}, ", PrettyToString(element));
}
result += "}";
return result;
}
private static string PrettyToString(object O)
{
var S = O as string;
if (S != null) return S;
var D = O as IDictionary;
if (D != null) return D.PrettyToString();
var L = O as IEnumerable;
if (L != null) return L.PrettyToString();
return O.ToString();
}
}
}
Related
List object1
[ { "empId":10001, "empName":"test1" }, { "empId":10002, "empName":"test2" } ]
List object2
[ { "empId":10001, "emailAddress":"test1#mail.com" }, { "empId":10002, "emailAddress":"test2#mail.com" } ]
Trying to get the merge result which matches "empId" in both objects.
Result
[
{
"empId":10001,
"empName":"test1",
"emailAddress":"test1#mail.com"
},
{
"empId":10002,
"empName":"test2",
"emailAddress":"test2#mail.com"
}
]
I have tried https://www.newtonsoft.com/json/help/html/MergeJson.htm. but not able to do the matching logic "empId"
Here is some code that will Join and Merge, working here.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Dynamic;
using System.Linq;
using Newtonsoft.Json;
public class Program
{
public static void Main()
{
var list1 = new[]
{
new { empId = 10001, empName = "test1" },
new { empId = 10002, empName = "test2" }
};
var list2 = new[]
{
new { empId = 10001, emailAddress = "test1#mail.com" },
new { empId = 10002, emailAddress = "test2#mail.com" }
};
var results1 = list1.MergeJoin(list2, e => e.empId);
Console.WriteLine($"{nameof(results1)}:");
Console.WriteLine(JsonConvert.SerializeObject(results1, Formatting.Indented));
Console.WriteLine();
IList<dynamic> dynamicList1 = new List<dynamic>
{
new { empId = 10001, empName = "test1", utterance = "wibble" },
new { empId = 10002, empName = "test2", expression = "bemused" }
};
IList<dynamic> dynamicList2 = new List<dynamic>
{
new { empId = 10001, emailAddress = "test1#mail.com", IQ = "moron" },
new { empId = 10002, emailAddress = "test2#mail.com", smell = "cheesy" }
};
var results2 = dynamicList1.MergeJoin(dynamicList2, e => e.empId);
Console.WriteLine($"{nameof(results2)}:");
Console.WriteLine(JsonConvert.SerializeObject(results2, Formatting.Indented));
}
}
public static class Extensions
{
public static IEnumerable<dynamic> MergeJoin<TKey>(
this IEnumerable<dynamic> outer,
IEnumerable<dynamic> inner,
Func<dynamic, TKey> keyAccessor)
{
return outer.Join(
inner,
keyAccessor,
keyAccessor,
Merge);
}
public static dynamic Merge(dynamic left, dynamic right)
{
IDictionary<string, object> dictionary1 = GetKeyValueMap(left);
IDictionary<string, object> dictionary2 = GetKeyValueMap(right);
var result = new ExpandoObject();
var d = result as IDictionary<string, object>;
foreach (var pair in dictionary1.Concat(dictionary2))
{
d[pair.Key] = pair.Value;
}
return result;
}
private static IDictionary<string, object> GetKeyValueMap(object values)
{
if (values == null)
{
return new Dictionary<string, object>();
}
var map = values as IDictionary<string, object>;
if (map != null)
{
return map;
}
map = new Dictionary<string, object>();
foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(values))
{
map.Add(descriptor.Name, descriptor.GetValue(values));
}
return map;
}
}
This should output,
results1:
[
{
"empId": 10001,
"empName": "test1",
"emailAddress": "test1#mail.com"
},
{
"empId": 10002,
"empName": "test2",
"emailAddress": "test2#mail.com"
}
]
results2:
[
{
"empId": 10001,
"empName": "test1",
"utterance": "wibble",
"emailAddress": "test1#mail.com",
"IQ": "moron"
},
{
"empId": 10002,
"empName": "test2",
"expression": "bemused",
"emailAddress": "test2#mail.com",
"smell": "cheesy"
}
]
The Merge() function you are using is not the only thing you need. You need LINQ's Join() to "merge" the two lists and then Merge() to merge individual items:
var xs = JArray
.Parse(#"[
{""empId"": 10001, ""empName"": ""test1""},
{""empId"": 10002, ""empName"": ""test2""}
]")
.Values<JObject>();
var ys = JArray
.Parse(#"[
{""empId"": 10001, ""emailAddress"": ""test1#mail.com""},
{""empId"": 10002, ""emailAddress"": ""test2#mail.com""}
]")
.Values<JObject>();
var merged = xs.Join(
ys,
x => x["empId"],
y => y["empId"],
(x, y) => { x.Merge(y); return x; });
Note that you can probably just cast the dynamic types back to JObject for much easier handling.
I want to get an object from a Dictionary<string, object> using a string expression as key.
For example taking the following data structure:
var data = new Dictionary<string, object>()
{
{ "Products", new object[]
{
new Dictionary<string, object>()
{
{ "Name", "Tin of biscuits" }
}
}
}
}
I want to return the product name with the expression: "Products[0].Name".
This is just an example, the data could be of any depth and the string expression could be something like "BookStore.Books[3].Author.ContactDetails.Address" for eg.
So far I have been experimenting with Recursive methods and have also tried string.Split('.') with Aggregate Linq method, but I am struggling and I think this is a perfect little puzzle for SO.
Assuming you'll always have the Dictionary<string, object> and object[] in your data structure, this approach should work:
private static object? GetSelectedValueOrDefault(this Dictionary<string, object> data, string selector)
{
string[] selectorSegments = selector.Split('.');
if (selectorSegments.Length == 0)
{
return null;
}
object? currentNode = data.GetValueOrDefault(selectorSegments[0]);
if (currentNode == null)
{
return null;
}
for (int index = 1; index < selectorSegments.Length; index++)
{
string segment = selectorSegments[index];
if (currentNode is not Dictionary<string, object> dictionary)
{
return null;
}
var selectorWithIndex = GetSelectorAndElementIndexOrDefault(segment);
if (selectorWithIndex is not null &&
currentNode is Dictionary<string, object> dict)
{
currentNode = dict.GetValueOrDefault(selectorWithIndex.Value.ItemSelector);
currentNode = GetElementOrDefault(currentNode, selectorWithIndex.Value.Index);
continue;
}
currentNode = dictionary.GetValueOrDefault(segment);
if (index == selectorSegments.Length - 1)
{
return currentNode;
}
}
return null;
}
private static object? GetElementOrDefault(object? currentNode, int index)
{
if (currentNode is not object[] array)
{
return null;
}
if (index >= array.Length)
{
return null;
}
return array[index];
}
private static (string ItemSelector, int Index)? GetSelectorAndElementIndexOrDefault(string segment)
{
if (!segment.Contains('['))
{
return null;
}
string[] result = segment.Split('[', ']');
return (result[0], int.Parse(result[1]));
}
Example
var data = new Dictionary<string, object>()
{
{
"BookStore",
new Dictionary<string, object>()
{
{
"Name",
"The Book Store"
},
{
"Books",
new object[]
{
new Dictionary<string, object>(),
new Dictionary<string, object>(),
new Dictionary<string, object>(),
new Dictionary<string, object>()
{
{
"Author",
new Dictionary<string, object>()
{
{
"Name",
"Luke T O'Brien"
},
{
"ContactDetails",
new Dictionary<string, object>()
{
{
"Address",
"Some address"
}
}
}
}
}
}
}
}
}
}
};
Console.WriteLine(data.GetSelectedValueOrDefault("BookStore.Name"));
Console.WriteLine(data.GetSelectedValueOrDefault("BookStore.Books[3].Author.Name"));
Console.WriteLine(data.GetSelectedValueOrDefault("BookStore.Books[3].Author.ContactDetails.Address"));
Output
The Book Store
Luke T O'Brien
Some address
I am having trouble with the syntax of how to get an XUnit Test Method to accept a dictionary as a paramater. This is my code which is broken:
Public static Dictionary<string, string> vals = new Dictionary<string, string> { { "a", "1" }, { "b", "2" }, { "ca", "3" } };
public static object[] dicList = new object[] { vals };
[Theory]
[MemberData(nameof(dicList))]
public void CheckSomething_ReturnsSomething(Dictionary<string, string> values)
{
test stuff...
I am getting an error I understand from the error thrown that I need a return type of
IEnumerable<object[]>
I am unsure of how to achieve this with a dictionary.
Have added some code as suggested: I am still getting an error:
public static Dictionary<string, string> vals = new Dictionary<string, string> { { "a", "1" }, { "b", "2" }, { "ca", "3" } };
public static IEnumerable<object[]> dicList = new List<object[]> { new object[] { vals } };
[Theory]
[MemberData(dicList)] //error here
public void DriverLogon_CheckParams(Dictionary<string, string> values)
{
}
The new error is
error. "Cannot convert from IEnumerable<object[]> To string
The dicList must be an IEnumerable<object[]> like the error message says. This is because one item in the dicList contains all the parameter values for one test (this doesn't matter for your case since you only have one parameter, but it's good to know).
So your dicList becomes a List<object[]> instead of just plain object array:
public static Dictionary<string, string> vals = new Dictionary<string, string> { { "a", "1" }, { "b", "2" }, { "ca", "3" } };
public static IEnumerable<object[]> dicList = new List<object[]> { new object[] { vals } };
I have a class and an array of property names defined as follows:
public class Dog {
public string Name { get; set; }
public string Breed { get; set; }
public int Age { get; set; }
}
var desiredProperties = new [] {"Name", "Breed"};
I also have a method that returns a list of dog objects:
List<Dog> dogs = GetAllDogs();
Is there an way I can return a subset of dogs that only contain the properties defined within the desiredProperties array? Eventually, this resulting list will be serialized to JSON.
I have been struggling with this problem for some time now, considering that the user will be allowed to specify any combination of properties (assuming they are all valid) as the output within the array. Some more examples:
var desiredProperties = new [] {"Name", "Age"};
// Sample output, when serialized to JSON:
// [
// { Name: "Max", Age: 5 },
// { Name: "Spot", Age: 2 }
// ]
var desiredProperties = new [] {"Breed", "Age"};
// [
// { Breed: "Scottish Terrier", Age: 5 },
// { Breed: "Cairn Terrier", Age: 2 }
// ]
you can write a function to do that. Use the extension method below.
public static class Extensions
{
public static object GetPropertyValue(this object obj, string propertyName)
{
return obj.GetType().GetProperty(propertyName).GetValue(obj);
}
public static List<Dictionary<string, object>> FilterProperties<T>(this IEnumerable<T> input, IEnumerable<string> properties)
{
return input.Select(x =>
{
var d = new Dictionary<string, object>();
foreach (var p in properties)
{
d[p] = x.GetPropertyValue(p);
}
return d;
}).ToList();
}
}
Test it like
var dogs = GetAllDogs();
var f1 = dogs.FilterProperties(new[]
{
"Name", "Age"
});
var f2 = dogs.FilterProperties(new[]
{
"Breed", "Age"
});
Console.WriteLine(JsonConvert.SerializeObject(f1));
Console.WriteLine(JsonConvert.SerializeObject(f2));
and result is
[{"Name":"Spot","Age":2},{"Name":"Max","Age":5}]
[{"Breed":"Cairn Terrier","Age":2},{"Breed":"Scottish Terrier","Age":5}]
I don't have a clue if this is the most efficient way to do it, but it's a way of doing it:
var list = new List<Dog>();
list.Add(new Dog {Name = "Max", Breed = "Bull Terrier", Age = 5});
list.Add(new Dog {Name = "Woofie", Breed = "Collie", Age = 3});
var desiredProperties = new[] {"Name", "Breed"};
var exportDogs = new List<Dictionary<string, object>>();
foreach(var dog in list)
{
var exportDog = new Dictionary<string, object>();
foreach(var property in desiredProperties)
{
exportDog[property] = dog.GetType().GetProperty(property).GetValue(dog, null);
}
exportDogs.Add(exportDog);
}
var output = JsonConvert.SerializeObject(exportDogs);
The output will look like this:
[{"Name":"Max","Breed":"Bull Terrier"},{"Name":"Woofie","Breed":"Collie"}]
If, however, you don't need to dynamically access properties, it's a lot better to do something like this:
var output = list.Select(dog => new {dog.Name, dog.Breed});
Then just serialize the output.
something like this...not tested...
var desiredProperties = new [] {"Name", "Breed"};
var lst = (from asm in AppDomain.CurrentDomain.GetAssemblies()
from asmTyp in asm.GetTypes()
where typeof(dog).IsAssignableFrom(asmTyp) && desiredProperties.All(p=> PropertyExists(asmTyp, p))
select asmTyp).ToArray();
private bool PropertyExists(Type dogType, string name)
{
bool ret=true;
try{ dogType.GetProperty(name);}
catch{ret=false};
return ret;
}
I have an adjacency list like this:
A - A1
A - A2
A - A3
A3 - A31
A31 - A311
A31 - A312
I am trying to obtain the following output:
{
"name": "A",
"children": [{
"name": "A1"
}, {
"name": "A2"
}, {
"name": "A3",
"children": [{
"name": "A31",
"children": [{
"name": "A311"
}, {
"name": "A312"
}]
}]
}]
};
I have a modestly large graph containing 100K links. What is a good way of doing this? I am thinking there is a very elegant recursive way of doing this but am not sure about how to create the JSON string directly.
Something like should work:
static void Main(string[] args)
{
var adjList = new List<Link>
{
new Link("A","A1"),
new Link("A","A2"),
new Link("A","A3"),
new Link("A3","A31"),
new Link("A31","A311"),
new Link("A31","A312"),
};
var rootsAndChildren = adjList.GroupBy(x => x.From)
.ToDictionary(x => x.Key, x => x.Select(y => y.To).ToList());
var roots = rootsAndChildren.Keys
.Except(rootsAndChildren.SelectMany(x => x.Value));
using (var wr = new StreamWriter("C:\\myjson.json"))
{
wr.WriteLine("{");
foreach (var root in roots)
AppendSubNodes(wr, root, rootsAndChildren, 1);
wr.WriteLine("};");
}
}
static void AppendSubNodes(TextWriter wr, string root,
Dictionary<string, List<string>> rootsAndChildren, int level)
{
string indent = string.Concat(Enumerable.Repeat(" ", level * 4));
wr.Write(indent + "\"name\" : \"" + root + "\"");
List<string> children;
if (rootsAndChildren.TryGetValue(root, out children))
{
wr.WriteLine(",");
wr.WriteLine(indent + "\"children\" : [{");
for (int i = 0; i < children.Count; i++)
{
if (i > 0)
wr.WriteLine(indent + "}, {");
AppendSubNodes(wr, children[i], rootsAndChildren, level + 1);
}
wr.WriteLine(indent + "}]");
}
else
{
wr.WriteLine();
}
}
With Link being the following class:
class Link
{
public string From { get; private set; }
public string To { get; private set; }
public Link(string from, string to)
{
this.From = from;
this.To = to;
}
}
Result of the previous code:
{
"name" : "A",
"children" : [{
"name" : "A1"
}, {
"name" : "A2"
}, {
"name" : "A3",
"children" : [{
"name" : "A31",
"children" : [{
"name" : "A311"
}, {
"name" : "A312"
}]
}]
}]
};
EDIT :
If you want to check the existence of graph cycles you can do the following (just after the creation of rootsAndChildren dictionary)
var allNodes = rootsAndChildren.Keys.Concat(rootsAndChildren.SelectMany(x => x.Value)).Distinct();
Func<string, IEnumerable<string>> getSuccessors =
(x) => rootsAndChildren.ContainsKey(x) ? rootsAndChildren[x] : Enumerable.Empty<string>();
var hasCycles = new Tarjan<string>().HasCycle(allNodes, getSuccessors);
With Tarjan being the following class:
// Please note that Tarjan does not detect a cycle due to a node
// pointing to itself. It's pretty trivial to account for that though...
public class Tarjan<T>
{
private class Node
{
public T Value { get; private set; }
public int Index { get; set; }
public int LowLink { get; set; }
public Node(T value)
{
this.Value = value;
this.Index = -1;
this.LowLink = -1;
}
}
private Func<T, IEnumerable<T>> getSuccessors;
private Dictionary<T, Node> nodeMaps;
private int index = 0;
private Stack<Node> stack;
private List<List<Node>> SCC;
public bool HasCycle(IEnumerable<T> nodes, Func<T, IEnumerable<T>> getSuccessors)
{
return ExecuteTarjan(nodes, getSuccessors).Any(x => x.Count > 1);
}
private List<List<Node>> ExecuteTarjan(IEnumerable<T> nodes, Func<T, IEnumerable<T>> getSuccessors)
{
this.nodeMaps = nodes.ToDictionary(x => x, x => new Node(x));
this.getSuccessors = getSuccessors;
SCC = new List<List<Node>>();
stack = new Stack<Node>();
index = 0;
foreach (var node in this.nodeMaps.Values)
{
if (node.Index == -1)
TarjanImpl(node);
}
return SCC;
}
private IEnumerable<Node> GetSuccessors(Node v)
{
return this.getSuccessors(v.Value).Select(x => this.nodeMaps[x]);
}
private List<List<Node>> TarjanImpl(Node v)
{
v.Index = index;
v.LowLink = index;
index++;
stack.Push(v);
foreach (var n in GetSuccessors(v))
{
if (n.Index == -1)
{
TarjanImpl(n);
v.LowLink = Math.Min(v.LowLink, n.LowLink);
}
else if (stack.Contains(n))
{
v.LowLink = Math.Min(v.LowLink, n.Index);
}
}
if (v.LowLink == v.Index)
{
Node n;
List<Node> component = new List<Node>();
do
{
n = stack.Pop();
component.Add(n);
} while (n != v);
SCC.Add(component);
}
return SCC;
}
}