Convert IEnumerable<X> to List<Y> - c#

i have, (in Linq), an IEnumerable of type Client.
Now i have to return a Generic List of type ClientVM.
ClientVM is a subset of Client (not a subtype or anything), and i can't get it to work.
Below is the code i have so far, but it doesn't work this way.
But maybe this code can give you an addition to my post to specify what i want to do:
clientVMs = clients.ToList().ConvertAll(new ClientVM( z => z.Reference, z=>z.Fullname ));
clientVMs is a generic List<ClientVM>, class ClientWM has a constructor that takes the two properties, clients is the IEnumerable<Client>
And, offtopic, the compiler messages when you're dealing with Generics aren't readible for humans, imho.

Maybe something like this?
var clientVMs = clients.Select(c => new ClientVM(c.Reference, c.Fullname))
.ToList();

You've got the wrong syntax for the delegate inside ConvertAll:
clientVMs = clients.ToList().ConvertAll(z => new ClientVM( z.Reference, z.Fullname ));

Your lambda expressions are misplaced. You probably want:
var clientVMs = clients.ToList().ConvertAll(
client => new ClientVM(client.Reference, client.Fullname));

clients.ToList().Select(new ClientVM{ z => z.Reference, z=>z.Fullname }).ToList();

Related

Nested .SelectMany statements in C# / LINQ

I have the below code which works perfectly fine:
return from fa in [...]
where fa.Flows.Any()
from f in fa.Flows
select new Flow(f.Id, f.DealingDate, f.NetCashUsd, fa.Account);
As you can see, I need Account from fa deeper down in the second select.
However I'm required to use ".Selects" instead, and this (equivalent I thought) does not work:
return [...]
.Where(fa => fa.Flows.Any())
.SelectMany(fa => fa.Flows)
.Select(f => new Flow(f.Id, f.DealingDate, f.NetCashUsd, fa.Account));
The issue here is obvious, the second .Select doesn't "know" the fa stuff anymore so can't get to Account.
(As you can probably tell by now, an "fa" has one Account and multiple Flows, and I want to turn them into "Flow"s which all have the Account assigned to them as well.)
How can I solve this using only the "." statements? I looked into different GroupBys as well but couldn't make them work either.
Thank you!
An overload of SelectMany uses an extra argument (result selector) in which you can define the returned objects. In this result selector you have access to both source item and sub item:
[....]
.SelectMany(fa => fa.Flows, (fa,f)=> new Flow(f.Id, f.DealingDate, f.NetCashUsd, fa.Account));
The equivalent would be to create an intermediate anonymous type to hold both objects.
return [...]
.SelectMany(fa => fa.Flows.Select(f => new { f, fa}))
.Select(x => new Flow(x.f.Id, x.f.DealingDate, x.f.NetCashUsd, x.fa.Account));
Also you don't need the Where since an empty Flows will just result in no selected items.

Possible to order a dynamic type?

Say I have the code below:
dynamic myData = GetMyData();
foreach(dynamic d in myData.data)
{
Console.WriteLine(d.name);
}
How could I writeout all of the names in alphabetical order? If I were using something like List<MyClass> i would just use myData.OrderBy(t => t.name), but this does not seem to work when I'm using a dynamic type.
Any suggestions to how I can order these values?
Enumerable.OrderBy(myData, (Func<dynamic, dynamic>)(t => t.name));
That should return the same as myData.OrderBy(t => t.name) would normally.
Since OrderBy is an extension method, it won't work on dynamic types. See this answer.
This might work for you:
IEnumerable<dynamic> sequence = Enumerable.Cast<dynamic>(myData);
foreach (var result in sequence.OrderBy(x => x.name))
{
Console.WriteLine(result.name);
}
Basically after the call to Cast<dynamic> and the conversion to IEnumerable<dynamic>, you can do what you like as a sequence rather than a single value.
Not that I've actually tried the above, but I believe it should work.

Sort List in C#

So I have this C# list:
List<DatsWussup.Models.JQGridMessage> gridMessages = new List<DatsWussup.Models.JQGridMessage>();
Each JQGridMessage has a property called age. What's the quickest and most efficient way to sort this list by age (youngest first). Age is an int.
Thanks!
The List<T> class has a Sort method which can be used to in place sort the data. One overload takes a Comparison delegate that can be implemented via an anonymous function. For example
gridMessages.Sort((x, y) => x.Age.CompareTo(y.Age));
Use Linq:
var sortedEnumerable = gridMessages.OrderBy(m => m.Age);
This will return a new IEnumerable sorted by age.
gridMessages.Sort((m1, m2) => m1.Age.CompareTo(m2.Age));
Can you use:
gridMessages = gridMessages.OrderBy(x => x.age).toList();

NHibernate 3 LINQ - how to create a valid parameter for Average()

