Is this loop possible to do using simple LINQ? - c#

I got a class called BG which has a property called Name Code.
I instantiate an object called bgList.
Now I am trying to get all the Code of the objects which have their 'Crop' property set to cropName.
I would like to convert the following working code to linq but for the life of me am unable to do that - am quite sure that I am missing something:
List<string> breedingGroupsAndRoles = new List<string>();
for (int i = 0; i < bgList.Count; i++)
{
if (bgList[i].Crop == cropName)
breedingGroupsAndRoles.Add(bgList.[i].Code);
}
The closest I came was this but it only nets me the first item:
breedingGroupsAndRoles.Add(bgrList.Find(c => c.Crop == cropName).Role);

List<string> breedingGroupsAndRoles = bgList
.Where(bg => bg.Crop == cropName)
.Select(bg => bg.Code)
.ToList();

Just for the sake of completeness, the Find method you tried calling on bgList is not part of LINQ, it's a member of the generic List class itself. It only returns the first element matched by the predicate you provide, which is why you were only getting one result. You probably wanted the FindAll method, which returns a list of all matching elements:
List<BG> breedingGroups = bgList.FindAll(c => c.Crop == cropName);
Note that this produces a list of matching BG instances rather than just their Role properties. Depending on how you're processing the results this may be sufficient, otherwise you'll still need LINQ or a loop and a second list to extract the Role values. In any case, an all-LINQ solution such #Tim Schmelter's is likely the better way to go.

Related

Ordering items that contain double quotes

