Convert Nested for loops into Linq - c#

Can any body convert the following nested forloop into linq query.
I have the following class definition.
public class MessageCodes
{
public string IdentityKey{ get; set; }
public Dictionary<string, string> LangCollection { get; set; }
public MessageCodes()
{
LangCollection = new Dictionary<string, string>();
}
}
IdentityKey in above class is unique, LangCollection can have multiple key, values.
Say
MessageCodes newCode = new MessageCodes()
{
MessageCode = "10",
LangCollection = new Dictionary<string, string>()
{
{"en","Hello"},
{"de","dello"},
{"fr","fello"},
}
};
MessageCodes oldCode = new MessageCodes()
{
MessageCode = "10",
LangCollection = new Dictionary<string, string>()
{
{"en","fello"},
{"de","pello"},
{"fr","fello"},
}
};
Above is an example of MessageCode Instance.
Now i have collection of NewMessageCodes and OldMessageCodes. I need to compare them
Following is the problem nested forloop,
foreach (MessageCodes newMessageCode in newDiffMsgCodeCollection)
{
foreach (MessageCodes oldMessageCode in oldDiffMsgCodeCollection)
{
if (newMessageCode.MessageCode == oldMessageCode.MessageCode)
{
MessageCodes msgCodes = new MessageCodes();
msgCodes.MessageCode = newMessageCode.MessageCode;
Dictionary<string, string> langCollection = new Dictionary<string, string>();
foreach (KeyValuePair<string, string> newLangNode in newMessageCode.LangCollection)
{
foreach (KeyValuePair<string, string> oldLangNode in oldMessageCode.LangCollection)
{
string newCode = newLangNode.Key.Trim().ToLower();
string oldCode = oldLangNode.Key.Trim().ToLower();
if (newCode == oldCode )
{
if (newLangNode.Value != oldLangNode.Value)
{
langCollection.Add(newCode, oldCode);
}
}
}
}
msgCodes.LangCollection = langCollection;
}
}
}
Convert the above code mess into a Linq query or Lambda expressions.

newDiffMsgCodeCollection.ForEach(pd => {
oldDiffMsgCodeCollection.ForEach(pds =>
{
if (pd.MessageCode == pds.MessageCode)
{
//business logic
}
});
});

I think this is pretty much what you want:
var messageCodesQuery =
from newMessageCode in newDiffMsgCodeCollection
from oldMessageCode in oldDiffMsgCodeCollection
where newMessageCode.MessageCode == oldMessageCode.MessageCode
select new MessageCodes()
{
MessageCode = newMessageCode.MessageCode,
LangCollection = (
from newLangNode in newMessageCode.LangCollection
from oldLangNode in oldMessageCode.LangCollection
let newCode = newLangNode.Key.Trim().ToLower()
let oldCode = oldLangNode.Key.Trim().ToLower()
where newCode == oldCode
where newLangNode.Value != oldLangNode.Value
select new { newCode, oldCode }
).ToDictionary(x => x.newCode, x => x.oldCode)
};
You just now need to make sure you do something with the results.

Related

Deserialise JSON to C# array, where index is in the property name

