Lambda expression to grab all values inside a Dictionary - c#

I have a LINQ query which returns all the values inside a Dictionary, conditional upon something:
var apps =
from entry in shape.Decorators
where entry.Value == DecoratorLayoutStyle.App
select entry.Key;
shape.Decorators is a
Dictionary<Shape, DecoratorLayoutStyle>
Is there something terser, and/or can I use a combination of lambdas or something?

var apps = shape.Decorators
.Where(x=>x.Value == DecoratorLayoutStyle.App)
.Select(x=>x.Key);
I think yours is just as fine.

That looks plenty terse to me, I guess you could use the extension functions instead of the from/select linq syntax, but that wouldn't be too different.
More imporantly, I'm not sure you want terser. The current format is very readable, and clearly documents exactly what you're trying to do.

var apps = shape.Decorators
.Where(e => e.Value == DecoratorLayoutStyle.App)
.Select(e => e.Key);
Do you think this is terser?
Personally I prefer the query syntax when I have more than one LINQ operator all the operators I use can be translated to it.

var apps = Shape.Decorators.Where(x => x.Value == DecoratorLayoutStyle.App)
.Select(x => x.Key);

Just to be different.
var apps = shape.Decorators.Keys
.Where(k => shape.Decorators[k] == DecoratorLayoutStyle.App);

Related

Optimize Linq in C#

I have columns list in which I need to assign Isselected as true for all except for two columns. (Bug and feature). I have used this following code to achieve it and working fine, but is there any quick or easy way to achieve the same?
DisplayColumns.ToList().ForEach(a => a.IsSelected = true);
DisplayColumns.ToList().Where(a => a.ColumnName == "Bug" || a.ColumnName == "Feature").ToList().ForEach(a => a.IsSelected = false);
Thanks in advance
I have used this following code to achieve it and working fine, but is there any quick or easy way to achieve the same?
Well there's a cleaner way to achieve it in my view - just don't use lambdas etc at all:
foreach (var item in DisplayColumns)
{
item.IsSelected = item.ColumnName != "Bug" && item.ColumnName != "Feature";
}
You can make the decision in one go - it's false if the column name is either "bug" or "feature"; it's true otherwise. And you don't need to call ToList and use ForEach when the C# language has a perfectly good foreach loop construct for when you want to execute some code using each item in a collection.
I love LINQ - it's fantastic - but its sweet spot is querying (hence the Q) rather than manipulation. In this case only the ToList part is even part of LINQ - List<T>.ForEach was introduced in .NET 2.0, before LINQ.
Sure, you can assign the IsSelected at once.
DisplayColumns.ToList().ForEach(a => a.IsSelected = !(a.ColumnName == "Bug" || a.ColumnName == "Feature"));
Provided that DisplayColumns isn't a projection of an anonymous type (in which case the properties are not re-assignable), you'll be able to change the flag in a single pass iteration through the collection.
You can also use Contains to ease the comparison. At class scope:
private static readonly string[] _searches = new [] {"Bug", "Feature"}
In your method:
DisplayColumns
.ToList() // For List.ForEach, although not #JonSkeet's caveat re mutating in Linq
.ForEach(a => a.IsSelected = !_searches.Contains(a.ColumnName));
Edit
As others have mentioned, creation of a new list simply to gain access to .ForEach to change objects in the (original) collection is wasteful and changes will be lost on a collection of value types. Rather, iterate over the original collection with foreach (or even for).
Firstly you only need to call ToList() once when creating a collection from your IEnumerable.
doing this after each operator is costly and redundant.
Secondly just change your condition . all true except for the tow.
DisplayColumns.Where(a => a.ColumnName != "Bug" && a.ColumnName != "Feature").ForEach(a => a.IsSelected = true).ToList();
Edit :
I'm sorry i like a part john's answer since this can be a re occurring thing , or IsSelected could be a Nullable , any ways lets keep it as general as possible .
I also like Stuart's approach , with the collection ( i also thought of it but didn't want to confuse . so let's give the best of all worlds.
when using linq we are actually building an expression tree at the end of which we can choose to materialize into a collection.
there for _searchs can change and each time we materialize that expression we do it with the values currently in that collection , thous making our code much more general .
private static readonly string[] _searches = new [] {"Bug", "Feature"}
DisplayColumns.ForEach(a => a.IsSelected = !_searchs.Contains(a.ColumnName)).ToList();
I'm assuming ForEach is an Extension method for type IEnumrable
Maybe this:
tmp = DisplayColumns.ToList();
var res = tmp.Except(tmp.Where(a => a.ColumnName == "Bug" || a.ColumnName == "Feature"));
foreach(var x in res) x.IsSeleceted = true;
Without using foreach
DisplayColumns
.Select(s=> {
s.IsSelected = (s.ColumnName == "Bug" && s.ColumnName == "Feature");
return s;
});

