Make a method generic - c#

I have a method that extract days names from a particular object,
like this:
private string ExtractWeekDayNames(FiscalReceipt fiscalReceipt)
{
string retVal = "";
Dictionary<string, bool> WeekDays =
new Dictionary<string, bool>() {
{ "Sun", fiscalReceipt.Sunday },
{ "Mon", fiscalReceipt.Monday },
{ "Tue", fiscalReceipt.Tuesday },
{ "Wed", fiscalReceipt.Wednesday },
{ "Thu", fiscalReceipt.Thursday },
{ "Fri", fiscalReceipt.Friday },
{ "Sat", fiscalReceipt.Saturday }
};
//Find week days
foreach (var item in WeekDays)
{
if (item.Value == true)
retVal += item.Key + ",";
}
if (!string.IsNullOrEmpty(retVal))
retVal = retVal.Substring(0, retVal.Length - 1);
return retVal;
}
I also have a similar method that performs the same operations
but having a different type of parameter, like:
private string ExtractWeekDayNames(NonFiscalReceipt nonFiscalReceipt)
{
...
}
Also the NonFiscalReceipt has the properties Sunday, Monday, etc.
How can I substitute these 2 methods with just one?

public class FiscalReceipt : Receipt{
FiscalReceipt specific fields
}
public class NonFiscalReceipt : Receipt{
NonFiscalReceipt specific fields..
}
public class Receipt{
fields common to both classes
}
private string ExtractWeekDayNames(Receipt receipt){
}
Both types of receipt inherit from receipt, that was you can pass in either and will still have all the fields :)

You need to create a common interface for your classes to implement, so that the method could accept anything that implements this interface.
interface IReceipt
{
bool Sunday {get; }
bool Monday {get; }
...
bool Saturday {get; }
}
Then your method should look like this:
private string ExtractWeekDayNames<T>(T receipt) where T : IReceipt
{
string retVal = "";
Dictionary<string, bool> WeekDays =
new Dictionary<string, bool>() {
{ "Sun", receipt.Sunday },
{ "Mon", fiscalReceipt.Monday },
{ "Tue", receipt.Tuesday },
{ "Wed", receipt.Wednesday },
{ "Thu", receipt.Thursday },
{ "Fri", receipt.Friday },
{ "Sat", receipt.Saturday }
};
//Find week days
foreach (var item in WeekDays)
{
if (item.Value == true)
retVal += item.Key + ",";
}
if (!string.IsNullOrEmpty(retVal))
retVal = retVal.Substring(0, retVal.Length - 1);
return retVal;
}
However, as juharr and Remy Grandin rightfully wrote in the comments - There is no need for generics in this case - you can simply pass the interface to the method:
private string ExtractWeekDayNames(IReceipt receipt)
{
string retVal = "";
Dictionary<string, bool> WeekDays =
new Dictionary<string, bool>() {
{ "Sun", receipt.Sunday },
{ "Mon", receipt.Monday },
{ "Tue", receipt.Tuesday },
{ "Wed", receipt.Wednesday },
{ "Thu", receipt.Thursday },
{ "Fri", receipt.Friday },
{ "Sat", receipt.Saturday }
};
//Find week days
foreach (var item in WeekDays)
{
if (item.Value == true)
retVal += item.Key + ",";
}
if (!string.IsNullOrEmpty(retVal))
retVal = retVal.Substring(0, retVal.Length - 1);
return retVal;
}

Create base interface for NonFiscalReceipt and FiscalReceipt that contains the common properties:
public interface IReceipt
{
bool Sunday{get;}
......
}
Replace the method signature with this: private string ExtractWeekDayNames(IReceipt fiscalReceipt)
Try to use DayOfWeek enum if it possible.

Related

Searching through an array of an array that has a key value object