Say I have a very simple entity like this:
public class TestGuy
{
public virtual long Id {get;set;}
public virtual string City {get;set;}
public virtual int InterestingValue {get;set;}
public virtual int OtherValue {get;set;}
}
This contrived example object is mapped with NHibernate (using Fluent) and works fine.
Time to do some reporting. In this example, "testGuys" is an IQueryable with some criteria already applied.
var byCity = testGuys
.GroupBy(c => c.City)
.Select(g => new { City = g.Key, Avg = g.Average(tg => tg.InterestingValue) });
This works just fine. In NHibernate Profiler I can see the correct SQL being generated, and the results are as expected.
Inspired by my success, I want to make it more flexible. I want to make it configurable so that the user can get the average of OtherValue as well as InterestingValue. Shouldn't be too hard, the argument to Average() seems to be a Func (since the values are ints in this case). Easy peasy. Can't I just create a method that returns a Func based on some condition and use that as an argument?
var fieldToAverageBy = GetAverageField(SomeEnum.Other);
private Func<TestGuy,int> GetAverageField(SomeEnum someCondition)
{
switch(someCondition)
{
case SomeEnum.Interesting:
return tg => tg.InterestingValue;
case SomeEnum.Other:
return tg => tg.OtherValue;
}
throw new InvalidOperationException("Not in my example!");
}
And then, elsewhere, I could just do this:
var byCity = testGuys
.GroupBy(c => c.City)
.Select(g => new { City = g.Key, Avg = g.Average(fieldToAverageBy) });
Well, I thought I could do that. However, when I do enumerate this, NHibernate throws a fit:
Object of type 'System.Linq.Expressions.ConstantExpression' cannot be converted to type 'System.Linq.Expressions.LambdaExpression'.
So I am guessing that behind the scenes, some conversion or casting or some such thing is going on that in the first case accepts my lambda, but in the second case makes into something NHibernate can't convert to SQL.
My question is hopefully simple - how can my GetAverageField function return something that will work as a parameter to Average() when NHibernate 3.0 LINQ support (the .Query() method) translates this to SQL?
Any suggestions welcome, thanks!
EDIT
Based on the comments from David B in his answer, I took a closer look at this. My assumption that Func would be the right return type was based on the intellisense I got for the Average() method. It seems to be based on the Enumerable type, not the Queryable one. That's strange.. Need to look a bit closer at stuff.
The GroupBy method has the following return signature:
IQueryable<IGrouping<string,TestGuy>>
That means it should give me an IQueryable, all right. However, I then move on to the next line:
.Select(g => new { City = g.Key, Avg = g.Average(tg => tg.InterestingValue) });
If I check the intellisense for the g variable inside the new { } object definition, it is actually listed as being of type IGrouping - NOT IQueryable>. This is why the Average() method called is the Enumerable one, and why it won't accept the Expression parameter suggested by David B.
So somehow my group value has apparently lost it's status as an IQueryable somewhere.
Slightly interesting note:
I can change the Select to the following:
.Select(g => new { City = g.Key, Avg = g.AsQueryable<TestGuy>().Average(fieldToAverageBy) });
And now it compiles! Black magic! However, that doesn't solve the issue, as NHibernate now doesn't love me anymore and gives the following exception:
Could not parse expression '[-1].AsQueryable()': This overload of the method 'System.Linq.Queryable.AsQueryable' is currently not supported, but you can register your own parser if needed.
What baffles me is that this works when I give the lambda expression to the Average() method, but that I can't find a simple way to represent the same expression as an argument. I am obviously doing something wrong, but can't see what...!?
I am at my wits end. Help me, Jon Skeet, you're my only hope! ;)
You won't be able to call a "local" method within your lambda expression. If this were a simple non-nested clause, it would be relatively simple - you'd just need to change this:
private Func<TestGuy,int> GetAverageField(SomeEnum someCondition)
to this:
private Expression<Func<TestGuy,int>> GetAverageField(SomeEnum someCondition)
and then pass the result of the call into the relevant query method, e.g.
var results = query.Select(GetAverageField(fieldToAverageBy));
In this case, however, you'll need to build the whole expression tree up for the Select clause - the anonymous type creation expression, the extraction of the Key, and the extraction of the average field part. It's not going to be fun, to be honest. In particular, by the time you've built up your expression tree, that's not going to be statically typed in the same way as a normal query expression would be, due to the inability to express the anonymous type in a declaration.
If you're using .NET 4, dynamic typing may help you, although you'd pay the price of not having static typing any more, of course.
One option (horrible though it may be) would be try to use a sort of "template" of the anonymous type projection expression tree (e.g. always using a single property), and then build a copy of that expression tree, inserting the right expression instead. Again, it's not going to be fun.
Marc Gravell may be able to help more on this - it does sound like the kind of thing which should be possible, but I'm at a loss as to how to do it elegantly at the moment.
Eh? the parameter to Queryable.Average is not Func<T, U>. It's Expression<Func<T, U>>
The way to do this is:
private Expression<Func<TestGuy,int>> GetAverageExpr(SomeEnum someCondition)
{
switch(someCondition)
{
case SomeEnum.Interesting:
return tg => tg.InterestingValue;
case SomeEnum.Other:
return tg => tg.OtherValue;
}
throw new InvalidOperationException("Not in my example!");
}
Followed by:
Expression<Func<TestGuy, int>> averageExpr = GetAverageExpr(someCondition);
var byCity = testGuys
.GroupBy(c => c.City)
.Select(g => new { City = g.Key, Avg = g.Average(averageExpr) });

LINQ without IEqualityComparer implementation

I have 2 collection with different classes. MyClass1 - Name,Age,etc MyClass2 - Nick, Age, etc
I want to find except of this collections. Something like
list1.Exept(list2, (l1,l2) => l1.Name==l2.Nick);
But i cannt write this code and need to implement my own comparer class with IEqualityComparer interface and it's looking very overhead for this small task. Is there any elegant solution?
Except really doesn't work with two different sequence types. I suggest that instead, you use something like:
var excludedNicks = new HashSet<string>(list2.Select(x => x.Nick));
var query = list1.Where(x => !excludedNicks.Contains(x.Name));
(Note that this won't perform the "distinct" aspect of Except. If you need that, please say so and we can work out what you need.)
Well, build a set of all the nicknames, then run against that.
var nicknames = new HashSet<string>(list2.Select(l2 => l2.Nick));
var newNames = from l1 in list1
where !nicknames.Contains(l1.Name)
select l1;

Categories

Resources