Need help understanding .Select method C# - c#

I am having difficulties understandting what type of statement this is and how to use the .select method.
var lines = System.IO.File.ReadLines(#"c:\temp\mycsvfil3.csv")
.Select(l => new
{
myIdentiafication= int.Parse(l.Split(',')[0].Trim()),
myName= l.Split(',')[1].Trim()
}
).OrderBy(i => i.Id);
any help is appreciated!

The Enumerable.Select method is an extension method for an IEnumerable<T> type. It takes a Func<TSource, TResult> that allows you to take in your IEnumerable<T> items and project them to something else, such as a property of the type, or a new type. It makes heavy use of generic type inference from the compiler to do this without <> everywhere.
In your example, the IEnumerable<T> is the string[] of lines from the file. The Select func creates an anonymous type (also making use of generic type inference) and assigns some properties based on splitting each line l, which is a string from your enumerable.
OrderBy is another IEnumerable<T> extension method and proceeds to return an IEnumerable<T> in the order based on the expression you provide.
T at this point is the anonymous type from the Select with two properties (myIdentiafication and myName), so the OrderBy(i => i.Id) bit won't compile. It can be fixed:
.OrderBy(i => i.myIdentiafication);

This is a LINQ query. Enumerable.Select projects each line from file into anonymous object with properties myIdentiafication and myName. Then you sort sequence of anonymous objects with Enumerable.OrderBy. But you should select property which exists in anonymous object. E.g. myIdentiafication because there is no id property:
var lines = File.ReadLines(#"c:\temp\mycsvfil3.csv") // get sequence of lines
.Select(l => new {
myIdentiafication = int.Parse(l.Split(',')[0].Trim()),
myName= l.Split(',')[1].Trim()
}).OrderBy(i => i.myIdentiafication);
NOTE: To avoid parsing each line twice, you can use query syntax with introducing new range variables:
var lines = from l in File.ReadLines(#"c:\temp\mycsvfil3.csv")
let pair = l.Split(',')
let id = Int32.Parse(pair[0].Trim())
orderby id
select new {
Id = id,
Name = pair[1].Trim()
};

From each string returned by ReadLines create an anonymous object with two properties (myIdentiaficiation and myName). Within the Select the context variable l represents a single line from the set returned by ReadLines.

Related

Cannot be inferred from the usage. Try specifying the type arguments explicitly

I want to make a projection as a performance wise but the select part returns an anonymous type and I can't to make required mapping.
var jobDegreesQuery = _context.JOBDEGREEs.AsQueryable().Select(d=> new {d.DEGREE_CODE,d.DEGREE_NAME });
if (!String.IsNullOrWhiteSpace(name))
jobDegreesQuery = jobDegreesQuery.Where(c => c.DEGREE_NAME.Contains(name));
var jobDegreeDTOs = jobDegreesQuery
.ToList()
.Select(Mapper.Map<JOBDEGREE, JobDegreeDTO>); //The error
The type arguments for method 'Enumerable.Select(IEnumerable, Func)' cannot be
inferred from the usage. Try specifying the type arguments explicitly.
How can I do the projection and map to DTO Successfully ?
As I understand you want to map JOBDEGREEs to JobDegreeDTO. You are first selecting it as anonymous type, so I think AutoMapper can not map because you are giving anon. type.
Change your code as below it will perform better:
IQueryable<JOBDEGREEs> jobDegreesQuery = _context.JOBDEGREEs; // it is already queryable
if (!String.IsNullOrWhiteSpace(name))
jobDegreesQuery = jobDegreesQuery.Where(c => c.DEGREE_NAME.Contains(name));
var jobDegreeDTOs = jobDegreesQuery
//.Select(d=> new {d.DEGREE_CODE,d.DEGREE_NAME }) // do you need this?
.Select(d => Mapper.Map<JOBDEGREE, JobDegreeDTO>(d)); // here you can give any expression
.ToList()
What is the result of your ToList()? It is a List of objects of some anonymous class, that contains data extracted from your sequence of JobDegrees
Whenever you want to use Enumerable.Select on a sequence of objects, you'll first have to name an identifier that represents one element of your sequence. This identifier is the part before the =>. After the => you'll write the code to return one object using this input identifier.
This is a difficult way to say something like:
IEnumerable<Person> myPersons = ...
var firstNames = myPersns.Select(person => person.FirstName);
Here the person before the => represents one item of your collection of Persons. Hence person seems a proper name for this identifier.
If you want you can use any identifier to identify a person, although not all identifiers will improve readability:
var firstNames = myPersns.Select(x => x.FirstName);
When using LINQ and entity framework it is good practice to identify collections with plural nouns and elements of collections with singular nouns.
After the => you write some code that uses this input person to return exactly one object. In this example the FirstName of the person.
Back to your question
The result of your ToList is a sequence of objects with a DegreeCode and a DegreeName.
If you want to convert every object in your sequence into one other object (this is called projection), you'll have to identify one object of your sequence before the '=>'.
For example
...ToList()
.Select(extractedDegreeData => ...)
Here, every extractedDegreeData corresponds with one element of your list.
Now what do you want to do with one such extractedDegreeData? You want to return the return value of Mapper.Map<JOBDEGREE, JobDegreeDTO>(extractedDegreeData).
Therefore your code should be like:
...ToList()
.Select(extractedDegreeData => Mapper.Map<JOBDEGREE, JobDegreeDTO>(extractedDegreeData));
Advice:
While constructing your LINQ query, don't use functions like ToList, or any other functions that does not return IEnumerable<TResult>, it is a waste of processing power. What if after your Select you would have put Take(2)? What a waste to create the complete list of 1000 elements if you only wanted the first two!
Therefore functions like ToList, FirstOrDefault, Max, Count should always be the last in your linq query.
Finally: dbContext.JobDegrees is a DbSet<JobDegree>, which implements IQueryable<JobDegree>, hence there is no need to use AsQueryable.

Custom Extension Method in Linq-EF Equivalent to SQL "IN" Clause

I am trying to create a custom Linq to Entities extension method which takes a comma-delimited string, converts it to an array, then using IEnumerable<string>.Contains to generate the equivalent of a SQL IN clause.
Easy enough when you always know the table/entity and its column/property that you want to apply this filter to. The challenge is that I want to be able to use this extension method on any entity or property.
This is how far I've come:
public static IQueryable<TSource> CustomInClause<TSource>(this IQueryable<TSource> myQuery, Expression<Func<TSource, string>> colExpression, string filterCriteria)
{
string[] myArray = filterCriteria.Split(",", StringSplitOptions.RemoveEmptyEntries);
//Various other operations here..............
if (myArray.Length > 0)
{
myQuery = myQuery.Where(b => myArray.Contains(colExpression));
}
return myQuery;
}
As you can see, I am trying to use colExpression as a dynamic expression which will be the equivalent of x => x.SomeColumn where SomeColumn could be any string/varchar column.
I would then implement this extension like this:
var q = context.SomeTable.CustomInClause(f => f.SomeColumn, someString);
var q2 = context.OtherTable.CustomInCluse(f => f.OtherColumn, otherString);
Right now I get this error:
'string[]' does not contain a definition for 'Contains' and the best
extension method overload
'ParallelEnumerable.Contains>>(ParallelQuery>>,
Expression>)' requires a receiver of type
'ParallelQuery>>'
I'm not quite sure how to use a parallel query in this instance, or if there is another solution. Any ideas?
You have to build Contains call as part of expression in where clause
var myArray = filterCriteria.Split(",".ToCharArray(), StringSplitOptions.RemoveEmptyEntries)
.ToList();
var containsExp = Expression.Call(Expression.Constant(myArray),
"Contains", null, colExpression.Body);
if (myArray.Count > 0)
{
myQuery = myQuery.Where(Expression.Lambda<Func<TSource, bool>>
(containsExp, colExpression.Parameters));
}
return myQuery;
List is better than Array in this case, because list has Contains function and Array has only extension

Return an IQueryable<dynamic> to filter in the parent method

For example, I have this code:
IQueryable<MyModel> q = new List<MyModel>().AsQueryable(); // this is just an example, this is obviously not a list
var query = from item in q select new { item.Property };
var oneItem = query.FirstOrDefault(x => x.SomeProperty == somevalue);
var allItems = query.ToArray();
Now in a bit more complex situation, I need to get oneItem and allItems in two different methods. So to follow DRY, i'd like to move my query to a private method and then in the consuming ones just call this.GetQuery().FirstOrDefault() or .ToArray() as required.
However, when I try to have the method as IQueryable<dynamic> I get the 'An expression tree may not contain a dynamic operation' error. If I change it to IQueryable<object> then my filtering in the oneItem doesn't work.
You need to return
IQueryable<MyObject>
You can make your methods/classes dry by using genrics eg
IQuerable<T> GetQueryable()
Then the consumer can specify what T should be and your away.
You can't use dynamic with linq. See here to understand why.
For two methods to communicate they must understand the same type so you really want to project into a named type.
If you insist on using dynamic programming it can be done but you will need a lot of casting because dynamic is not a type but just a way of treating object:
IQueryable<MyModel> q = new List<MyModel>().AsQueryable(); // this is just an example, this is obviously not a list
IQueryable<object> query = from item in q select (object)new { item.Property };
var oneItem = query.FirstOrDefault(x => ((dynamic)x).SomeProperty == somevalue);
object[] allItems = query.ToArray();

Making a list distinct in C#

In C#, I have an object type 'A' that contains a list of key value pairs.
The key value pairs is a category string and a value string.
To instantiate object type A, I would have to do the following:
List<KeyValuePair> keyValuePairs = new List<KeyValuePair>();
keyValuePairs.Add(new KeyValuePair<"Country", "U.S.A">());
keyValuePairs.Add(new KeyValuePair<"Name", "Mo">());
keyValuePairs.Add(new KeyValuePair<"Age", "33">());
A a = new A(keyValuePairs);
Eventually, I will have a List of A object types and I want to manipulate the list so that i only get unique values and I base it only on the country name. Therefore, I want the list to be reduced to only have ONE "Country", "U.S.A", even if it appears more than once.
I was looking into the linq Distinct, but it does not do what I want because it I can't define any parameters and because it doesn't seem to be able to catch two equivalent objects of type A. I know that I can override the "Equals" method, but it still doesn't solve the my problem, which is to render the list distinct based on ONE of the key value pairs.
To expand upon Karl Anderson's suggestion of using morelinq, if you're unable to (or don't want to) link to another dll for your project, I implemented this myself awhile ago:
public static IEnumerable<T> DistinctBy<T, U>(this IEnumerable<T> source, Func<T, U>selector)
{
var contained = new Dictionary<U, bool>();
foreach (var elem in source)
{
U selected = selector(elem);
bool has;
if (!contained.TryGetValue(selected, out has))
{
contained[selected] = true;
yield return elem;
}
}
}
Used as follows:
collection.DistinctBy(elem => elem.Property);
In versions of .NET that support it, you can use a HashSet<T> instead of a Dictionary<T, Bool>, since we don't really care what the value is so much as that it has already been hashed.
Check out the DistinctBy syntax in the morelinq project.
A a = new A(keyValuePairs);
a = a.DistinctBy(k => new { k.Key, k.Value }).ToList();
You need to select the distinct property first:
Because it's a list inside a list, you can use the SelectMany. The SelectMany will concat the results of subselections.
List<A> listOfA = new List<A>();
listOfA.SelectMany(a => a.KeyValuePairs
.Where(keyValue => keyValue.Key == "Country")
.Select(keyValue => keyValue.Value))
.Distinct();
This should be it. It will select all values where the key is "Country" and concat the lists. Final it will distinct the country's. Given that the property KeyValuePairs of the class A is at least a IEnumerable< KeyValuePair< string, string>>
var result = keyValuePairs.GroupBy(x => x.Key)
.SelectMany(g => g.Key == "Country" ? g.Distinct() : g);
You can use the groupby statement. From here you can do all kind off cool stuf
listOfA.GroupBy(i=>i.Value)
You can groupby the value and then sum all the keys or something other usefull

LINQ query on PropertyInfo list

I have a sub class which is 'derived' (is that the right word) from a base class two levels up. I have a list of all the properties in this class (so that includes properties from the parent, and the parent of the parent). What I want is just the properties where the DeclaringType is "CrazyNinjaBadger" (i.e. only the properties from my sub - class).
I've tried this statement:
PropertyInfo[] properties = type.GetProperties().Select(x => x.DeclaringType.ToString() == "CrazyNinjaBadger");
But I just get
"Cannot implicitly convert type
'System.Collections.Generic.IEnumerable' to
'System.Reflection.PropertyInfo[]'.
Please can someone suggest a statement that will work?
Use Where to filter properties, and convert result to array:
PropertyInfo[] properties = type.GetProperties()
.Where(x => x.DeclaringType.ToString() == "CrazyNinjaBadger")
.ToArray();
Also I believe you want to use type name like this x.DeclaringType.Name == "CrazyNinjaBadger". Btw Select operator projects properties to sequence of boolean values in your case. So, your query actually returns IEnumerable<bool> with results of type string comparison to CrazyNinjaBadger.
PropertyInfo[] properties = type.GetProperties().Select(x => x.DeclaringType.ToString() == "CrazyNinjaBadger");
Select(...) returns an implemenetation of IEnumerable<T>. The compiler error is very explicit.
Another point is you want to filter properties. .Select(...) is for projecting an enumerable into another of the same or other type.
For example:
IEnumerable<string> strings = new string[] { "0", "1" };
// Converting the string enumerable to an enumerable of integers:
IEnumerable<int> integers = strings.Select(some => int.Parse(some));
// Also, convert each strings into an anonymous object!
IEnumerable<object> whoKnows = strings.Select(some => new { Value = some });
In order to filter an enumerable you need to use .Where(...).
In the other hand, x.DeclaringType.ToString() == "CrazyNinjaBadger" is correct but it should be x.DeclaringType.Name == "CrazyNinjaBadger" (you don't need to convert the type to string as Type has a property Name).
Finally I'd argue that you don't need to set the result in an array, you can just do this:
IEnumerable<PropertyInfo> properties =
type.GetProperties()
.Where(x => x.DeclaringType.Name == "CrazyNinjaBadger");
PropertyInfo[] properties = type.GetProperties()
.Select(x => x.DeclaringType.ToString() == "CrazyNinjaBadger")
.ToArray();
The ToArray() needs to be added to convert to Array...
You're getting that error because Select() always returns an IEnumerable.
Just Add .ToArray() at the end of the line to make it work.
Add ToArray() at the end of the line
PropertyInfo[] properties = type.GetProperties()
.Select(x => x.DeclaringType.ToString() == "CrazyNinjaBadger")
.ToArray();
You were almost there! Select returns an IEnumerable, but you're trying to set the value of a PropertyInfo array. All you need is an additional call to ToArray and you're there!
PropertyInfo[] properties = type.GetProperties().Select(x => x.DeclaringType.ToString() == "CrazyNinjaBadger").ToArray()

Categories

Resources