I am consuming an API that returns JSON that looks like this:
{
"lookup_table_data": [
[
{
"key": "id",
"value": 0
},
{
"key" : "label",
"value" : "something"
}
],
[
{
"key": "id",
"value": 1
},
{
"key" : "label",
"value" : "something_else"
}
]
]
}
I made a class that I deserialize the json object into that looks like this:
public class LookupResponseModel
{
public Lookup_Table_Data[][] Lookup_table_data { get; set; }
public class Lookup_Table_Data
{
public string Key { get; set; }
public object Value { get; set; }
}
}
Now imagine that the JSON response has over 1,000 records instead of the 2 that I gave in my example.
I am wanting to search through my model and be able to find where the value of the key "id" is equal to 1 - because I want to use the label key value of "something_else".
How would I be able to grab the label "something_else" with an id of 1 with this model?
var lookup = model.lookupTableData.Select(data => new { key = (long)data.First(kvp => kvp.Key == "id").Value, value = (string)data.First(kvp => kvp.Key == "label").Value).ToDictionary(kvp => kvp.key, kvp => kvp.value)
var displayText = lookup[1]; // "something_else"
My attempt from a phone, might not be 100% correct syntax.
I would suggest an approach like this:
public class LookupResponseModel
{
public Lookup_Table_Data[][] Lookup_table_data { get; set; }
public class Lookup_Table_Data
{
public string Key { get; set; }
public object Value { get; set; }
}
}
// This will help compare the values and convert if necessary
// This part was missing from my original answer and made it not work
bool ObjectCompare(object a, object b)
{
if (object.Equals(a, b))
{
return true;
}
else
{
var altB = Convert.ChangeType(b, Type.GetTypeCode(a.GetType()));
return object.Equals(a, altB);
}
}
// This will break the result up into an Array of Dictionaries
// that are easier to work with
Dictionary<string, object>[] MakeTables(LookupResponseModel lrm)
{
return lrm.Lookup_table_data.Select( entry => entry.ToDictionary( e => e.Key, e => e.Value ) ).ToArray();
}
// This will help you find the dictionary that has the values you want
Dictionary<string, object> FindTable( Dictionary<string, object>[] tables, string key, object value )
{
return tables.Where( dict => dict.TryGetValue(key, out object val) && ObjectCompare(value, val) ).FirstOrDefault();
}
// Here is how you might use them together
string GetLabel()
{
var lrm = JsonConvert.DeserializeObject<LookupResponseModel>(json);
var lookup = MakeTables(lrm);
var table = FindTable( lookup, "id", 1 );
return table["label"].ToString(); // Returns "something_else"
}
Data :
LookupResponseModel model = new LookupResponseModel();
model.Lookup_table_data = new LookupResponseModel.Lookup_Table_Data[][]
{
new LookupResponseModel.Lookup_Table_Data[]
{
new LookupResponseModel.Lookup_Table_Data(){Key = "id", Value = "0"},
new LookupResponseModel.Lookup_Table_Data(){Key = "label", Value = "hello"},
new LookupResponseModel.Lookup_Table_Data(){Key = "textbox", Value = "bye"}
},
new LookupResponseModel.Lookup_Table_Data[]
{
new LookupResponseModel.Lookup_Table_Data(){Key = "id", Value = "1"},
new LookupResponseModel.Lookup_Table_Data(){Key = "label", Value = "banana"},
new LookupResponseModel.Lookup_Table_Data(){Key = "textbox", Value = "potatoe"}
},
new LookupResponseModel.Lookup_Table_Data[]
{
new LookupResponseModel.Lookup_Table_Data(){Key = "id", Value = "2"},
new LookupResponseModel.Lookup_Table_Data(){Key = "label", Value = "cat"},
new LookupResponseModel.Lookup_Table_Data(){Key = "textbox", Value = "bydog"}
}
};
This query gives us second set (all 3 Lookup_Table_Data where key is ID and value is 1) It still [][] and it can contain more than one result :
var _result = model.Lookup_table_data.Where(x => x.Any(y => y.Key == "id" && y.Value.Equals("1")));
And this one gives you exactly value of "label" key from previous set (Key = "label", Value = "banana") :
var _exactlyLabel = _result.Select(x => x.Where(y => y.Key == "label"));
Or you can make _result.SelectMany(... for [] not for [][]
The simple answer is, you say something like this
public class LookupResponseModel
{
public LookupTableData[][] lookupTableData { get; set; }
public class LookupTableData
{
public string Key { get; set; }
public object Value { get; set; }
}
public LookupTableData[] FindById( int id )
{
if (this.lookupTableData == null) throw new InvalidOperationException();
foreach ( LookupTableData[] entry in lookupTableData )
{
if (entry != null)
{
foreach( LookupTableData item in entry )
{
bool isMatch = "id".Equals( item.Key ?? "", StringComparison.Ordinal )
&& item.Value is int
&& ((int)item.Value) == id
;
if ( isMatch )
{
return entry;
}
}
}
}
return null;
}
}

Using JSON.Net / C# to read root value in json data

Sorry for the nube-like question, but having been retired for sometime I find myself forgetting some things.
Given this sample json string:
{
"range": [
{ "num": 0 },
{ "num": 1 },
{ "num": 2 },
{ "num": 3 },
{ "num": 4 },
{ "num": 5 },
{ "num": 6 },
{ "num": 7 },
{ "num": 8 },
{ "num": 9 }
],
"friends": [
{
"id": 0,
"name": "Christian Cruz"
},
{
"id": 1,
"name": "Hunter Moon"
},
{
"id": 2,
"name": "Holden Gentry"
}
]
}
I would like to be able to read the root value ("range" and "friends" in this case) for each line in the data, then parse the remaining values.
void Main()
{
var json = File.ReadAllText(#"c:\data\sample.json");
JObject obj = JObject.Parse(json);
foreach(JProperty child in obj.Children())
{
}
}
Where I bogged down is as I iterate through the children collection (foreach(JProperty child ...) I can read the items in the array (e.g. "num", "id" and "name") but I am unable to read the root values (e.g. "range" and "friends")
Any help you could lend an old man would be very much appreciated.
It's much easier to deserialise it into C# objects, for example:
public class RootObject
{
public List<Range> range { get; set; }
public List<Friend> friends { get; set; }
}
public class Range
{
public int num { get; set; }
}
public class Friend
{
public int id { get; set; }
public string name { get; set; }
}
Now you can use deserialise like this:
var root = JsonConvert.DeserializeObject<RootObject>(json);
And use the data:
foreach (var range in root.Range)
{
//Do stuff
}
foreach (var friend in root.Friends)
{
//Do stuff
}
You can use SelectTokens , put the json in list and then iterate through JProperty.
var files = JObject.Parse(YourJson);
var recList = files.SelectTokens("$").ToList();
foreach (JProperty item in recList.Children())
{
var key = item.Name.ToString(); //store the root item here
var value = item.Value.ToString();
//Do your stuffs
}
Using this small recursive function below you can unpack JSON to see properties as well as values without making any classes
static void Main(string[] args)
{
var json = "{\"range\":[{\"num\":0},{\"num\":1},{\"num\":2},{\"num\":3},{\"num\":4},{\"num\":5},{\"num\":6},{\"num\":7},{\"num\":8},{\"num\":9}],\"friends\":[{\"id\":0,\"name\":\"Christian Cruz\"},{\"id\":1,\"name\":\"Hunter Moon\"},{\"id\":2,\"name\":\"Holden Gentry\"}]}";
JObject obj = JObject.Parse(json);
void UnpackJson(JToken jobj, int indent)
{
if (jobj == null)
return;
var name = (jobj as JProperty)?.Name;
if (name != null)
{
Console.Write(new string(' ', indent) + name + " :\n");
indent += 4;
}
foreach (var child in jobj.Children())
{
var chname = (child as JProperty)?.Name;
if (chname != null)
Console.Write(new string(' ', indent) + chname + " : ");
var value = (child as JProperty)?.Value;
if (child.Values().Count() > 1)
{
if (chname != null || name != null)
Console.WriteLine();
IEnumerable<JToken> jt = (value is JArray) ? child.Values() : child.Children();
foreach (var val in jt)
UnpackJson(val, indent + 4);
}
else
{
if (value != null)
Console.WriteLine(value);
}
}
}
UnpackJson(obj, 0);
Console.Read();
}
Output:
range :
num : 0
num : 1
num : 2
num : 3
num : 4
num : 5
num : 6
num : 7
num : 8
num : 9
friends :
id : 0
name : Christian Cruz
id : 1
name : Hunter Moon
id : 2
name : Holden Gentry

Using MongoDB C# driver find and update a node from parent children hierarchy

I have a hierarchical category document, like parent - Children - Children and so on....
{
id: 1,
value: {
}Children: [{
id: 2,
value: {
}Children: [{
id: 3,
value: {
}Children: [{
id: 4,
value: {
}Children: [{
id: 5,
value: {
}Children: [{
id: 6,
value: {
}Children: [{
id: 7,
value: {
}Children: []
}]
}]
}]
}]
}]
}]
}
In such documents, using MongoDB C# driver, how can I find a node where Id = x
I tried something like this
var filter = Builders<Type>.Filter.Eq(x => x.Id, 3);
var node = mongoDbRepository.GetOne(filter) ??
mongoDbRepository.GetOne(Builders<Type>.Filter.Where(x => x.Children.Any(c=>c.Id == 3)));
But this covers only two levels. In my example, I have 7 levels and I don't have a restriction on depth of level
Once I find that node I need to update that node.
MongoDB Documentation talks about hierarchical documents, but doesn't cover my scenario.
In your situation if you
don't have a restriction on depth of level
you can`t create update query. You must change schema for store data:
https://docs.mongodb.com/v3.2/tutorial/model-tree-structures/
If you depth is fixed:
public class TestEntity
{
public int Id { get; set; }
public TestEntity[] Value { get; set; }
}
class Program
{
static void Main()
{
const string connectionString = "mongodb://localhost:27017";
var client = new MongoClient(connectionString);
var db = client.GetDatabase("TestEntities");
var collection = db.GetCollection<TestEntity>("Entities");
collection.InsertOne(CreateTestEntity(1, CreateTestEntity(2, CreateTestEntity(3, CreateTestEntity(4)))));
const int selctedId = 3;
var update = Builders<TestEntity>.Update.AddToSet(x => x.Value, CreateTestEntity(9));
var depth1 = Builders<TestEntity>.Filter.Eq(x => x.Id, selctedId);
var depth2 = Builders<TestEntity>.Filter.Where(x => x.Value.Any(item => item.Id == selctedId));
var depth3 = Builders<TestEntity>.Filter.Where(x => x.Value.Any(item => item.Value.Any(item2 => item2.Id == selctedId)));
var filter = depth1 | depth2 | depth3;
collection.UpdateMany(filter, update);
// if you need update document on same depth that you match it in query (for example 3 as selctedId),
// you must make 2 query (bad approach, better way is change schema):
//var testEntity = collection.FindSync(filter).First();
//testEntity.Value[0].Value[0].Value = new[] {CreateTestEntity(9)}; //todo you must calculate depth what you need in C#
//collection.ReplaceOne(filter, testEntity);
}
private static TestEntity CreateTestEntity(int id, params TestEntity[] values)
{
return new TestEntity { Id = id, Value = values };
}
}
There seems to be something wrong in your example document. If the parent has 3 fileds: _id ,id and value, the example should be
{
"_id" : ObjectId("581bce9064989cce81f2b0c1"),
"id" : 1,
"value" : {
"Children" : [
{
"id" : 2,
"value" : {
"Children" : [
{
"id" : 3,
"value" : {
"Children" : [
{
"id" : 4,
"value" : {
"Children" : [
{
"id" : 5,
"value" : {
"Children" : [
{
"id" : 6,
"value" : {
"Children" : [
{
"id" : 7,
"value" : {
"Children" : []
}
}
]
}
}
]
}
}
]
}
}
]
}
}
]
}
}
]
}
}
Please try the following function, where target is the document and x is the id number you want to update.
bool FindAndUpdate2(BsonDocument target, int x)
{
BsonValue id, children;
while (true)
{
try
{
if (target.TryGetValue("_id", out children))
{
id = target.GetValue(1);
children = target.GetValue(3);
}
else
{
id = target.GetValue(0);
children = target.GetValue(2);
}
if (id.ToInt32() == x)
{
Update(target); //change as you like
return true; //success
}
else
target = children[0] as BsonDocument;
}
catch (Exception ex)
{
return false; //failed
}
}
}
else if the parent has 4 fileds: _id ,id, value and children the example should be
{
"_id" : ObjectId("581bdd3764989cce81f2b0c2"),
"id" : 1,
"value" : {},
"Children" : [
{
"id" : 2,
"value" : {},
"Children" : [
{
"id" : 3,
"value" : {},
"Children" : [
{
"id" : 4,
"value" : {},
"Children" : [
{
"id" : 5,
"value" : {},
"Children" : [
{
"id" : 6,
"value" : {},
"Children" : [
{
"id" : 7,
"value" : {},
"Children" : []
}
]
}
]
}
]
}
]
}
]
}
]
}
Then you can try this:
bool FindAndUpdate2(BsonDocument target, int x)
{
BsonValue id, children;
while (true)
{
try
{
if (target.TryGetValue("_id", out children))
{
id = target.GetValue(1);
children = target.GetValue(3);
}
else
{
id = target.GetValue(0);
children = target.GetValue(2);
}
if (id.ToInt32() == x)
{
Update(target); //change as you like
return true; //success
}
else
target = children[0] as BsonDocument;
}
catch (Exception ex)
{
return false; //failed
}
}
}
I have a version, which is based on #DmitryZyr 's answer, and uses 2 answer of the question How do I create an expression tree calling IEnumerable<TSource>.Any(...)?. Thanks to Aaron Heusser and Barry Kelly:
class Program
{
#region Copied from Expression.Call question
static MethodBase GetGenericMethod(Type type, string name, Type[] typeArgs, Type[] argTypes, BindingFlags flags)
{
int typeArity = typeArgs.Length;
var methods = type.GetMethods()
.Where(m => m.Name == name)
.Where(m => m.GetGenericArguments().Length == typeArity)
.Select(m => m.MakeGenericMethod(typeArgs));
return Type.DefaultBinder.SelectMethod(flags, methods.ToArray(), argTypes, null);
}
static bool IsIEnumerable(Type type)
{
return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IEnumerable<>);
}
static Type GetIEnumerableImpl(Type type)
{
// Get IEnumerable implementation. Either type is IEnumerable<T> for some T,
// or it implements IEnumerable<T> for some T. We need to find the interface.
if (IsIEnumerable(type))
return type;
Type[] t = type.FindInterfaces((m, o) => IsIEnumerable(m), null);
Debug.Assert(t.Length == 1);
return t[0];
}
static Expression CallAny(Expression collection, Expression predicateExpression)
{
Type cType = GetIEnumerableImpl(collection.Type);
collection = Expression.Convert(collection, cType); // (see "NOTE" below)
Type elemType = cType.GetGenericArguments()[0];
Type predType = typeof(Func<,>).MakeGenericType(elemType, typeof(bool));
// Enumerable.Any<T>(IEnumerable<T>, Func<T,bool>)
MethodInfo anyMethod = (MethodInfo)
GetGenericMethod(typeof(Enumerable), "Any", new[] { elemType },
new[] { cType, predType }, BindingFlags.Static);
return Expression.Call(anyMethod, collection, predicateExpression);
}
#endregion
public class TestEntity
{
public int Id { get; set; }
public TestEntity[] Value { get; set; }
}
private static TestEntity CreateTestEntity(int id, params TestEntity[] values)
{
return new TestEntity { Id = id, Value = values };
}
static void Main(string[] args)
{
const string connectionString = "mongodb://localhost:27017";
var client = new MongoClient(connectionString);
var db = client.GetDatabase("TestEntities");
IMongoCollection<TestEntity> collection = db.GetCollection<TestEntity>("Entities");
collection.InsertOne(CreateTestEntity(1, CreateTestEntity(2, CreateTestEntity(3, CreateTestEntity(4)))));
const int selectedId = 4;
int searchDepth = 6;
// builds the expression tree of expanding x => x.Value.Any(...)
var filter = GetFilterForDepth(selectedId, searchDepth);
var testEntity = collection.Find(filter).FirstOrDefault();
if (testEntity != null)
{
UpdateItem(testEntity, selectedId);
collection.ReplaceOne(filter, testEntity);
}
}
private static bool UpdateItem(TestEntity testEntity, int selectedId)
{
if (testEntity.Id == selectedId)
{
return true;
}
if (UpdateItem(testEntity.Value[0], selectedId))
testEntity.Value[0] = CreateTestEntity(11);
return false;
}
private static FilterDefinition<TestEntity> GetFilterForDepth(int id, int depth)
{
// item
var idEqualsParam = Expression.Parameter(typeof(TestEntity), "item");
// .Id
var idProp = Expression.Property(idEqualsParam, "Id");
// item.Id == id
var idEquals = Expression.Equal(idProp, Expression.Constant(id));
// item => item.Id == id
var idEqualsLambda = Expression.Lambda<Func<TestEntity, bool>>(idEquals, idEqualsParam);
// x
var anyParam = Expression.Parameter(typeof(TestEntity), "x");
// .Value
var valueProp = Expression.Property(anyParam, "Value");
// Expression.Call would not find easily the appropriate .Any((TestEntity x) => x == id)
// .Value.Any(item => item.Id == id)
var callAny = CallAny(valueProp, idEqualsLambda);
// x => x.Value.Any(item => item.Id == id)
var firstAny = Expression.Lambda<Func<TestEntity, bool>>(callAny, anyParam);
return NestedFilter(Builders<TestEntity>.Filter.Eq(x => x.Id, id), firstAny, depth);
}
static int paramIndex = 0;
private static FilterDefinition<TestEntity> NestedFilter(FilterDefinition<TestEntity> actual, Expression<Func<TestEntity, bool>> whereExpression, int depth)
{
if (depth == 0)
{
return actual;
}
// paramX
var param = Expression.Parameter(typeof(TestEntity), "param" + paramIndex++);
// paramX.Value
var valueProp = Expression.Property(param, "Value");
// paramX => paramX.Value.Any(...)
var callLambda = Expression.Lambda<Func<TestEntity, bool>>(CallAny(valueProp, whereExpression), param);
return NestedFilter(Builders<TestEntity>.Filter.Where(whereExpression), callLambda, depth - 1) | actual;
}
}
It's still fixed length depth search, but the depth can be varied dinamically. Just 1 step to upgrade the code to try 1st level, 2nd level, .... And the depth is infinite

loop over forms collection and add data to list

I have the following code, which I'm using to get the values from a forms collection
List<FlowSettings> lst = new List<FlowSettings>();
string[] IsVisible = fc["IsVisible"].Split(',');
string[] Editable = fc["Editable"].Split(',');
string[] Revisable = fc["Revisable"].Split(',');
string[] tbl = fc["tblId"].Split(',');
The above arrays are just for me to ensure that I'm getting the data as expected. My problem is, I can loop over the forms collection, but cannot get the values out and add to my list.
foreach (var _key in fc.Keys)
{
var _value = fc[_key.ToString()];
//lst.Add(new FlowSettings { TblId = Convert.ToInt32(_value[0]), ChxIsVisible =
Convert.ToBoolean(_value[1]),
ChxEditable = true,
ChxRevisable = true
});
}
The values in IsVisible etc have 10 rows which are bool and tbl is an int
Can anyone let me know what I'm missing
--------------Extra code-------------------
public ActionResult FlowItems(FormCollection fc)
lst is in the foreach loop
FormCollection does not implement the IDictionary(TKey,TValue) interface, so you need to loop through and get the values.
Data
public class FlowSettings
{
public bool IsVisible { get; set; }
public bool Editable { get; set; }
public bool Revisable { get; set; }
public int TblId { get; set; }
}
private bool ParseBool(string value)
{
return Convert.ToBoolean(EmptyToFalse(value));
}
private int ParseInt(string value)
{
return Convert.ToInt32(EmptyToInvalid(value));
}
private string EmptyToFalse(string value)
{
return string.IsNullOrWhiteSpace(value) ? bool.FalseString : value;
}
private string EmptyToInvalid(string value)
{
return string.IsNullOrWhiteSpace(value) ? "-1" : value;
}
Create
var col1 = new NameValueCollection
{
{ "IsVisible", "True" },
{ "Editable", "True" },
{ "Revisable", "True" },
{ "tblId", "100" },
};
var col2 = new NameValueCollection
{
{ "IsVisible", "True" },
{ "Editable", "" },
{ "Revisable", "True" },
{ "tblId", "101" },
};
var formCollection = new FormCollection
{
col1,
col2
};
var length =
formCollection
.Cast<string>()
.Select(entry => formCollection.GetValues(entry).Length)
.Max();
Loop
var items = new List<FlowSettings>();
for(var i = 0; i < length; i++)
{
var flowSettings = new FlowSettings
{
IsVisible = ParseBool(formCollection.GetValues("IsVisible")[i]),
Editable = ParseBool(formCollection.GetValues("Editable")[i]),
Revisable = ParseBool(formCollection.GetValues("Revisable")[i]),
TblId = ParseInt(formCollection.GetValues("tblId")[i]),
};
items.Add(flowSettings);
}
There is a caveat to this approach. If there is data missing from col1 or col2. e.g.
var col3 = new NameValueCollection
{
{ "IsVisible", "True" },
{ "Editable", "" },
// { "Revisable", "True" }, Missing this entry
{ "tblId", "102" },
};
Then the loop with be out of bounds.
The problem might be that the collection contains values that are comma-delimited (I'm assuming that because of the Split() at the beginning your question) but in the for loop you are using the value directly without splitting on commas. So I guess it would be trying to create a bool out of the 2nd character (index of 1) of the value.
Try this instead:
foreach (var _key in fc.Keys)
{
var _value = fc[_key.ToString()];
string[] tokenized = _value.Split(',');
bool b = Convert.ToBoolean(tokenized[1]);
}

JSON from Adjacency list?

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;
}
}

Categories

Resources