Get property from a Func<> - c#

I'm trying to return the property that has been passed to a Func<> so I can then use this property with a DbSet. See below code for this.
Calling code:
_exampleRepository.CountByField(x => x.Status)
Receiving method in a generic repository:
public Dictionary<string, int> CountByField(Expression<Func<TEntity, string>> field)
{
var whichField = ((MemberExpression) field.Body).Member.Name;
var data = _dbSet.Select(x => whichField).GroupBy(y => y);
var returnData = new Dictionary<string, int>();
foreach (var item in data)
{
returnData.Add(item.Key, item.Count());
}
return returnData;
}
At the moment I'm able to return the name of the property passed, but I need to use the actual property instead to complete the _dbSet.Select() line properly.
Any ideas? I feel like I'm not a million miles away! Just need a point in the right direction.
Cheers.

You're overcomplicating things; your field argument is already fit to be passed directly to Select :) You should probably also aggregate the data in the query, rather than issuing a new query for each group you get:
public Dictionary<string, int> CountByField(Expression<Func<TEntity, string>> field)
=>
_dbSet
.Select(field)
.GroupBy(i => i)
.Select(i => new { Key = i.Key, Count = i.Count() })
.ToDictionary(i => i.Key, i => i.Count);

Related

Iterating over properties and use each one in a lambda expression