LINQ - "Unable to create constant value of type [type]"?

Here's my error
Unable to create a constant value of type 'Courses.Model.Track'. Only primitive types or enumeration types are supported in this context.
and here's the code that causes it:
model.Courses = db.Courses
.Where(c => model.Tracks.Any(t => t.Certification.ID == certificate))
.ToList();
Also, model.Courses is IEnumerable<Courses>, and I'm not sure if that has anything to do with my problem.
Could anybody shed some light on this for me? Many thanks.
db.Courses is an IQueryable. While the syntax is virtually identical to the LINQ methods of IEnumerable, under the hood they're completely different.
The IQueryable code isn't actually exectued anywhere at all. It just creates a bunch of Expression objects that different query providers are able to use to do...whatever they want (in this case query a database). Those query providers need to specifically have code to handle any given type of input. There are some things that they either can't sensibly transform into a SQL query, or things that the programmers simply didn't think of or choose to handle (even if it might have a sensible SQL translation).
In sort, the query provider just doesn't know how to translate model.Tracks.Any(t => t.Certification.ID == certificate) into SQL.
You simply need to know what types of code is and isn't supported by the query provider that you're using and try to manipulate the code you have into something that it can handle. Something like this should work:
var certificates = model.Tracks.Select(t => t.Certification.ID).ToList();
model.Courses = db.Courses
.Where(c => certificates.Contains(c))
.ToList();
If this were C# code executing all of this in LINQ to objects the two queries would be identical, but to a query provider they're not. They simply have special support for knowing when they see List.Contains to map it to a SQL IN clause. They didn't add specific support for what you did in your first query.
Try using LINQ 'Where In', this should solve your problem:
var names = new string[] { "Alex", "Colin", "Danny", "Diego" };
var matches = from person in people
where names.Contains(person.Firstname)
select person;
OR
var matches = from person in people
where person.Firstname == "Alex" ||
person.Firstname == "Colin" ||
person.Firstname == "Danny" ||
person.Firstname == "Diego"
select person;
http://blogs.msdn.com/b/alexj/archive/2009/03/26/tip-8-writing-where-in-style-queries-using-linq-to-entities.aspx
My apologies for the almost unnecessary question, although #klugerama did help me figure it out.
There was a problem with this:
model.Courses = db.Courses
.Where(c => model.Tracks.Any(t => t.Certification.ID == certificate))
.ToList();
Until I changed it to this:
model.Courses = db.Courses
.Where(c => c.Track.Certification.ID == certificate)
.ToList();
Again, my apologies. Thanks to everyone for their input.

Lambda Max and Max and Max