How would I order a list of items where some of the items contain double quotes?
Advance
Access
“Chain free” deal
Binding
Broker
Doing this FaqData = repo.FaqData.OrderBy(q => q.Description) results in the following
“Chain free” deal
Advance
Access
Binding
Broker
Tried this as well
FaqData = repo.FaqData.OrderBy(q => q.QuestionDescription.Replace("”", ""))
FaqData = repo.FaqData.OrderBy(q => q.Description.Replace(#"""",""))
OrderBy calls the delegate once per item contained in the list being sorted. The delegate should return a value which, when compared to values obtained in the same way for other items in the list, will provide a value that can be sorted.
Typically the value returned in the delegate is a property of the listed item - but because its code, it could return anything you like, including values that arn't anything to do with the items in the list.
In this example instead of returning the list property ".Description" the code is returning a new string value derived from the ".Description" property. The derivation is simply to use the .net String.Replace to replace all double-quote values with an empty string.
This means the sorting algorithm sorts on the ".Description" with double-quotes removed.
This is not very efficient if you call this sorting code many times, and could easily be done differently; either by adding a new property to the class being sorted as such;
public string PlainTextDescription
{
get {
return this.Description.Replace(#"""","");
}
}
and sorting like this;
FaqData = repo.FaqData.OrderBy(q => q.PlainTextDescription)
or by pre-populating the PlainTextDescription field using the logic, but only when the .Description value changes; this would be much more efficient because the String.Replace would only be called once each time the .Description changes - with the example above, the String.Replace code must be called every time the sorter needs to evaluate the PlainTextDescription field, which means we're doing the String.Replace many times instead of once.
I'm thinking you not only want to ignore quotes but anything that isn't A-Z.
All you need to do is include a function to Where that strips out anything you don't want. To do that you can use a regular expression, like this:
var filtered = Regex.Replace(s, #"[^A-Za-z0-9]","")
Now to put it in a Where statement:
var tests = new[] { "Advance","Access","Binding","Broker",#"""Chain free"" deal","`Twas the night before Christams","#NotAllMen","Zenit","Quickly"};
var sorted = tests.OrderBy(s => Regex.Replace(s, #"[^A-Za-z0-9]",""));
foreach (var s in sorted)
{
Console.WriteLine(s);
}
Output:
Access
Advance
Binding
Broker
"Chain free" deal
#NotAllMen
Quickly
`Twas the night before Christams
Zenit
Code on DotNetFiddle

EF - A proper way to search several items in database

I have about 100 items (allRights) in the database and about 10 id-s to be searched (inputRightsIds). Which one is better - first to get all rights and then search the items (Variant 1) or to make 10 checking requests requests to the database
Here is some example code:
DbContext db = new DbContext();
int[] inputRightsIds = new int[10]{...};
Variant 1
var allRights = db.Rights.ToLIst();
foreach( var right in allRights)
{
for(int i>0; i<inputRightsIds.Lenght; i++)
{
if(inputRightsIds[i] == right.Id)
{
// Do something
}
}
}
Variant 2
for(int i>0; i<inputRightsIds.Lenght; i++)
{
if(db.Rights.Any(r => r.Id == inputRightsIds[i]);)
{
// Do something
}
}
Thanks in advance!
As other's have already stated you should do the following.
var matchingIds = from r in db.Rights
where inputRightIds.Contains(r.Id)
select r.Id;
foreach(var id in matchingIds)
{
// Do something
}
But this is different from both of your approaches. In your first approach you are making one SQL call to the DB that is returning more results than you are interested in. The second is making multiple SQL calls returning part of the information you want with each call. The query above will make one SQL call to the DB and return only the data you are interested in. This is the best approach as it reduces the two bottle necks of making multiple calls to the DB and having too much data returned.
You can use following :
db.Rights.Where(right => inputRightsIds.Contains(right.Id));
They should be very similar speeds since both must enumerate the arrays the same number of times. There might be subtle differences in speed between the two depending on the input data but in general I would go with Variant 2. I think you should almost always prefer LINQ over manual enumeration when possible. Also consider using the following LINQ statement to simplify the whole search to a single line.
var matches = db.Rights.Where(r=> inputRightIds.Contains(r.Id));
...//Do stuff with matches
Not forget get all your items into memory to process list further
var itemsFromDatabase = db.Rights.Where(r => inputRightsIds.Contains(r.Id)).ToList();
Or you could even enumerate through collection and do some stuff on each item
db.Rights.Where(r => inputRightsIds.Contains(r.Id)).ToList().Foreach(item => {
//your code here
});

How do I write a C# lambda returning "true" at all times most elegantly?

I want to invoke Queryable.Where() and get all elements. There's no version of Where() that works without a predicate function. So I have to right this:
var result = table.Where( x => true );
and it works but that feels really stupid to me - x is never used, and there's no "transformation" for the => "arrow" symbol.
Is there a more elegant solution?
You can use the following, which is more elegant:
var result = table;
You could also omit result completely, and use table directly.
Isn't table.Where(x=>true) essentially a noop? I mean, what is the point? You can do use _ instead of x though, which is idiomatic.
table.Where(_=> true);
But really, the following is what you are doing:
for (var item in table)
{
if (true) // your Where() clause..
{
yield item;
}
}
See how it doesn't really make sense?
table.Where( x => true ) is not "returning all elements". It simply returns an enumerable that has enough information to return some subset of elements when it is being enumerated upon. Until you enumerate it, no elements are "returned".
And since this subset is not even proper in this case (i.e. all elements are returned), this is essentially a no-op.
To enumerate over all elements, write a simple foreach, or use ToList or ToArray or if you don't care about actually returning any elements (and just want to enumerate, presumably for side-effects): table.All(x => true) or table.Any(x => false), or even just table.Count().
In this case you would not need to call Where because you are not filtering the Queryable.
If you still wish to call Where and you do this in many places you could define a static Func and reuse that:
public static Func<int, bool> t = ReturnTrue;
public static bool ReturnTrue(int i)
{
return true;
}
table.Where(t);
If you're trying to get a copy of the contents of table instead of a reference,
var result = table.ToList();
but it's not clear if that's really what you're trying to accomplish. Details?

List(T) RemoveAll() not working as intended...?

Let's say I have a list of User objects with two properties...ID and Name
List<User> lst = List<User>();
I fill it up with a bunch of Users. Ok, now I want to trim my list using RemoveAll() and this function.
private Boolean IsExisting(int id) {
//blah blah
return true;
//blah blah
return false;
}
So I use this statement:
gdvFoo.DataSource = lst.RemoveAll(t => IsExisting(t.ID));
It is my understanding that whenever IsExisting returns true that element should be removed from lst, but what happens, strangely enough, is it returns an integer?, not a truncated list and I received the following error message:
Data source is an invalid type. It must be either an IListSource, IEnumerable, or IDataSource.>
List.RemoveAll method
The method removes all matching instances from the list on which you called it. This modifies the existing list, rather than returning a new one.
The return value is the number of rows removed.
RemoveAll() returns the number of elements removed.
You need to do this:
lst.RemoveAll(t => IsExisting(t.ID));
gdvFoo.DataSource = lst;
The docs are very clear about what's going on:
Return Value
Type: System.Int32
The number of elements removed from the List .
Perhaps the following Linq would be more in line with your expectations?
lst.Except(t => IsExisting(t.ID)).ToList();
Instead of RemoveAll(), you could try using IEnumerable's filter where you would say something like :
var filteredList = lst.Where(item => IsExisting(item.Id))
This makes the code a little more easier to read and focusses on the objective of the task at hand, rather than how to look at implementing it.
List<T>.RemoveAll(...) has a return type of int which is not an IListSource, IEnumerable nor IDataSource
The RemoveAll is modifying the list and returning the number of items removed. You just set your datasource to the list in a second step.
lst.RemoveAll(t => IsExisting(t.ID));
gdvFoo.DataSource = lst;

Finding the index of a queue that holds a member of a containing object for a given value

I have a Queue that contains a collection of objects, one of these objects is a class called GlobalMarker that has a member called GlobalIndex.
What I want to be able to do is find the index of the queue where the GlobalIndex contains a given value (this will always be unique).
Simply using the Contains method shown below returns a bool. How can I obtain the queue index of this match?
RealTimeBuffer
.OfType<GlobalMarker>()
.Select(o => o.GlobalIndex)
.Contains(INT_VALUE);
If you need the index then perhaps you are using the wrong collection type. A queue is not designed to support random access (like an array or a List<T>). If you need random access then perhaps you should be using a type that implements IList<T>.
Queue's don't provide an interface that returns the index of a matching element, neither, unfortunately does LINQ.
You may want to consider using a List<> or Array if you need such methods. However, it is possible to treat the Queue as an IEnumerable and roll your own implementation - alternatively you could create a List from the queue and use IndexOf:
RealTimeBuffer.OfType<GlobalMarker>()
.Select(o => o.GlobalIndex).ToList().IndexOf( INT_VALUE );
If all Items in your collection are of type GlobalMarker, it is better to use ILis<>
List<GlobalMarker> RealTimeBuffer = new List<GlobalMarker>();
GlobalMarker globalMarker =
RealTimeBuffer.SingleOrDefault(o => o.GlobalIndex == 1);
If they are not All the same Type, You may use IList, but keep in mind, It is not a recommended approach. Here is a sample which will return GlobalMarker object
ArrayList RealTimeBuffer = new ArrayList();
RealTimeBuffer.Add(new GlobalMarker(){ GlobalIndex = 3 });
GlobalMarker globalMarker =
RealTimeBuffer.OfType<GlobalMarker>().FirstOrDefault(o => o.GlobalIndex == INT_VALUE);

Categories

Resources