Could anyone provide their approach for deserializing the following JSON
{
"i": 5
"b0": "ABC",
"b1": "DEF",
"b2": "GHI",
"s0": "SABC",
"s1": "SDEF",
"s2": "SGHI",
}
into a class in C# to provide the same structure as this
class Example {
public int Index {get;set;}
public string[] B {get;set;}
public string[] S {get;set;}
}
var b = new [] {"ABC", "DEF", "GHI"}
var s = new [] {"SABC", "SDEF", "SGHI"}
I generally use ServiceStack.Text, but Json.Net approach or even a BsonDocument from the MongoDb provider is fine.
Could use a JToken and use .Values() then .ToArray():
var json = "{\r\n \"b0\": \"ABC\",\r\n \"b1\": \"DEF\",\r\n \"b2\": \"GHI\",\r\n}";
var token = JToken.Parse(json);
var b = token.Values().ToArray();
This one solution might be useful too:
public class Example
{
public int Index { get; set; }
public string[] B { get; set; }
public string[] S { get; set; }
}
var strData = #"{'i': 5, 'b0': 'ABC','b1': 'DEF', 'b2': 'GHI', 's0': 'SABC', 's1': 'SDEF', 's2': 'SGHI',}";
var data = JsonConvert.DeserializeObject<JToken>(strData).Values().ToList();
Example result = new Example();
result.Index = data.Values().FirstOrDefault(x => x.Path == "i").Value<int>();
result.B = data.Values().Where(x => x.Path.StartsWith("b")).Select(x => x.Value<string>()).ToArray();
result.S = data.Values().Where(x => x.Path.StartsWith("s")).Select(x => x.Value<string>()).ToArray();
there is solution for dynamic name of arrays, but string array is not the best type in this case:
public class Example
{
public int Index { get; set; }
public Dictionary<string, string[]> Data { set; get; }
}
var s = #"{'i': 5, 'b0': 'ABC','b1': 'DEF', 'b2': 'GHI', 's0': 'SABC', 's1': 'SDEF', 's2': 'SGHI',}";
var data = JsonConvert.DeserializeObject<JToken>(s).Values().ToList();
Example result = new Example();
result.Index = data.Values().FirstOrDefault(x => x.Path == "i").Value<int>();
result.Data = new Dictionary<string, string[]>();
var stringData = data.Values().Where(x => x.Path != "i").ToList();
stringData.ForEach(x =>
{
var key = x.Path[0].ToString();
if (!result.Data.ContainsKey(key))
{
result.Data.Add(key, new string[0]);
}
var currentValue = result.Data[key].ToList();
currentValue.Add(x.Value<string>());
result.Data[key] = currentValue.ToArray();
});

Convert List<List<List<T>>> into List<List<T>> based on key

