How to convert this sample foreach into lambda expression? - c#

How to convert this sample foreach into lambda expression?
foreach (ADOMD.Member iMember in pMemberCollection)
{
decimal lDimensionValue = 0;
if (Decimal.TryParse(iMember.Name, out lDimensionValue))
lDimensionValues.Add(lDimensionValue);
}
lDimensionValues.Sort();
ADOMD.Member is a interface looks like
[TypeLibType(xxx)]
[Guid("xxxxx")]
public interface Member
{
[DispId(0)]
string Caption { get; }
[DispId(1610743817)]
int ChildCount { get; }
string Name { get; }
[DispId(1610743812)]
Member Parent { get; }
[DispId(1610743819)]
bool ParentSameAsPrev { get; }
[DispId(1610743815)]
}

lDimensionValues =
pMemberCollection
.Cast<ADOMD.Member>()
.Select(iMember => {
decimal lDimensionValue = 0;
if (Decimal.TryParse(iMember.Name, out lDimensionValue))
return (decimal?)lDimensionValue;
else return null;
})
.Where(x => x != null)
.Select(x => x.Value)
.OrderBy(x => x)
.ToList();
Very ugly and verbose. If we had a TryParseDecimal method it would be cleaner.
This is not a perfect case for LINQ. Among other reasons due to the legacy collection that requires a Cast apparently.

Had to try to do this in as few lines as possible, interesting problem, i would not convert your method to LINQ though, if it already works (what works works)
lDimensionValues = pMemberCollection.Where(a => {
decimal lDimensionValued;
return decimal.TryParse(a.Name, out lDimensionValued);
}).Select(a=> decimal.Parse(a.Name)).Sort();

Related

LINQ select property by name [duplicate]

