I have two classes:
public GeneralClassName
{
public GeneralClassName ()
{
SpecificList = new List<OtherClass>();
}
public string StringValue;
public string OtherStringValue;
public List<OtherClass> SpecificList;
}
and
public OtherClass
{
public string Name;
public string Number;
}
After a JSON deserialization I obtain a nice List<GeneralClassName>, the result I want is a Dictionary<string, int> whose value is the sum of the variabiles "Number" inside List<OtherClass> inside List<GeneralClassName>, while the key is the variabile Name.
In other words I'd like to sum Number grouping by Name.
Now, the only thing that came across my mind is a nested foreach, something like that:
Dictionary<string, int> resultDictionary = new Dictionary<string, int>();
foreach(List<OtherClass> listOtherClass in bigListGeneralClass.Select(x => x.SpecificList))
{
foreach(OtherClass otherClass in listOtherClass)
{
int value = 0;
if(resultDictionary.ContainsKey(otherClass.Name))
{
resultDictionary[otherClass.Name] += otherClass.Number;
}
else
{
resultDictionary.Add(otherClass.Name, otherClass.Number);
}
}
}
While this solution seems to work well, I don't like it at all.
Is there a more clean way to find this result? Maybe through a nice LINQ query?
As you don't use any information from the GeneralClassName you can use SelectMany to flatten your list. This flat list of OtherClass instances is than grouped by the Name property. Finally, the list of groups is transformed into a dictionary with the key of the group (aka the Name property) being the key of the new property and the value being the sum of all Number values in that group:
var result = bigListGeneralClass.SelectMany(x => x.SpecificList)
.GroupBy(x => x.Name)
.ToDictionary(x => x.Key,
x => x.Sum(y => y.Number));
This code assumes that OtherClass.Number is in fact an int not a string. This assumption is also used in your sample code with the loop.
If this assumption is not correct, change y.Number to int.Parse(CultureInfo.InvariantCulture, y.Number).
Note: This will throw an exception if any of the numbers can't be parsed, so you might want to make sure beforehand that all contain valid numbers.
Try this:
Dictionary<string, int> result =
bigListGeneralClass.SpecificList.GroupBy(sl => sl.Name)
.ToDictionary(group => group.Key, group => group.Sum(x => Int32.Parse(x.Number)));
Related
I have a class that contains all the properties of a query I'm constructing could possibly have (most of which are optional)
For example:
public class QueryClass
{
public string Offset {get; set;}
public string Limit {get; set;}
public string Sort {get; set;}
}
Then in a BuildQuery() method I am constructing the query by doing this:
private string BuildQuery(QueryClass query)
{
var queryDictionary = new Dictionary<string, string>();
if (!string.IsNullOrEmpty(query.Offset)
{
queryDictionary.Add("offset", query.Offset);
}
if (!string.IsNullOrEmpty(query.Limit)
{
queryDictionary.Add("limit", query.Limit);
}
if (!string.IsNullOrEmpty(query.Sort)
{
queryDictionary.Add("sort", query.Sort);
}
var content = new FormUrlEncodedContent(queryDictionary);
return content.ReadAsStringAsync().Result;
}
This works, but the issue is my actual QueryClass is significantly larger than this, there's got to be a better way to do this than to have a ton of IF statements for every optional property but I haven't been able to come up with a more elegant solution. I also don't care for adding the keys in the dictionary in this way, I probably need a new approach for how I structure the QueryClass.
If you don't mind taking the reflection hit, just project, filter nulls, then send to ToDictionary
Note : The assumptions here are, all the property names are your keys, and all the properties are convertible to string
var queryClass = new QueryClass()
{
Limit = "asdf",
Sort = "Bob"
};
var results = queryClass
.GetType()
.GetProperties()
.Select(x => (Value: x.GetValue(queryClass) as string, x.Name))
.Where(x => !string.IsNullOrWhiteSpace(x.Value))
.ToDictionary(
x => x.Name.ToLower(),
x => x.Value);
foreach (var (key, value) in results)
Console.WriteLine($"{key} : {value}");
Output
limit : asdf
sort : Bob
Add pepper and salt to taste
Or as an extension method
public static IDictionary<string, string> ConvertToDictionary<T>(this T source)
=> typeof(T).GetProperties()
.Select(x => (Value: x.GetValue(source) as string, x.Name))
.Where(x => !string.IsNullOrWhiteSpace(x.Value))
.ToDictionary(
x => x.Name.ToLower(),
x => x.Value!);
I have a dictionary which holds information from a parsed test run. The key is the name of the method and the value is a list of TestRunProperties. My dictionary contains all methods from a test run and I would like to remove the methods which failed during a test run. Is this possible to do with Linq?
TestRunProperties class:
public class TestRunProperties
{
public string computerName { get; set; }
public TimeSpan duration { get; set; }
public string startTime { get; set; }
public string endTime { get; set; }
public string testName { get; set; }
public string outcome { get; set; }
}
Dictionary:
//Key is the name of the method, value is the properties associated with each run
private static Dictionary<string, List<TestRunProperties>> runResults = new Dictionary<string, List<TestRunProperties>>();
I've tried this but I think I'm getting confused with the Where part:
runResults.Remove(runResults.Where(methodName => methodName.Value.Where(method => method.outcome.ToLower().Equals("failed"))));
I'm quite new to Linq and Lambda and I'm still trying to understand how to access data like this.
Just use a loop to remove the items you don't want. You can write an extension method to make it easier to call:
public static class DictionaryExt
{
public static void RemoveAll<K, V>(this IDictionary<K, V> dict, Func<K, V, bool> predicate)
{
foreach (var key in dict.Keys.ToArray().Where(key => predicate(key, dict[key])))
dict.Remove(key);
}
}
This usually will be more efficient than creating an entirely new dictionary, especially if the number of items being removed is relatively low compared to the size of the dictionary.
Your calling code would look like this:
runResults.RemoveAll((key, methodName) => methodName.Value.Where(method => method.outcome.ToLower().Equals("failed")));
(I chose the name RemoveAll() to match List.RemoveAll().)
You could create a new dictionary by filtering out the invalid ones:
var filtered = runResults.ToDictionary(p => p.Key, p => p.Value.Where(m => m.outcome.ToLower() != "failed").ToList());
Ok, grrrrrr was faster :-)
To be honest you're probably better off selecting a new dictionary from the existing one:
runResults.Select().ToDictionary(x => x.Key, x => x.Value.Where(x => x.Value.outcome != "failed"));
*editted to reflect list in the dictionary.
Actually, you can get rid of the ones with no successful results by doing this too:
runResults.Select(x => new { x.Key, x.Value.Where(x => x.Value.outcome != "failed")} ).Where(x => x.Value.Any()).ToDictionary(x => x.Key, x => x.Value);
I find that I am repeating a lot of new object initialization code in Linq queries, for example when creating different overloaded methods that use the same query structure.
var result = ItemResponses
.GroupBy(ir => ir.ItemID)
.Select(
grouped => new
{
ItemID = grouped.Key,
Average = (double)grouped.Average(g => g.OptionValue),
...etc. lots of properties, similar structure across lots of methods...
...Would really love to be able to write this code once somewhere...
}
);
At first I thought using constructors might be one way of doing it, something along these lines:
var result = ItemResponses
.GroupBy(ir => ir.ItemID)
.Select(grouped => new TestClass(grouped) //or anonymous type
);
public class TestClass
{
public int ItemID { get; set; }
public double Average { get; set; }
public TestClass() {}
public TestClass(IGrouping<int, ItemRespons> values)
{
ItemID = values.Key;
Average = values.Average(g => g.OptionValue);
}
}
But I see that Linq (to Entities at least) only allows parameterless constructors and initializers. So this approach doesn't seem to work.
Is there another way I can achieve simplifying this type of repetive code, and only having it in one place?
Use a delegate:
Func<IQueryable<ItemResponse>,IEnumerable<TestClass>> SelectResult = q =>
q.GroupBy(ir => ir.ItemID)
.Select(
grouped => new TestClass
{
ItemID = grouped.Key,
Average = (double)grouped.Average(g => g.OptionValue),
...
});
Then you can use it like this:
var result = SelectResult(ctx.ItemResponse);
It's even better to make it a extension method of course:
public static class Extensions
{
public static IEnumerable<TestClass> SelectResult(this IQueryable<ItemResponse> q)
{
return q.GroupBy(ir => ...)
}
}
And use it like this:
var result = ctx.ItemResponses.SelectResult();
It's not possible for anonymous type projections because there is no way to define a typed result, except some non generic type like dynamic, object or IQueryable, but then you'll have problem consuming it.
However it is possible to reuse projections to a custom types (like your sample TestClass). But instead of constructor, you have to put the code in a expression returning method.
For instance, instead of this
public TestClass(IGrouping<int, ItemResponse> values)
{
ItemID = values.Key;
Average = values.Average(g => g.OptionValue);
// ...etc. lots of properties
}
you could use something like this
static Expression<Func<IGrouping<int, ItemResponse>, TestClass>> ToTestClass()
{
return values => new TestClass
{
ItemID = values.Key,
Average = values.Average(g => g.OptionValue)
// ...etc. lots of properties
};
}
and the sample query would be
var result = ItemResponses
.GroupBy(ir => ir.ItemID)
.Select(ToTestClass());
Suppose i have two (could be more) lists of same object having same fields/properties.
Each list represent the same object
Proerpties:
HoursWorked
HoursRate
I want to take iterate and take sum of each field from all lists (could be 2, 3, or so on) and store it in an dictionary with key value pair. e.g HoursWorked:2 and HourseRate:6
Currently, i am able to do it for only one field only (hard coded). I want to make it generic so i can fill dictionary with Key/Value for all fields.
I have defined my dictionary as follow
public Dictionary<string, double> TotalCount { get; set; }
Linq Query:
Dictionary<string, double> totalCount = records
.GroupBy(x => records)
.ToDictionary(x => Convert.ToString("HoursWorked"), x => x.Where(y => y.HoursWorked != null).Sum(y => y.HoursWorked).Value);
Any help on this?
Sample Data:
Input
report =
{
[HoursWorked: 1.0, HoursRate:10],
[HoursWork:2.0, HoursRate:15]
}
Expected Output
Dictioary = {Key:HoursWorked Value: 3.0,Key:HoursRate Value:25}
Dictionary<string, double> dictionary = new[] { "HoursWorked", "HoursRate" }
.ToDictionary(k => k, v => collections
.SelectMany(x => x)
.Sum(y => (double)y.GetType().GetProperty(v).GetValue(y)));
Where 'collections' is your collection of lists.
Obviously totally type unsafe and will fall down very easily!
I would argue that a much better pattern for this, as opposed to using reflection, would be to write a method or interface that will return the correct double value given the string key.
class someObject
{
public int workingHours { get; set; }
public int hourRate { get; set; }
}
You can create a common list from all list.
List<SomeObject> lst = new List<SomeObject>();
lst.AddRange(Oldlst1);
lst.AddRange(Oldlst2);
Then you can group by based on hour rate.
var n = lst.GroupBy(x=>x.hourRate);
And then you can create a dictionary.
var m=n.ToDictionary(x=>x.Key, x=>x.Sum(y=>y.workingHours));
Here in m you will get hourRate and in value you will get sum of working hour.
I have a List and MyClass is:
public class MyClass
{
public bool Selected { get; set; }
public Guid NoticeID { get; set; }
public Guid TypeID { get; set; }
}
My question is, how do i convert this list into a Dictionary<Guid, List<Guid>>, where the dictionary key is the GUID from the TypeID property, and the value is a list of all the NoticeID values corresponding to that TypeID. I have tried like so:
list.GroupBy(p => p.TypeID).ToDictionary(p => p.Key, p => p.ToList())
but this returns a Dictionary <Guid, List<MyClass>>, and I want a Dictionary<Guid, List<Guid>>.
Well, when you group you can specify the value you want for each element of the group:
var dictionary = list.GroupBy(p => p.TypeID, p => p.NoticeID)
.ToDictionary(p => p.Key, p => p.ToList());
However, I would strongly consider using a lookup instead of a dictionary:
var lookup = list.ToLookup(p => p.TypeID, p => p.NoticeID);
Lookups are much cleaner in general:
They're immutable, whereas your approach ends up with lists which can be modified
They express in the type system exactly what you're trying to express (one key to multiple values)
They make looking keys up easier by returning an empty sequence of values for missing keys, rather than throwing an exception