I am trying to simplify my code a bit by iterating over the columns in a table and use each column to map to a value instead of having multiple lines of code with a single different value.
So going from this:
foreach(var n in groupedResults){
SetCellForData("Column1", n.Sum(x => x.ColumnName1FromTable));
SetCellForData("Column2", n.Sum(x => x.ColumnName2FromTable));
...
SetCellForData("Column10", n.Sum(x => x.ColumnName10FromTable));
}
To something like this:
var columnProperties = typeof(TableClass).GetProperties().Select(t => t);
foreach(var n in groupedResults){
foreach(var columnProperty in columnProperties ){
SetCellForData(columnProperty.Name, n.Sum(x => x.????);
}
}
Where the ???? part uses the columnProperty to Sum the column in the n grouped result.
Since arekzyla decided to delete his answer before I could confirm that it was what I needed then I have decided to answer my own question with arekzyla's answer.
This method creates the function:
private static Func<T, U> GetPropertyFunc<T, U>(string propertyName)
{
var parameter = Expression.Parameter(typeof(T));
var body = Expression.PropertyOrField(parameter, propertyName);
var lambda = Expression.Lambda<Func<T, U>>(body, parameter);
return lambda.Compile();
}
This code iterates over the properties in the database type and creates a function for each of them returning an integer.
var columnProperties = typeof(DatabaseType).GetProperties()
.Where(x => x.PropertyType == typeof(int))
.Select(t => new { t.Name, GetPropertyFunc = GetPropertyFunc<DatabaseType, int>(t.Name) })
.ToList();
Then we iterate over some grouped list:
foreach (var n in groupedList)
{
foreach (var property in columnProperties)
{
var sumOfElementsInGrouping = n.Sum(x => property.GetPropertyFunc(x));
}
}
Then we have the sum of the specific column based on the property.

About LINQ and LAMBDA Chain usage

I'm trying to understand how Linq chain or lambda chain is working but I couldn't get it. I wrote a sample code below. I can actually run the code with long way like that but I want to learn the other way. I used AWS SDK for the code. I'm trying to do single line code that is managed exactly the same thing while using lambda chain. There is a "_dict" variable for what I want to get from lambda Chain. I must use multiple "GroupBy"s and Select commands for "reservedList" variable but how can I do that?
Dictionary<string, Dictionary<string, int>> _dict = new Dictionary<string, Dictionary<string, int>>();
//Dict<AvailabilityZone, Dict<InstanceType, Count>>
var reservedList = _ec2Client.DescribeReservedInstances(new DescribeReservedInstancesRequest { }).ReservedInstances
.GroupBy(q => q.AvailabilityZone);
foreach (var _availabilityZoneItems in reservedList)
{
if (!_dict.ContainsKey(_availabilityZoneItems.Key))
_dict.Add(_availabilityZoneItems.Key, new Dictionary<string, int>());
var typeGroup = _availabilityZoneItems.GroupBy(q => q.InstanceType);
foreach (var _type in typeGroup)
{
var selectionCount = _type.Where(q => q.State == ReservedInstanceState.Active).Sum(q=>q.InstanceCount);
_dict[_availabilityZoneItems.Key].Add(_type.Key, selectionCount);
}
}
Trying to do something like
var reservedList = var reservedList = _ec2Client.DescribeReservedInstances(new DescribeReservedInstancesRequest { }).ReservedInstances
.GroupBy(q => q.AvailabilityZone)...
.GroupBy(q => q.InstanceType)
....Select...Where..Count...
You are probably looking for the Enumerable.SelectMany() This will flatten lists inside a list..
Something like: (untested/incomplete yet)
var typeGroup = _ec2Client
.DescribeReservedInstances(new DescribeReservedInstancesRequest {})
.ReservedInstances.GroupBy(q => q.AvailabilityZone)
.SelectMany(availabilityZone =>
availabilityZone
.GroupBy(q => q.InstanceType)
.Select(type => new
{
AvailabilityZone = availabilityZone,
Type = type
}));
foreach (var _type in typeGroup)
{
var selectionCount = _type.Type.Where(q => q.State == ReservedInstanceState.Active).Count();
_dict[_type.AvailabilityZoneItems.Key][_type.Type.Key] = selectionCount;
}
Maybe this points you into the right direction. I normally would use Linq for this..
I would try something along these lines
_ec2Client.DescribeReservedInstances(new DescribeReservedInstancesRequest()).ReservedInstances
.GroupBy(q => q.AvailabilityZone)
.ToList()
.ForEach (_availabilityZoneItems => {
_availabilityZoneItems.GroupBy(q => q.InstanceType)
.ToList()
.ForEach(_type => {
_dict[_availabilityZoneItems.Key][_type.Key] = _type.Where(q => q.State == ReservedInstanceState.Active).Count();
});
});
This is untested

Use linq expression to filter a dictionary with a list of keys

I have a dictionary containing all users with their corresponding age.
Dictionary<string,int> AllUsers;
I have a list of specific users.
List<String> Users;
I would like to filter the first dictionary AllUsers with only the users who have their name in the SpecificUsers list.
I have done something manually with loops but I would like to use linq expression but I am not very familiar with them.
Thanks in advance for your help
You could filter Users:
Users.Where(i => AllUsers.ContainsKey(i)).Select(i => new { User = i, Age = AllUsers[i] });
The major benefit of this is that you're using the indexed AllUsers to do the filtering, so your total computational complexity only depends on the amount of users in Users (Dictionary.Contains is O(1)) - the naïve approaches tend to be Users * AllUsers.
If you want a dictionary on output, it's as simple as replacing the .Select(...) above with
.ToDictionary(i => i, i => AllUsers[i])
It might work
var newdict = AllUsers.Where(x => Users.Contains(x.Key))
.ToDictionary(val => val.Key, val => val.Value);
it will create new dictionary (cause linq is for querying not updating) with all the users from dictionary that are on the Users list. You need to use ToDictionary to actualy make it dictionary.
EDIT:
As #Rawling said it would be more performant to filter on Dictionary rather than on list. Solution to achieve that is present in #Luaan answer (I won't copy it as some do)
You can use a join() method to actually join the two collections. It allows us to get what you need with a single line of linq.
var allUsers = new Dictionary<string, int>();
allUsers.Add("Bob", 10);
allUsers.Add("Tom", 20);
allUsers.Add("Ann", 30);
var users = new List<string>();
users.Add("Bob");
users.Add("Tom");
users.Add("Jack");
var result = allUsers.Join(users, o => o.Key, i => i, (o, i) => o);
foreach(var r in result)
{
Console.WriteLine(r.Key + " " + r.Value);
}
It will output the following in the console:
Bob 10
Tom 20
Only the names that appears in both collection will be available in the result collection
There are multiple ways to do this
You can use this using where keyword
var result= yourDictionary.Where(p=> yourList.Contains(p.Key))
.ToDictionary(p=> p.Key, p=> p.Value);
But if you have lot of entries its better to use HashSet
var strings = new HashSet<string>(yourList);
var result= yourDictionary.Where(p=> strings.Contains(p.Key))
.ToDictionary(p=> p.Key, p=> p.Value);
using JOIN
var query =
from kvp in yourDictionary
join s in yourList on kvp.Key equals s
select new { kvp.Key, kvp.Value };
With the help of the following useful function
public static class Extensions
{
public static KeyValuePair<TKey,TValue>? Find<TKey, TValue>(this IDictionary<TKey, TValue> source, TKey key)
{
TValue value;
return source.TryGetValue(key, out value) ? new KeyValuePair<TKey, TValue>(key, value) : (KeyValuePair<TKey, TValue>?)null;
}
}
here is IMO the optimal solution (uses single lookup per key and does not introduce closure):
var filteredUsers = Users.Select(AllUsers.Find)
.Where(item => item.HasValue)
.ToDictionary(item => item.Value.Key, item => item.Value.Value);

NameValueCollection returns Length Property instead of Name Value

Could someone shed some light on this my NameValueCollection returns the Length property instead of Name and Value could some show me what im doing wrong here. I can't set the DataTextField or DataValueField for the dropdownlist it just gives me length.
public NameValueCollection GetDisplayForumGroups()
{
using (CMSEntities db = new CMSEntities())
{
var forums = (from x in db.Forums where x.ParentID == null select new { Name = x.Title, Value = x.ForumID });
NameValueCollection collection = new NameValueCollection();
foreach (var forum in forums)
{
collection.Add(forum.Name, forum.Value.ToString());
}
return collection;
}
}
public Dictionary<string, int> GetDisplayForumGroups()
{
using (CMSEntities db = new CMSEntities())
{
Dictionary<string, int> forums = (from x in db.Forums where x.ParentID == null select x).ToDictionary(x => x.Title, x => x.ForumID);
return forums;
}
}
You can't bind directly to a NameValueCollection since it doesn't provide a suitable enumerator. The standard enumerator enumerates through the keys only.
Then again you shouldn't be using a NameValueCollection for this in the first place, you should use the generic Dictionary unless you need multiple values per key (and even then there are better alternatives for most cases). There's even a Linq method for automagically making a dictionary:
Dictionary<string, int> forums = (from x
in db.Forums
where x.ParentID == null
select x)
.ToDictionary(x => x.Title, x => x.ForumID);

Filtering out values from a C# Generic Dictionary

I have a C# dictionary, Dictionary<Guid, MyObject> that I need to be filtered based on a property of MyObject.
For example, I want to remove all records from the dictionary where MyObject.BooleanProperty = false. What is the best way of acheiving this?
If you don't care about creating a new dictionary with the desired items and throwing away the old one, simply try:
dic = dic.Where(i => i.Value.BooleanProperty)
.ToDictionary(i => i.Key, i => i.Value);
If you can't create a new dictionary and need to alter the old one for some reason (like when it's externally referenced and you can't update all the references:
foreach (var item in dic.Where(item => !item.Value.BooleanProperty).ToList())
dic.Remove(item.Key);
Note that ToList is necessary here since you're modifying the underlying collection. If you change the underlying collection, the enumerator working on it to query the values will be unusable and will throw an exception in the next loop iteration. ToList caches the values before altering the dictionary at all.
Since Dictionary implements IEnumerable<KeyValuePair<Key, Value>>, you can just use Where:
var matches = dictionary.Where(kvp => !kvp.Value.BooleanProperty);
To recreate a new dictionary if you need it, use the ToDictionary method.
You can simply use the Linq where clause:
var filtered = from kvp in myDictionary
where !kvp.Value.BooleanProperty
select kvp
public static Dictionary<TKey, TValue> Where<TKey, TValue>(this Dictionary<TKey, TValue> instance, Func<KeyValuePair<TKey, TValue>, bool> predicate)
{
return Enumerable.Where(instance, predicate)
.ToDictionary(item => item.Key, item => item.Value);
}
Here is a general solution, working not only for boolean properties of the values.
Method
Reminder: Extension methods must be placed in static classes. Dont forget the using System.Linq; statement at the top of the source file.
/// <summary>
/// Creates a filtered copy of this dictionary, using the given predicate.
/// </summary>
public static Dictionary<K, V> Filter<K, V>(this Dictionary<K, V> dict,
Predicate<KeyValuePair<K, V>> pred) {
return dict.Where(it => pred(it)).ToDictionary(it => it.Key, it => it.Value);
}
Usage
Example:
var onlyWithPositiveValues = allNumbers.Filter(it => it.Value > 0);
I added the following extension method for my project which allows you to filter an IDictionary.
public static IDictionary<keyType, valType> KeepWhen<keyType, valType>(
this IDictionary<keyType, valType> dict,
Predicate<valType> predicate
) {
return dict.Aggregate(
new Dictionary<keyType, valType>(),
(result, keyValPair) =>
{
var key = keyValPair.Key;
var val = keyValPair.Value;
if (predicate(val))
result.Add(key, val);
return result;
}
);
}
Usage:
IDictionary<int, Person> oldPeople = personIdToPerson.KeepWhen(p => p.age > 29);
Hope it fit your needs.
Dictionary<string, string> d = new Dictionary<string, string>();
d.Add("Product", "SELECT * FROM PRODUCT");
d.Add("Order", "SELECT * FROM ORDER");
d.Add("Customer", "SELECT * FROM CUSTOMER");
var x = d.Where(kvp => kvp.Key == "Order")
.ToDictionary(item => item.Key, item => item.Value)
.FirstOrDefault();
if(x.Key != null)
{
Console.WriteLine($"{x.Value}"); //this should return "SELECT * FROM ORDER"
}

Categories

Resources