Quick and probably easy Lambda question:
I have a restaurant with reviews.
I want to query for the one with the:
Max(AverageRating)
And the Max(ReviewCount)
And the Max(NewestReviewDate)
And the Min(DistanceAway)
Something like this:
var _Result = AllRestaurants
.Max(x => x.AverageRating)
.AndMax(x => x.ReviewCount)
.AndMax(x => x.NewestReviewDate)
.AndMin(x => x.DistanceAway);
Now, I know that is pseudo code. But it describes it perfectly!
Of course, in multiple statements, this is simple.
Just wondering if this is possible in one statement without killing the readability.
Thank you in advance. I know some of you love the query questions!
You can't have multiple maxes or mins, that doesn't make sense.
You'll need some kind of heuristic like:
.Max(x => x.AverageRating * x.ReviewCount - x.DaysSinceLastReview - x.DistanceAway)
Perhaps this would do?
var bestRestaurant = AllRestaurants
.OrderByDescending(r => r.AverageRating)
.ThenByDescending(r => r.ReviewCount)
.ThenByDescending(r => r.NewestReviewCount)
.ThenBy(r => r.DistanceAway)
.FirstOrDefault();
You'd need to change the order of the statements to reflect which is the most important.
An alternative to having some weighted heuristic is to order by AverageRating, then ReviewCount, then ...
Something like this should work:
var _Result = AllRestaurants
.OrderByDescending(x => x.AverageRating)
.ThenByDescending(x => x.ReviewCount)
.ThenByDescending(x => x.NewestReviewDate)
.ThenByDescending(x => x.DistanceAway);
// using *Descending so you get the higer-valued ones first
Consider something like this...
List<RestaurantRecord> _Restaurants;
public RestaurantRecord Best()
{
return _Restaurants.Where(
x =>
x.AverageRating >= _BestRating &&
x.ReviewCount >= _MinReviews &&
x.Distance <= _MaxDistance)
.GetFirstOrDefault();
}
That being said, using a lambda in this case will have maintainability consequences down the road. It would be a good idea to refactor this, such that if other criteria appear in the future (e.g.: smartphone access? Cuisine type?), your app can be more easily modified to adapt to those.
On that note, a slightly better implementation might be something like:
public RestaurantRecord Best()
{
IQueryable temp = _Restaurants.Clone();
temp = temp.Where( x => x.AverageRating >= _BestRating );
temp = temp.Where( x => x.ReviewCount >= _MinReviews );
// ...snip...
return temp.GetFirstOrDefault();
}
I hope this sets you on the right track. :)
If I'm understanding your question, I think the best approach is going to be writing individual statements as you mention...
var HighestRating = AllRestaurants.Max(x => x.AverageRating);
var HighestReviewCount = AllRestaurants.Max(x => x.ReviewCount);
var LatestReviewDate = AllRestaurants.Max(x => x.NewestReviewDate);
var ShortestDistanceAway = AllRestaurants.Min(x => x.DistanceAway);
Retrieving various maxes and mins from a single Linq query would get pretty messy and I'm not sure there'd be any advantage with efficiency, either.

how to select an item from generic list by linq

I have a LINQ query which contains a method GetInstanceForDatabase()
principlesList.Select(p => p.GetInstanceForDatabase()).ToList()
where
List<PrincipleInstance>() principlesList = ...
// (contains list of principle like "Manual Trades", "OPM", "Flora")
GetInstanceForDatabase() is a method which takes all other info about a principle (like manual trades).
My problem is that I want to sort out only principle like only "Manual Trades".
I want to put a where clause. I tried but it is fails.
To get a single item use:
query.First(x => x.property == "Manual Trades");
// or
query.FirstOrDefault(x => x.property == "Manual Trades");
var list = p.GetInstanceForDatabase().where(x => x.propertyName == "Manual Trades").ToList();
I'm sure you're GetInstanceForDatabase needs to return your collection that you then filter for the 'Manual Trades' but I can't really tell how you get your list of PrincipalInstances from the question.
This is the correct syntax of using Where in LINQ
principlesList.Select(p => p.GetInstanceForDatabase()).Where(p => p.SomeProperty == "SomeValue").ToList();

How do I write this in LINQ?

Im sure there is some way to write this code in Linq. But I'm new to LINQ and don't know how to do it?
Here is the code:
List<IEntityMITARBEITER> leiter = new List<IEntityMITARBEITER>();
foreach (IEntityMITARBEITER mitarbeiter in mit)
{
foreach (IEntityREF_SCHULLUNG refs in refSchullung)
{
if (refs.Id_person == mitarbeiter.Id_mit)
{
leiter.Add(mitarbeiter);
}
}
}
leiter = mit.Where(x => refSchullung.Any(y => y.Id_person == x.Id_mit)).ToList();
(in case the co-worker doesn't appear in more courses.)
var selectedMitarbeiter = mit
.Where(m => refSchulung.Any(s => s.Id_person == m.Id_mit));
leiter.AddRange(selectedMitarbeiter.ToList());
why do you want re-write it using LINQ? What you have done is ok:it is very readable and much faster than it would be in LINQ
If you really want to use LINQ I can suggest you to install ReSharper which will convert it for you.
http://blogs.jetbrains.com/dotnet/2009/12/resharper-50-preview-loops-2-linq/
Using Join() would make a lot of sense in this case:
var joinQuery = mit.Join(refSchullung, x => x.Id_mit, x => x.Id_Person, (x, y) => x);
leiter.AddRange(joinQuery.ToList());
This will match up each element of the two sequences where the keys match, and select a single item for each case.

Categories