This question already has answers here:
Dynamic LINQ OrderBy on IEnumerable<T> / IQueryable<T>
(24 answers)
Closed 2 years ago.
The community reviewed whether to reopen this question last year and left it closed:
Original close reason(s) were not resolved
I'm attempting to use a variable inside of a LINQ select statement.
Here is an example of what I'm doing now.
using System;
using System.Collections.Generic;
using System.Linq;
using Faker;
namespace ConsoleTesting
{
internal class Program
{
private static void Main(string[] args)
{
List<Person> listOfPersons = new List<Person>
{
new Person(),
new Person(),
new Person(),
new Person(),
new Person(),
new Person(),
new Person(),
new Person(),
new Person(),
new Person(),
new Person()
};
var firstNames = Person.GetListOfAFirstNames(listOfPersons);
foreach (var item in listOfPersons)
{
Console.WriteLine(item);
}
Console.WriteLine();
Console.ReadKey();
}
public class Person
{
public string City { get; set; }
public string CountryName { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public Person()
{
FirstName = NameFaker.Name();
LastName = NameFaker.LastName();
City = LocationFaker.City();
CountryName = LocationFaker.Country();
}
public static List<string> GetListOfAFirstNames(IEnumerable<Person> listOfPersons)
{
return listOfPersons.Select(x => x.FirstName).Distinct().OrderBy(x => x).ToList();
}
public static List<string> GetListOfCities(IEnumerable<Person> listOfPersons)
{
return listOfPersons.Select(x => x.FirstName).Distinct().OrderBy(x => x).ToList();
}
public static List<string> GetListOfCountries(IEnumerable<Person> listOfPersons)
{
return listOfPersons.Select(x => x.FirstName).Distinct().OrderBy(x => x).ToList();
}
public static List<string> GetListOfLastNames(IEnumerable<Person> listOfPersons)
{
return listOfPersons.Select(x => x.FirstName).Distinct().OrderBy(x => x).ToList();
}
}
}
}
I have a Some very not DRY code with the GetListOf... Methods
i feel like i should be able to do something like this
public static List<string> GetListOfProperty(
IEnumerable<Person> listOfPersons, string property)
{
return listOfPersons.Select(x =>x.property).Distinct().OrderBy(x=> x).ToList();
}
but that is not vaild code. I think the key Might Relate to Creating a Func
if That is the answer how do I do that?
Here is a second attempt using refelection But this is also a no go.
public static List<string> GetListOfProperty(IEnumerable<Person>
listOfPersons, string property)
{
Person person = new Person();
Type t = person.GetType();
PropertyInfo prop = t.GetProperty(property);
return listOfPersons.Select(prop).Distinct().OrderBy(x =>
x).ToList();
}
I think the refection might be a DeadEnd/red herring but i thought i would show my work anyway.
Note Sample Code is simplified in reality this is used to populate a datalist via AJAX to Create an autocomplete experience. That object has 20+ properties and I can complete by writing 20+ methods but I feel there should be a DRY way to complete this. Also making this one method also would clean up my controller action a bunch also.
Question:
Given the first section of code is there a way to abstract those similar methods into a single method buy passing some object into the select Statement???
Thank you for your time.
You would have to build the select
.Select(x =>x.property).
by hand. Fortunately, it isn't a tricky one since you expect it to always be the same type (string), so:
var x = Expression.Parameter(typeof(Person), "x");
var body = Expression.PropertyOrField(x, property);
var lambda = Expression.Lambda<Func<Person,string>>(body, x);
Then the Select above becomes:
.Select(lambda).
(for LINQ based on IQueryable<T>) or
.Select(lambda.Compile()).
(for LINQ based on IEnumerable<T>).
Note that anything you can do to cache the final form by property would be good.
From your examples, I think what you want is this:
public static List<string> GetListOfProperty(IEnumerable<Person>
listOfPersons, string property)
{
Type t = typeof(Person);
PropertyInfo prop = t.GetProperty(property);
return listOfPersons
.Select(person => (string)prop.GetValue(person))
.Distinct()
.OrderBy(x => x)
.ToList();
}
typeof is a built-in operator in C# that you can "pass" the name of a type to and it will return the corresponding instance of Type. It works at compile-time, not runtime, so it doesn't work like normal functions.
PropertyInfo has a GetValue method that takes an object parameter. The object is which instance of the type to get the property value from. If you are trying to target a static property, use null for that parameter.
GetValue returns an object, which you must cast to the actual type.
person => (string)prop.GetValue(person) is a lamba expression that has a signature like this:
string Foo(Person person) { ... }
If you want this to work with any type of property, make it generic instead of hardcoding string.
public static List<T> GetListOfProperty<T>(IEnumerable<Person>
listOfPersons, string property)
{
Type t = typeof(Person);
PropertyInfo prop = t.GetProperty(property);
return listOfPersons
.Select(person => (T)prop.GetValue(person))
.Distinct()
.OrderBy(x => x)
.ToList();
}
I would stay away from reflection and hard coded strings where possible...
How about defining an extension method that accepts a function selector of T, so that you can handle other types beside string properties
public static List<T> Query<T>(this IEnumerable<Person> instance, Func<Person, T> selector)
{
return instance
.Select(selector)
.Distinct()
.OrderBy(x => x)
.ToList();
}
and imagine that you have a person class that has an id property of type int besides those you already expose
public class Person
{
public int Id { get; set; }
public string City { get; set; }
public string CountryName { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
all you need to do is fetch the results with type safe lambda selectors
var ids = listOfPersons.Query(p => p.Id);
var firstNames = listOfPersons.Query(p => p.FirstName);
var lastNames = listOfPersons.Query(p => p.LastName);
var cityNames = listOfPersons.Query(p => p.City);
var countryNames = listOfPersons.Query(p => p.CountryName);
Edit
As it seems you really need hardcoded strings as the property inputs, how about leaving out some dynamism and use a bit of determinism
public static List<string> Query(this IEnumerable<Person> instance, string property)
{
switch (property)
{
case "ids": return instance.Query(p => p.Id.ToString());
case "firstName": return instance.Query(p => p.FirstName);
case "lastName": return instance.Query(p => p.LastName);
case "countryName": return instance.Query(p => p.CountryName);
case "cityName": return instance.Query(p => p.City);
default: throw new Exception($"{property} is not supported");
}
}
and access the desired results as such
var cityNames = listOfPersons.Query("cityName");
You should be able to do it with Reflection. I use it something similar.
Just change your reflection try to this:
public static List<string> GetListOfValues(IEnumerable<Person> listOfPersons, string propertyName)
{
var ret = new List<string>();
PropertyInfo prop = typeof(Person).GetProperty(propertyName);
if (prop != null)
ret = listOfPersons.Select(p => prop.GetValue(p).ToString()).Distinct().OrderBy(x => x).ToList();
return ret;
}
I hope it helps.
It's based on C# 6
You can also use this. works for me.
public static class ObjectReflectionExtensions
{
public static object GetValueByName<T>(this T thisObject, string propertyName)
{
PropertyInfo prop = typeof(T).GetProperty(propertyName);
return prop.GetValue(thisObject);
}
}
And call like this.
public static List<string> GetListOfProperty(IEnumerable<Person> listOfPersons, string propertyName)
{
return listOfPersons.Select(x =>(string)x.GetValueByName(propertyName)).Distinct().OrderBy(x=> x).ToList();
}
If you want to select all the values:
object[] foos = objects.Select(o => o.GetType().GetProperty("PropertyName").GetValue(o)).ToArray();

LINQ Generic List cannot convert issue

public static List<Reservations> getReservations(int reservationId)
{
return hoteldb.hotel_reservations.Where(x => x.reservation_id == reservationId).ToList();
}
I converted the values, I made sure that everything is good but to no avail.
It's not really working even the code below:
public List<Reservations> getReservations(int reservationId)
{
return hoteldb.hotel_reservations.Where(x => x.reservation_room_desc == "sdfsfs").ToList();
}
Maybe getReservations() should use the type : List<hotel_reservations> ?
public List<hotel_reservations> getReservations(int reservationId)
{
var y = hoteldb.hotel_reservations
.Where(x => x.reservation_room_desc == "sdfsfs")
.ToList();
return y;
}

How can I read a complete XML element here?

I have an XML file with a number of Units:
<Unit Name="Length">
<Prefix Char="c"
IsSelected="false"
Factor="1"/>
<Prefix Char="d"
IsSelected="true"
Factor="104"/>
</Unit>
I want to read an entire object:
public static Dictionary<string, Unit> Units { get; set; }
public class Prefix
{
public Func<double, double> fnc = null;
public Prefix(string c, double f, bool i, bool ifix = false,string fn = null)
{
Char = c;
Factor = f;
IsFixed = ifix;
Unit.funcs.TryGetValue(fn, out fnc);
}
public bool IsSelected { get; set; }
public bool IsFixed { get; set; }
public double Factor { get; set; }
public string Char { get; set; }
}
public Unit() { }
public Unit(string n, List<Prefix> p)
{
_name = n;
Prefixes = p;
}
private List<Prefix> _prefixes;
public List<Prefix> Prefixes
{
get { return _prefixes; }
set { _prefixes = value; }
}
private string _name;
public string Name
{
get { return _name; }
set { _name = value; }
}
....
}
I now have this:
Form.Units = (data.Descendants("Unit").Select(x => new Unit
(
x.Attribute("Name").Value,
(List<Prefix>) x.Descendants("Prefix").Select(p => new Prefix(
p.Attribute("Char").Value,
Convert.ToDouble(p.Attribute("Factor").Value),
p.Attribute("IsSelected").Value == "true",
p.Attribute("IsFixed").Value == "true",
p.Attribute("Func").Value)
)
)
)
).ToDictionary(x => x.Name, x => x);
and get the following error:
"Unable to cast object of type
'WhereSelectEnumerableIterator2[System.Xml.Linq.XElement,DD.Prefix]'
to type 'System.Collections.Generic.List1[DD.Prefix]'."
Apparently there is something wrong with the (List<Prefix>)
What would the query have to be then? How do I get stuff in the List<>?
.
I would express this code in the following manner.. for it avoids doing parameter constructors that would constrain these types to IEnuermable expressions. i.e. avoid doing parameter constructors on types you plan to use for querying..
I'm placeholding the types that are using in/by Decendents as XmlElement type... but I'm sure that's inaccurate.. just replace it with whatever is the correct type.
Additionally, this snippet doesn't account for Unit.funcs.TryGetValue(fn, out fnc);.. and it presumes there is property name Func on the Prefix type. You can perform null checks during he assignment/setting..
data.Descendants("Unit")
.Select<XmlElement, Unit>(x => new Unit()
{
Name = x.Attribute("Name").Value,
Prefixes = x.Descendants("Prefix").Select<XmlElement, Prefix>(p => new Prefix()
{
Char = p.Attribute("Char").Value,
Factor = Convert.ToDouble(p.Attribute("Factor").Value),
IsSelectd = p.Attribute("IsSelected").Value == "true",
IsFixed = p.Attribute("IsFixed").Value == "true",
Func = p.Attribute("Func").Value
}).ToList()
})
.Select<Unit, KeyValuePair<string, Unit>>(unit => new KeyValuePair<string, Unit>()
{
Key = x.Name,
Value = x
})
.ToList()
.ForEach( kvp => {
Form.Units.Add(kvp.Key, kvp.Value);
});
It's not a list as is but a query, so you can't just cast it to a list but you can call ToList on it to have it enumerated and a list returned:
Form.Units = (data.Descendants("Unit").Select(x => new Unit
(
x.Attribute("Name").Value,
//(List<Prefix>) no casting needed
x.Descendants("Prefix").Select(p => new Prefix(
p.Attribute("Char").Value,
Convert.ToDouble(p.Attribute("Factor").Value),
p.Attribute("IsSelected").Value == "true",
p.Attribute("IsFixed").Value == "true",
p.Attribute("Func").Value)
).ToList() // you had an IEnumerable<Prefix> now this is a List<Prefix>
)
)
).ToDictionary(x => x.Name, x => x);
To expand upon my comments, there are a couple of things here. First, you can use the .ToList() extension method on the .Select to convert the IEnumerable<T> collection to a List<T>.
Secondly, you will get a null reference exception if any attributes or elements are missing in the query. You can handle this safely by explicitly casting to string (and then converting the result if needed).
The updated query would look like this:
Form.Units = (data.Descendants("Unit").Select(x => new Unit
((string)x.Attribute("Name"),
x.Descendants("Prefix").Select(p => new Prefix(
(string)p.Attribute("Char"),
Convert.ToDouble((string)p.Attribute("Factor")),
(string)p.Attribute("IsSelected") == "true",
(string)p.Attribute("IsFixed") == "true",
(string)p.Attribute("Func")).ToList()
)
)
)
).ToDictionary(x => x.Name, x => x);
Note that you don't need .Value when using (string) (since .Value is already string).

Intersection of List of List

I have a list of lists which looks like the following
public class FilteredVM
{
public int ID { get; set; }
public string Name { get; set; }
public string Number { get; set; }
}
List<List<FilteredVM>> groupedExpressionResults = new List<List<FilteredVM>>();
I would like to Intersect the lists within this list based upon the ID's, whats the best way to tackle this?
Here's an optimized extension method:
public static HashSet<T> IntersectAll<T>(this IEnumerable<IEnumerable<T>> series, IEqualityComparer<T> equalityComparer = null)
{
if (series == null)
throw new ArgumentNullException("series");
HashSet<T> set = null;
foreach (var values in series)
{
if (set == null)
set = new HashSet<T>(values, equalityComparer ?? EqualityComparer<T>.Default);
else
set.IntersectWith(values);
}
return set ?? new HashSet<T>();
}
Use this with the following comparer:
public class FilteredVMComparer : IEqualityComparer<FilteredVM>
{
public static readonly FilteredVMComparer Instance = new FilteredVMComparer();
private FilteredVMComparer()
{
}
public bool Equals(FilteredVM x, FilteredVM y)
{
return x.ID == y.ID;
}
public int GetHashCode(FilteredVM obj)
{
return obj.ID;
}
}
Like that:
series.IntersectAll(FilteredVMComparer.Instance)
You could just write
series.Aggregate((a, b) => a.Intersect(b, FilteredVMComparer.Instance))
but it 'd be wasteful because it'd have to construct multiple sets.
Intersect will work when the type are dead equals, which in your case won't apply because you haven't implemented the GetHashCode and Equals methods, which is the best and complete way.
Thus, If you only intended to take elements that contains in both lists, than the following solution will suit you right.
Assuming list1 and list2 are type List<FilteredVM> than, The most simple way, will be doing this:
var intersectByIDs = list1.Where(elem => list2.Any(elem2 => elem2.ID == elem.ID));
If you are a fan of one-liner solutions you can use this:
List<FilteredVM> result = groupedExpressionResults.Aggregate((x, y) => x.Where(xi => y.Select(yi => yi.ID).Contains(xi.ID)).ToList());
And if you just want the IDs you can just add .Select(x => x.ID), like this:
var ids = groupedExpressionResults.Aggregate((x, y) => x.Where(xi => y.Select(yi => yi.ID).Contains(xi.ID)).ToList()).Select(x => x.ID);
Working Demo

Multiple columns in Linq

Problem: how to simplify the code below, as I'm aiming towards 30 different properties in the thing class.
The code looks for uniqueness in a 'thing' property.
public class thing
{
public string Name { get; set; }
public string Colour { get; set; }
public string Position { get; set; }
public string Height { get; set; }
}
public List<thing> SeeIfAnyInListHaveAUniqueSingleColumn(List<thing> listOfThings)
{
// try colour
IEnumerable<IGrouping<string, thing>> thingQuery2 = from t in listOfThings
group t by t.Colour;
List<thing> listOfThingsFound = new List<thing>();
foreach (var thingGroup in thingQuery2)
{
if (thingGroup.Count() == 1)
{
foreach (thing thing in thingGroup) // there is only going to be 1
listOfThingsFound.Add(thing);
}
}
// try position
IEnumerable<IGrouping<string, thing>> thingQuery3 = from t in listOfThings
group t by t.Position;
foreach (var thingGroup in thingQuery3)
{
if (thingGroup.Count() == 1)
{
foreach (thing thing in thingGroup) // there is only going to be 1
listOfThingsFound.Add(thing);
}
}
return listOfThingsFound;
}
Downloadable code on http://www.programgood.net/2010/11/06/FindingUniquenessInData.aspx
I think that if you abstract away the FindUnique operation, you can more easily write the tests:
static IEnumerable<T> FindDistinct<T, TKey>(this IEnumerable<T> source,
Func<T, TKey> keySelector)
{
return from item in source
group item by keySelector(item) into grp
where grp.Count() == 1
from single in grp
select single;
}
Then you can write:
var thingsWithUniqueName = listOfThings.FindDistinct(t => t.Name);
var thingsWithUniquePosition = listOfThings.FindDistinct(t => t.Position);
var thingsWithUniqueHeight = listOfThings.FindDistinct(t => t.Height);
You would like to write code like:
foreach var property in Thing.Properties
{
IEnumerable<IGrouping<string, thing>> thingQuery2 = from t in listOfThings
group t by t.property;
List<thing> listOfThingsFound = new List<thing>();
foreach (var thingGroup in thingQuery2)
{
if (thingGroup.Count() == 1)
{
foreach (thing thing in thingGroup) // there is only going to be 1
listOfThingsFound.Add(thing);
}
}
...
}
You can only do that through Reflection and that is something you should stay away from. The only thing I can think of is store the properties in some kind of collection, like a dictionary and iterate that.
I just noticed that Gabe has provided the same answer I was about to post. I thought I would post this anyway just to reinforce that this answer is a good strong use of LINQ. Please accept Gabe's answer rather than this one. Well done Gabe!
public static IEnumerable<T> WhereUniqueByKey<T, P>(
this IEnumerable<T> #this, Func<T, P> keySelector)
{
return #this
.GroupBy(keySelector)
.Where(gt => gt.Count() == 1)
.SelectMany(gt => gt, (_, t) => t);
}
As per Gabe's answer, my function is an extension method, and it needs to be defined in a static class. The only real difference between our answers is that Gabe has used the LINQ query syntax and I used direct LINQ method calls. The result is the same and so is the usage:
var thingsWithUniqueName = listOfThings.WhereUniqueByKey(t => t.Name);
var thingsWithUniquePosition = listOfThings.WhereUniqueByKey(t => t.Position);
var thingsWithUniqueHeight = listOfThings.WhereUniqueByKey(t => t.Height);

Categories