LINQ: How to remove element from IQueryable<T> - c#

How do you loop through IQueryable and remove some elements I don't need.
I am looking for something like this
var items = MyDataContext.Items.Where(x => x.Container.ID == myContainerId);
foreach(Item item in items)
{
if(IsNotWhatINeed(item))
items.Remove(item);
}
Is it possible? Thanks in advance

You should be able to query that further as in this
var filtered = items.Where(itm => IsWhatINeed(itm));
Also notice the subtle change in the boolean function to an affirmative rather than a negative. That (the negative) is what the not operator is for.

items = items.Where( x => !IsNotWhatINeed(x) );

var items = MyDataContext.Items.Where(x => x.Container.ID == myContainerId
&& !IsNotWhatINeed(x));
or
var items = MyDataContext.Items.Where(x => x.Container.ID == myContainerId)
.Where(x=> !IsNotWhatINeed(x));

The other answers are correct in that you can further refine the query with a 'where' statement. However, I'm assuming your query is a Linq2Sql query. So you need to make sure you have the data in memory before further filtering with a custom function:
var items = MyDataContext.Items.Where(x => x.Container.ID == myContainerId)
.ToList(); // fetch the data in memory
var itemsToRemove = items.Where(IsNotWhatINeed);
If you really want to extend the IQueryable, then the 'IsNotWhatINeed' function must be translated to something that Linq2Sql understands.

Try This:
var items = YourDataContext.Items.Where(x => x.Container.ID == myContainerId
&& !IsNotWhatYouNeed(x));

Related