I have a this complex 3D structure of List<List<List<myClass>>> and I want it to reduced to List<List<myClass>>.
public class MyClass
{
public string Key { get; set; }
public decimal Price { get; set; }
}
The problem is that the MyClass is been duplicated in the origin 3D lists with the same key like:
(I'm having trouble to write 3D and 2D multi-dimensional array so I just hard coded an example.)
List<List<List<MyClass>>> level1 = new List<List<List<MyClass>>>();
List<List<MyClass>> level2_1 = new List<List<MyClass>>();
List<MyClass> level3_1 = new List<MyClass>()
{
new MyClass() { Key = "key1", Price = 10 }
new MyClass() { Key = "key2", Price = 20 }
};
level2_1.Add(level3_1);
List<List<MyClass>> level2_2 = new List<List<MyClass>>();
List<MyClass> level3_2 = new List<MyClass>()
{
new MyClass() { Key = "key2", Price = 10 }
new MyClass() { Key = "key3", Price = 20 }
};
level2_2.Add(level3_2);
I need the converted list will be like:
List<List<MyClass>> level1 = new List<List<MyClass>>();
List<MyClass> level2_1 = new List<MyClass>()
{
new MyClass() { Key = "key1", Price = 10 }
}
List<MyClass> level2_2 = new List<MyClass>()
{
new MyClass() { Key = "key2", Price = 10 },
new MyClass() { Key = "key2", Price = 20 }
}
List<MyClass> level2_3 = new List<MyClass>()
{
new MyClass() { Key = "key3", Price = 20 }
}
level1.Add(level2_1);
level1.Add(level2_2);
level1.Add(level2_3);
So the main list is distinct by the Key and the child list to be duplicated by its Prices.
Note that Iv'e looked through these question: 1, 2, 3,
Any other elegant way achieving this? maybe linq?
Try select many
public static List<List<MyClass>> MyConvertLinq(List<List<List<MyClass>>> items)
{
var allItems = items.SelectMany(m => m).ToList();
return allItems;
}
--- Edit ---
you can use a GroupBy to build Groups
public static List<List<MyClass>> MyConvertLinq(List<List<List<MyClass>>> items)
{
var allItems = items.SelectMany(m => m).ToList().SelectMany(m => m).ToList();
var sortedItems = allItems.GroupBy(m => m.Key, m => m,
(k, classes) => classes.ToList()).ToList();
return sortedItems;
}
The best solution I was able to think of:
public List<List<MyClass>> MyConvert(List<List<List<MyClass>>> items)
{
Dictionary<string, List<MyClass>> resultsDic = new Dictionary<string, List<MyClass>>();
foreach (List<List<MyClass>> item in items)
{
foreach (List<MyClass> innerItem in item)
{
foreach (MyClass myClass in innerItem)
{
if (!resultsDic.ContainsKey(myClass.Key))
{
resultsDic.Add(myClass.Key, innerItem);
}
}
}
}
List<List<MyClass>> convertedResults = resultsDic.Select(x => x.Value).ToList();
return convertedResults;
}

How to use attribute to map properties

I have the following code.
public class SyncProperty : Attribute
{
public readonly string PropertyName;
public SyncProperty(string propertyName)
{
this.PropertyName = propertyName;
}
}
public class SyncContact
{
[SyncProperty("first_name")]
public string FirstName { get; set; }
[SyncProperty("last_name")]
public string LastName { get; set; }
[SyncProperty("phone")]
public string Phone { get; set; }
[SyncProperty("email")]
public string Email { get; set; }
}
I need to create an instance of my SyncContact such as
var contact = new SyncContact { FirstName="Test", LastName="Person", Phone="123-123-1234", Email="test#test.com"};
And then with that object I need to create a NameValueCollection where the object's property uses the SyncProperty's PropertyName as the Key in the collection. Then use that to make a post request to an API. So in this case I would end up with a collection like...
collection["first_name"] = "Test"
collection["last_name"] = "Person"
collection["phone"] = "123-123-1234"
collection["email"] = "test#test.com"
How can I do that?
If you want to get some type metadata, you should use Reflection. To read attribute you can use GetCustomAttribute<AttributeType>() extension for MemberInfo.
This extension method builds sync dictionary from type properties decorated with SyncPropertyAttribute (I suggest to use the dictionary instead of NameValueCollection):
public static Dictionary<string, string> ToSyncDictionary<T>(this T value)
{
var syncProperties = from p in typeof(T).GetProperties()
let name = p.GetCustomAttribute<SyncProperty>()?.PropertyName
where name != null
select new {
Name = name,
Value = p.GetValue(value)?.ToString()
};
return syncProperties.ToDictionary(p => p.Name, p => p.Value);
}
Usage:
var collection = contact.ToSyncDictionary();
Output:
{
"first_name": "Test",
"last_name": "Person",
"phone": "123-123-1234",
"email": "test#test.com"
}
Note: if you are going to use contact data in POST request, then you should consider using simple JSON serialization attributes instead of creating your own attributes. E.g. with Json.NET:
public class SyncContact
{
[JsonProperty("first_name")]
public string FirstName { get; set; }
[JsonProperty("last_name")]
public string LastName { get; set; }
[JsonProperty("phone")]
public string Phone { get; set; }
[JsonProperty("email")]
public string Email { get; set; }
}
Then simple serialization will do the job:
string json = JsonConvert.SerializeObject(contact);
And the result will be exactly same as above.
The attributes belong to the properties of the class, so you need to get the type of the class, then find the appropriate properties and then get the custom attributes.
Something like:
var contact = new SyncContact { FirstName="Test", LastName="Person", Phone="123-123-1234", Email="test#test.com"};
var t = typeof(SyncContact);
var props = t.GetProperties().Select(p => new { p, attr = p.GetCustomAttribute<SyncProperty>() }).Where(p => p.attr != null);
var dict = props.ToDictionary(p => p.attr.PropertyName, v => v.p.GetValue(contact) );
This leaves out any properties that aren't tagged, but you can decide to handle those differently if you wanted to.
Fiddle
Assuming SyncProperty is tagged on every property, this should do the job:
var contact = new SyncContact { FirstName="Test", LastName="Person", Phone="123-123-1234", Email="test#test.com"};
var collection = contact.GetType().GetProperties()
.Select(x => new
{
x.GetCustomAttribute<SyncProperty>().PropertyName,
Value = x.GetValue(contact).ToString()
})
.ToDictionary(x => x.PropertyName, x => x.Value);
As a helper method:
public static class SynxHelper
{
public static Dictionary<string, string> Serialize<T>(T obj)
{
return typeof(T).GetProperties()
.Select(x => new
{
SyncProperty = x.GetCustomAttribute<SyncProperty>(),
Value = x.GetValue(obj)
})
.Where(x => x.SyncProperty != null)
.ToDictionary(x => x.SyncProperty.PropertyName, x => x.Value.ToString());
}
}
// usage
var collection = SynxHelper.Serialize(contact);
Here is a one line linq solution for this
(from prop in obj.GetType().GetProperties()
where prop.GetCustomAttribute<SyncProperty>() != null
select new { Key = prop.GetCustomAttribute<SyncProperty>().PropertyName, Value = prop.GetValue(obj) })
.ToDictionary(k => k.Key, v => v.Value);
BUT!!!!!!!! Don't try doing this your self. This is not optimized and slow as all reflection is.
This is just to demonstrate how bad reflection is
static void Main(string[] args)
{
var data = Enumerable.Range(0, 10000).Select(i => new SyncContact { FirstName = "Test", LastName = "Person", Phone = "123-123-1234", Email = "test#test.com" }).ToArray();
Stopwatch sw = new Stopwatch();
long m1Time = 0;
var total1 = 0;
sw.Start();
foreach (var item in data)
{
var a = ToSyncDictionary(item);
total1++;
}
sw.Stop();
m1Time = sw.ElapsedMilliseconds;
sw.Reset();
long m2Time = 0;
var total2 = 0;
sw.Start();
foreach (var item in data)
{
var a = ToSyncDictionary2(item);
total2++;
}
sw.Stop();
m2Time = sw.ElapsedMilliseconds;
Console.WriteLine($"ToSyncDictionary : {m1Time} for {total1}");
Console.WriteLine($"ToSyncDictionary2 : {m2Time} for {total2}");
Console.ReadLine();
}
public static IDictionary<string, string> ToSyncDictionary<T>(T value)
{
var syncProperties = from p in typeof(T).GetProperties()
let name = p.GetCustomAttribute<SyncProperty>()?.PropertyName
where name != null
select new
{
Name = name,
Value = p.GetValue(value)?.ToString()
};
return syncProperties.ToDictionary(p => p.Name, p => p.Value);
}
public static IDictionary<string, string> ToSyncDictionary2<T>(T value)
{
return Mapper<T>.ToSyncDictionary(value);
}
public static class Mapper<T>
{
private static readonly Func<T, IDictionary<string, string>> map;
static Mapper()
{
map = ObjectSerializer();
}
public static IDictionary<string, string> ToSyncDictionary(T value)
{
return map(value);
}
private static Func<T, IDictionary<string, string>> ObjectSerializer()
{
var type = typeof(Dictionary<string, string>);
var param = Expression.Parameter(typeof(T));
var newExp = Expression.New(type);
var addMethod = type.GetMethod(nameof(Dictionary<string, string>.Add), new Type[] { typeof(string), typeof(string) });
var toString = typeof(T).GetMethod(nameof(object.ToString));
var setData = from p in typeof(T).GetProperties()
let name = p.GetCustomAttribute<SyncProperty>()?.PropertyName
where name != null
select Expression.ElementInit(addMethod,
Expression.Constant(name),
Expression.Condition(Expression.Equal(Expression.Property(param, p), Expression.Constant(null)),
Expression.Call(Expression.Property(param, p), toString),
Expression.Constant(null,typeof(string))));
return Expression.Lambda<Func<T, IDictionary<string, string>>>(Expression.ListInit(newExp, setData), param).Compile();
}
}
On my machine I got a 10x boost in pers.
If you can use some serializer like JSON.net since you will need to change a lot of things to make it work well and you already have a tone of stuff that dose it for you.

Create a dictionary of a model?

It's quite hard for me to explain this, but I will give it a go.
Objective:
Create a LINQ query that will return a dictionary of data. However it must be a dictionary of the model which I am using.
View Model:
public class ValueBySupplierAndClaimTypeViewModel : ReportViewModel
{
public IQueryable<ValueBySupplierAndClaimTypeModel> ReportData {get; set; }
public TotalValueBySupplierAndClaimTypeModel ReportTotalData { get; set; }
public Dictionary<string, decimal> DictionaryData { get; set; }
public string output { get; set; }
}
Interface:
Dictionary<string, decimal> DictData;
TotalValueBySupplierAndClaimTypeModel GetTotalValueBySupplierAndClaimType(
int ClientID, int ReviewPeriodID, int StatusCategoryID);
SQL Repository:
public TotalValueBySupplierAndClaimTypeModel GetTotalValueBySupplierAndClaimType(int ClientID, int ReviewPeriodID, int StatusCategoryID)
{
var rt =
this.GetValueBySupplierAndClaimType(ClientID, ReviewPeriodID, StatusCategoryID);
TotalValueBySupplierAndClaimTypeModel x = new TotalValueBySupplierAndClaimTypeModel()
{
NormalTotal = rt.Sum(c=>c.Normal) ?? 0,
QueryTotal = rt.Sum( c => c.Query) ?? 0,
StrongTotal = rt.Sum( c => c.Strong) ?? 0
};
return x;
}
I'm really not sure how to do this. Can anybody help?
I have this function that converts an object to a dictionary. It gets all the properties of the class, as the dictionary's keys. May be you can modify it to meet your needs:
public Dictionary<string, object> ConvertClassToDict(object classToConvert)
{
Dictionary<string, object> result = new Dictionary<string, object>();
PropertyInfo[] properties = classToConvert.GetType().GetProperties();
List<string> propertiesNames = properties.Select(p => p.Name).ToList();
foreach (var propName in propertiesNames)
{
PropertyInfo property = properties.First(srcProp => srcProp.Name == propName);
var value = property.GetValue(classToConvert, null);
result.Add(propName, value);
}
return result;
}
The argument classToConvert, is just an instance of any class.
Similar to #lukiller's answer, but with LINQ:
public Dictionary<string, object> MapToDictionary(object instance)
{
if(instance == null) return null;
return instance.GetType()
.GetProperties()
.ToDictionary(p => p.Name,
p => p.GetValue(instance));
}
For example, let's suppose we have the following class:
public class User
{
public string Username { get; set; }
public string Password { get; set; }
}
We can print it like this (one line):
MapToDictionary(new User()
{
Username = "mcicero",
Password = "abc123"
}).ToList().ForEach(i => Console.WriteLine("{0}: {1}", i.Key, i.Value));
This prints out:
Username: mcicero
Password: abc123

How to convert a dictionary pair.value to a dictionary in C#?

i have the following JSON in my application.
string json = #"{""dest"":[ { ""mode"": ""1"", ""test"":""test1,test,test2""},{ ""mode"": ""2"", ""test"": ""test3"" }]}";
To get the value of dest I m using the following method.
var json_serializer = new JavaScriptSerializer();
Dictionary<string, object> dictionary = json_serializer.Deserialize<Dictionary<string, object>>(json);
public Dictionary<string, object> GetObject(Dictionary<string, object> view, string name)
{
Dictionary<string, object> result = new Dictionary<string, object>();
object value = null;
foreach (KeyValuePair<string, object> pair in view)
{
Type type = pair.Value.GetType();
if (pair.Key == name)
{
**Dictionary<string, object> child = (System.Collections.Generic.Dictionary<string, object>)pair.Value;**
result = GetObject(child, name);
if (result != null)
{
break;
}
}
else
{
}
}
return result;
}
I m getting error in the line Dictionary child = (System.Collections.Generic.Dictionary)pair.Value;.
The error says "Unable to cast object of type 'System.Collections.ArrayList' to type 'System.Collections.Generic.Dictionary`2[System.String,System.Object]'."
Anyone knows how to fix this?
Assuming tset in your array is a typo and it actually is test, you can use concerete classes ....
string json = #"{""dest"":[ { ""mode"": ""1"", ""test"":""test1,test,test2""},{ ""mode"": ""2"", ""test"": ""test3"" }]}";
var obj = new JavaScriptSerializer().Deserialize<MyObject>(json);
public class Dest
{
public string mode { get; set; }
public string test { get; set; }
}
public class MyObject
{
public List<Dest> dest { get; set; }
}

Categories