Why Lambda expression returns System.Linq.Enumerable+WhereSelectEnumerableIterator`2 as a result

I'm getting following string as result while returning a list of string using lambda expression:
System.Linq.Enumerable+WhereSelectEnumerableIterator`2[HOrg.ServiceCatalog.Contracts.Models.IOfferProperty,System.String]
My code is:
IList<string> offerIds = new List<string>();
foreach (var offer in offerProperties)
{
offerIds.Add(offer
.Where(x => x.PropertyDefinitionId == propertyDefinitionId)
.Select(x => x.OfferId)
.ToString());
}
Within foreach loop, offer variable contains expected values. But when I make condition using lambda expression, it returns System.Linq.Enumerable+WhereSelectEnumerableIterator`2 as a result.
When I search for this, I got a few suggestions like,
Copying results of lambda expressions in to a seperate list
Use ToList() for lambda expression then assign it to a result variable
and more suggestion. But no answer is helpful for me.
Is anybody know what's wrong in this code?
Instead of converting sequence to String:
// How can .Net convert sequence into string? The only way is to return type name
// which is unreadable System.Linq.Enumerable+WhereSelectEn...
offer
.Where(x => x.PropertyDefinitionId == propertyDefinitionId)
.Select(x => x.OfferId)
.ToString()
Join the items into a string
// Join items into string with "; " delimiter, e.g. "1; 2; 3; 4"
offerIds.Add(string.Join("; ", offer
.Where(x => x.PropertyDefinitionId == propertyDefinitionId)
.Select(x => x.OfferId)));
If you expect a single result for every offer, try:
IList<string> offerIds = new List<string>();
foreach (var offer in offerProperties)
{
offerIds.Add(offer.Where(x => x.PropertyDefinitionId == propertyDefinitionId).Select(x => x.OfferId).FirstOrDefault()?.ToString());
}
It seems to me that you want a collection of offerIds as a string, where multiple are attached to the offerproperties.
If so, then you are looking for the addrange function. Also move your ToString() call inside the select statement, not after it.
IList<string> offerIds = new List<string>();
foreach (var offer in offerProperties)
{
offerIds.AddRange(offer.Where(x => x.PropertyDefinitionId == propertyDefinitionId).Select(x => x.OfferId.ToString()));
}
Now for each offer, a selection of OfferId-strings is added to your offerIds IList

how to use linq to retrieve values from a 2 dimensional generic list

I have a generic List List[int, myClass], and I would like to find the smallest int value, and retrieve the items from the list that match this.
I am generating this from another LINQ statement
var traysWithExtraAisles = (from t in poolTrays
where t.TrayItems.Select(i=>i.Aisle)
.Any(a=> ! selectedAisles.Contains(a))
select new
{
count= t.TrayItems.Select(i=>i.Aisle)
.Count(a=> !selectedAisles.Contains(a)),
tray=t
}).ToList();
this gives me my anonymous List of [count, Tray], but now I want to figure out the smallest count, and return a sublist for all the counts that match this.
Can anyone help me out with this?
var smallestGroup = traysWithExtraAisles
.GroupBy(x => x.count)
.OrderBy(g => g.Key)
.First();
foreach(var x in smallestGroup)
{
var poolTray = x.tray;
}
You can use SelectMany to "flatten" your list. Meaning, combine all of the lists into one, then take the Min. So;
int minimum = poolTrays.SelectMany(x => x).Min(x => x.TheIntegerIWantMinOf);
Will give you the smallest value contained in the sub lists. I'm not entirely sure this is what you're asking for but if your goal is simply to find the smallest element in the collection then I would scrap the code you posted and use this instead.
Right, I now realise this is actually incredibly easy to do with a bit more fiddling around. I have gone with
int minCount = traysWithExtraAisles.Min(x=>x.count);
var minAislesList = (from t in trayswithExtraAisles
where t.count==mincount
select t).ToList()
I imagine it is probably possible to do this in one statement
You can use GroupBy as answered by Tim... or OrderBy as follow:
var result = traysWithExtraAisles.OrderBy(x=>x.count)
.TakeWhile((x,i)=> i == 0 || x.count == traysWithExtraAisles[i-1]).count;

Is there a way to loop inside Where clause?

There are two lists "ObjlistA" and "ObjlistB".
var newList = from someObg i ObglistB
where [condition = true if some property of any element in the list ObjlistAA equals someObg's some property]
select someObg
Is there a way to loop inside the where clause, so that the obj's property can be compared with each and every element's property in the list?
Can anyone help me out the "Where" part?
Is this what you're looking for?
var newList = ObjlistB.Where(someObj => ObjlistA.Any(a => a.SomeProperty == someObj.SomeProperty))
where ObjlistA.Any(x => x.Property == someObj.Property)
you mean this?
var newList = from someObg in ObjlistB
where ObjlistA.Any(a => a.ID == someObg.ID)
select someObg;

Update all objects except one in a collection using Linq

Is there a way to do the following using Linq:
foreach (var c in collection)
{
if (c.Condition == condition)
{
c.PropertyToSet = value;
// I must also check I only set this value to one minimum and only one element.
}
else
{
c.PropertyToSet = otherValue;
}
}
To clarify, I want to iterate through each object in a collection and then update a property on each object except for one element of my collection that should updated to another value.
At this moment I use a counter to check I set my value to one and only one element of my collection. I removed it from this example to let people suggest other solutions.
The original question without exception in collection is here
EDIT
I ask this question because I'm not sure it's possible to do it with LinQ. so your answers comfort my opinion about LinQ. Thank you.
You can use .ForEach to make the change, and .Single to verify only one element matches the condition:
// make sure only one item matches the condition
var singleOne = collection.Single(c => c.Condition == condition);
singleOne.PropertyToSet = value;
// update the rest of the items
var theRest = collection.Where(c => c.Condition != condition);
theRest.ToList().ForEach(c => c.PropertyToSet = otherValue);
I don't suggest you to implement this with Linq. Why? Because Linq is for querying, not for modification. It can return you objects which match some condition, or objects which don't match. But for updating those objects you still need to use foreach or convert query results to list and use ForEach extension. Both will require enumerating sequence twice.
So, simple loop will do the job:
foreach (var c in collection)
{
c.PropertyToSet = (c.Condition == condition) ? value : otherValue;
}
collection.Where(x => <condition>).ToList().ForEach(x => <action>);
Hacky way to use LINQ if you persist to use:
var result = collection.Select(c =>
{
c.PropertyToSet = c.Condition == condition ? value : otherValue;
return c;
});
But my recommendation, don't do this, you code actually get the best approach, for more readability, you can change:
foreach (var c in collection)
c.PropertyToSet = c.Condition == condition ? value : otherValue;
You can use a ternary operator in conjunction with a linq statement:
collection.ToList().ForEach(c => c.PropertyToSet = c.Condition == condition ? value : otherValue);
However I woud just use a regular foreach here to avoid converting the collection to a list.
Well, you could do:
var itemToSetValue = collection.FirstOrDefault(c => c.Condition == condition);
if(itemToSetValue != null)
itemToSetValue.PropertyToSet = value;
// Depending on what you mean, this predicate
// might be c => c != itemToSetValue instead.
foreach (var otherItem in collection.Where(c => c.Condition != condition))
{
otherItem.PropertyToSet = otherValue;
}
Now of course that's not a pure LINQ solution, but pure LINQ solutions are not appropriate for modifying existing collections.

Selecting ListBox values against a List

I have a list:
var _books = new List<int> {233,5,20};
And a ListBox with a lot of books (their value is an item id)
How do I take this working code:
var t = from ListItem n in lbBooks.Items
where _books.Contains(int.Parse(n.Value))
select n;
foreach(ListItem i in t)
{
i.Selected = true;
}
and convert it to lambda:
lbBooks.Items.Cast<ListItem>()
.Where(n => _books.Contains(int.Parse(n.Value)))
.Select(n => n.Selected = true);
The best option? Don't. LINQ queries are supposed to be just that – queries. And queries aren't supposed to have side effects. Your approach using foreach is perfectly fine.
If you really wanted, you could create your own extension method ForEach(), similar to the one on List<T>, but I don't think that's a good idea.
Your not far off, you need to return the whole object and set Selected to true. Something like:
lbBooks.Items.Cast<ListItem>()
.Where(n => _books.Contains(int.Parse(n.Value)))
.Select(n => SetSelected(n));
The above .Select can be shortend to .Select(SetSelected); if you prefer
private ListItem SetSelected(ListItem listItem)
{
listItem.Selected = true;
return listItem
}
Additionally to save casting you could utilise the SetSelected to handle your casting once you have just the records you want. Your query would then become:
lbBooks.Items.Where(n => _books.Contains(int.Parse(n.Value)))
.Select(n => SetSelected(n));
private ListItem SetSelected(Item item)
{
ListItem result = item as ListItem;
result.Selected = true;
return result;
}
There is no build in ForEach extension method. You can create your own, but I do not see any problem with the code you have.
Cast to .ToList() and use the `ForEach' operator as so:
lbBooks.Items.Cast<ListItem>()
.Where(n => _books.Contains(int.Parse(n.Value)))
.Select(n => n).ToList().ForEach(n=> n.Selected=true);

Categories

Resources