Remove item from list based on condition - c#

I have a struct like this:
public struct stuff
{
public int ID;
public int quan;
}
and want to to remove the product where ID is 1.
I'm trying this currently:
prods.Remove(new stuff{ prodID = 1});
and it's not working.
THANKS TO ALL

If your collection type is a List<stuff>, then the best approach is probably the following:
prods.RemoveAll(s => s.ID == 1)
This only does one pass (iteration) over the list, so should be more efficient than other methods.
If your type is more generically an ICollection<T>, it might help to write a short extension method if you care about performance. If not, then you'd probably get away with using LINQ (calling Where or Single).

Using linq:
prods.Remove( prods.Single( s => s.ID == 1 ) );
Maybe you even want to use SingleOrDefault() and check if the element exists at all ...
EDIT:
Since stuff is a struct, SingleOrDefault() will not return null. But it will return default( stuff ), which will have an ID of 0. When you don't have an ID of 0 for your normal stuff-objects you can query for this ID:
var stuffToRemove = prods.SingleOrDefault( s => s.ID == 1 );
if( stuffToRemove.ID != 0 )
{
prods.Remove( stuffToRemove );
}

If you have LINQ:
var itemtoremove = prods.Where(item => item.ID == 1).First();
prods.Remove(itemtoremove)

prods.Remove(prods.Find(x => x.ID == 1));

Here is a solution for those, who want to remove it from the database with Entity Framework:
prods.RemoveWhere(s => s.ID == 1);
And the extension method itself:
using System;
using System.Linq;
using System.Linq.Expressions;
using Microsoft.EntityFrameworkCore;
namespace LivaNova.NGPDM.Client.Services.Data.Extensions
{
public static class DbSetExtensions
{
public static void RemoveWhere<TEntity>(this DbSet<TEntity> entities, Expression<Func<TEntity, bool>> predicate) where TEntity : class
{
var records = entities
.Where(predicate)
.ToList();
if (records.Count > 0)
entities.RemoveRange(records);
}
}
}
P.S. This simulates the method RemoveAll() that's not available for DB sets of the entity framework.

prods.Remove(prods.Single(p=>p.ID == 1));
you can't modify collection in foreach, as Vincent suggests

You can only remove something you have a reference to. So you will have to search the entire list:
stuff r;
foreach(stuff s in prods) {
if(s.ID == 1) {
r = s;
break;
}
}
prods.Remove(r);
or
for(int i = 0; i < prods.Length; i++) {
if(prods[i].ID == 1) {
prods.RemoveAt(i);
break;
}
}

A bit late to the game, however, a simple extension method can implement a RemoveAll on IList<T>. The trick is to loop over the collection in reverse order to avoid the extra logic of deleting the current item and having to try on the current index if removed. Also the reverse order prevents having to copy all the remaining items. Depending on the .NET version, this copy could be expensive.
public static int RemoveAll<T>(this IList<T> list, Predicate<T> match)
{
if (list == null) throw new ArgumentNullException("list");
if (match == null) throw new ArgumentNullException("match");
int count = 0;
for (int i = list.Count - 1; i >= 0; i--)
{
if (match(list[i]))
{
++count;
list.RemoveAt(i);
}
}
return count;
}

You could use Linq.
var prod = from p in prods
where p.ID != 1
select p;

Related

Update one object in collection using LINQ

I have object and collection in this object.
myObject.myCollection.Where(a => a.Id == Id).FirstOrDefault() = newMyCollection;
Unfortunately this didn't works
If I change single element, then work. For example:
myObject.myCollection.Where(a => a.Id == Id).FirstOrDefault().Id = newMyCollection.Id;
How to update all of object? Thanks for any help
I can do something like this:
myObject.myCollection.Remove(objectOfMyCollection);
myObject.MyCollection.Add(myNewCollection);
But: If my objectOfMyCollection is firstly, then my new object be last.
Try this
var customListItem2 = myObject.myCollection.Where(a => a.Id == Id).FirstOrDefault();
var index = myObject.myCollection.IndexOf(customListItem2);
if(index != -1)
myObject.myCollection[index] = newCustomListItem;
You can just query for a list and iterate over that:
foreach (var obj in myObject.myCollection.Where(a => a.Id == Id).ToList()) {
obj.Id = newMyCollection.Id;
}
To change only first index you can do something like this:
myObject.myCollection.Where(a => a.Id == Id).FirstOrDefault(a => { a.Id = newMyCollection.Id; return true; });
Which in plain English does :
- From myCollection get items Where item's Id is equal to Id
- From selected values get FirstItem and set it's Id to the new one
- Do not check the rest
Or even make it simplier :
myObject.myCollection.FirstOrDefault(a => { if(a.Id == Id) { a.Id = newMyCollection.Id; return true; } return false; });
You would have to replace the underlying references in your list which isn't possible with LINQ (which is designed for querying, not updating objects).
To do this with a list use an old-school for-loop:
for(int i = 0; i < myList.Count; i++)
{
if(myList[i].Id == Id)
{
myList[i] = newElement;
break;
}
}
Using a for- instead of a foreach is necessary here because you re-reference the instances in your list which is not possible using the latter.
Another approach was to create a temporary copy of your list, get the matching element there and it´s index in the list and replace the element at that index in the original list:
var tmp = myList;
foreach(var elem in tmp)
{
if(elem.Id == Id)
{
var index = myList.IndexOf(x => x.Id == Id);
myList[index] = newElement;
break;
}
}
If you want to replicate SQL UPDATE with JOIN and update one or more objects, you can use LINQ as below, this time using CollectionBase-derived objects. But you can use List<T>, etc
(from Type1 o1 in collection1
join Type2 o2 in collection2 on o1.Id equals o2.RelatedId
select new { O1 = o1, O2 = o2 }).
Count(item => (item.O1.SomeProperty = item.O2.SomeOtherProperty) == null);
A little hacky but it acts similar to SQL and does not mutate collections. YeA, it creates light objects that only hold reference to original collection items.

Grouping lists into groups of X items per group

I'm having a problem knowing the best way to make a method to group a list of items into groups of (for example) no more than 3 items. I've created the method below, but without doing a ToList on the group before I return it, I have a problem with it if the list is enumerated multiple times.
The first time it's enumerated is correct, but any additional enumeration is thrown off because the two variables (i and groupKey) appear to be remembered between the iterations.
So the questions are:
Is there a better way to do what I'm trying to achieve?
Is simply ToListing the resulting group before it leaves this method
really such a bad idea?
public static IEnumerable<IGrouping<int, TSource>> GroupBy<TSource>
(this IEnumerable<TSource> source, int itemsPerGroup)
{
const int initial = 1;
int i = initial;
int groupKey = 0;
var groups = source.GroupBy(x =>
{
if (i == initial)
{
groupKey = 0;
}
if (i > initial)
{
//Increase the group key if we've counted past the items per group
if (itemsPerGroup == initial || i % itemsPerGroup == 1)
{
groupKey++;
}
}
i++;
return groupKey;
});
return groups;
}
Here's one way to do this using LINQ...
public static IEnumerable<IGrouping<int, TSource>> GroupBy<TSource>
(this IEnumerable<TSource> source, int itemsPerGroup)
{
return source.Zip(Enumerable.Range(0, source.Count()),
(s, r) => new { Group = r / itemsPerGroup, Item = s })
.GroupBy(i => i.Group, g => g.Item)
.ToList();
}
Live Demo
I think you are looking for something like this:
return source.Select((x, idx) => new { x, idx })
.GroupBy(x => x.idx / itemsPerGroup)
.Select(g => g.Select(a => a.x));
You need to change your return type as IEnumerable<IEnumerable<TSource>>
The problem with using GroupBy() is that unless it somehow has knowledge under the hood that the input is ordered by key value, it has to read the entire sequence and allocate everything to its bucket before it can emit a single group. That's overkill in this case, since the key is a function of its ordinal position within the sequence.
I like the source.Skip(m).Take(n) approach, but that makes assumptions that items in source can be directly addressed. If that's not true or Skip() and Take() have no knowledge of the underlying implementation, then the production of each group is going to be an O(n/2) operation on the average, as it repeatedly iterates over source to produce the group.
This makes the overall partitioning operation, potentially quite expensive.
IF producing a group is an O(n/2) operation on the average, and
Given a group size of s, the production of approximately n/s groups is required,
Then the total cost of the operation is something like O(n2/2s), right?
So, I would do something this, an O(n) operation (feel free to use an IGrouping implementation if you'd like):
public static IEnumerable<KeyValuePair<int,T[]>> Partition<T>( this IEnumerable<T> source , int partitionSize )
{
if ( source == null ) throw new ArgumentNullException("source") ;
if ( partitionSize < 1 ) throw new ArgumentOutOfRangeException("partitionSize") ;
int i = 0 ;
List<T> partition = new List<T>( partitionSize ) ;
foreach( T item in source )
{
partition.Add(item) ;
if ( partition.Count == partitionSize )
{
yield return new KeyValuePair<int,T[]>( ++i , partition.ToArray() ) ;
partition.Clear() ;
}
}
// return the last partition if necessary
if ( partition.Count > 0 )
{
yield return new Partition<int,T>( ++i , items.ToArray() ) ;
}
}
.net Fiddle
Essentially you have an IEnumerable, and you want to group it into an IEnumerable of IGroupables which each contain the key as an index and the group as the values. Your version does seem to accomplish on the first pass, but I think that you can definitely stream line a little bit.
Using skip and take is the most desirable way to accomplish in my opinion, but the custom key for grouping is where there is an issue. There is a way around this which is to create your own class as a grouping template (seen in this answer: https://stackoverflow.com/a/5073144/1026459).
The end result is this:
public static class GroupExtension
{
public static IEnumerable<IGrouping<int, T>> GroupAt<T>(this IEnumerable<T> source, int itemsPerGroup)
{
for(int i = 0; i < (int)Math.Ceiling( (double)source.Count() / itemsPerGroup ); i++)
{
var currentGroup = new Grouping<int,T>{ Key = i };
currentGroup.AddRange(source.Skip(itemsPerGroup*i).Take(itemsPerGroup));
yield return currentGroup;
}
}
private class Grouping<TKey, TElement> : List<TElement>, IGrouping<TKey, TElement>
{
public TKey Key { get; set; }
}
}
And here is the demo in the fiddle which consumes it on a simple string
public class Program
{
public void Main(){
foreach(var p in getLine().Select(s => s).GroupAt(3))
Console.WriteLine(p.Aggregate("",(s,val) => s += val));
}
public string getLine(){ return "Hello World, how are you doing, this just some text to show how the grouping works"; }
}
edit
Alternatively as just an IEnumerable of IEnumerable
public static IEnumerable<IEnumerable<T>> GroupAt<T>(this IEnumerable<T> source, int itemsPerGroup)
{
for(int i = 0; i < (int)Math.Ceiling( (double)source.Count() / itemsPerGroup ); i++)
yield return source.Skip(itemsPerGroup*i).Take(itemsPerGroup);
}
This is based on Selman's Select with index idea, but using ToLookup to combine both the GroupBy and Select together as one:
public static IEnumerable<IEnumerable<TSource>> GroupBy<TSource>
(this IEnumerable<TSource> source, int itemsPerGroup)
{
return source.Select((x, idx) => new { x, idx })
.ToLookup(q => q.idx / itemsPerGroup, q => q.x);
}
The main difference though is that ToLookup actually evaluates results immediately (as concisely explained here: https://stackoverflow.com/a/11969517/7270462), which may or may not be desired.

How to optimize this LINQ expression w/ Where condition and calling Method?

I am seeking any advice or tips on the following method I have that is using LINQ to find a certain property in a Collection that is null and then go through the results (sub-list) and execute a method on another property from the same Collection.
private void SetRaises()
{
if (employeeCollection != null)
{
var noRaiseList = employeeCollection .Where(emp => emp.Raise == null).ToList();
foreach (var record in noRaiseList )
{
CalculateRaise(record);
}
}
}
public void CalculateRaise(Employee emp)
{
if (emp!= null)
emp.Raise = emp.YearsOfService * 100;
}
The part I don't like in the first method, SetRaises(), is the following snippet:
foreach (var record in noRaiseList )
{
CalculateRaise(record);
}
Is there a way to integrate that part into my LINQ expression directly, i.e. some extension method I am not aware of?
Thank you!
The first thing you could do would be: don't generate an intermediate list:
var pending = employeeCollection.Where(emp => emp.Raise == null);
foreach (var record in pending)
{
CalculateRaise(record);
}
which is identical to:
foreach (var record in employeeCollection.Where(emp => emp.Raise == null))
{
CalculateRaise(record);
}
This is now non-buffered deferred execution.
But frankly, the LINQ here isn't giving you much. You could also just:
foreach(var emp in employeeCollection)
{
if(emp.Raise == null) CalculateRaise(emp);
}
If you don't need list of employees without Raise you can do this in one line:
employeeCollection.Where(emp => emp.Raise == null).ToList().ForEach(x => x.Raise = x.YearsOfService * 100);
You could use the ForEach chain-method. But that's only sugar syntax.
employeeCollection.Where(emp => emp.Raise == null)
.ToList()
.ForEach(record => CalculateRaise(record))
It should be something like this:
var noRaiseList = employeeCollection .Where(emp => emp.Raise == null).ToList().ForEach(e=>e.Raise = e.YearsOfService * 100);

How can I iterate over a collection and change values with LINQ extension methods?

Lets say I have a collection of Messages which has the properties "UserID" (int) and "Unread" (bool).
How can I use LINQ extension methods to set Unread = false, for any Message in the collection in whose UserID = 5?
So, I know I can do something like:
messages.Any(m => m.UserID == 5);
But, how do I set the Unread property of each of those with an extension method as well?
Note: I know I should not do this in production code. I'm simply trying to learn some more LINQ-fu.
Actually, this is possible using only the built-in LINQ extension methods without ToList.
I believe that this will perform very similarly to a regular for loop. (I haven't checked)
Don't you dare do this in real code.
messages.Where(m => m.UserID == 5)
.Aggregate(0, (m, r) => { m.Unread = false; return r + 1; });
As an added bonus, this will return the number of users that it modified.
messages.Where(m => m.UserID == 5).ToList().ForEach(m => m.Unread = false);
Then submit the changes.
Standard LINQ extension methods doesn't include side effects aimed methods. However you can either implement it yourself or use from Reactive Extensions for .NET (Rx) like this:
messages.Where(m => m.UserID == 5).Run(m => m.Unread = false);
As there is no explicit extension method that does a ForEach, you are stuck with either using a secondary library, or writing the foreach statement on your own.
foreach (Message msg in messages.Where(m => m.UserID == 5))
{
msg.Unread = false;
}
If you really want to use a Linq statement to accomplish this, create a copy the collection using the ToList() method, accessing the ForEach() method of the List type:
messages.Where(m => m.UserID == 5).ToList().ForEach(m => m.Unread = false);
or place the side-effect in a Where() statement:
messages.Where(m =>
{
if (m.UserID == 5) { m.Unread = false; return true; }
return false;
});
In either case, I prefer to use the explicit foreach loop as it doesn't make unnecessary copies and is clearer than the Where hack.
With LINQ you can't because LINQ is a query language/extension. There is however a project called MoreLinq, which defines an extension method called ForEach which allows you to pass an action which will be performed on every element.
So, you could do with MoreLinq:
messages.Where(m => m.UserID == 5).ForEach(m => m.Unread = false);
Best Regards,
Oliver Hanappi
This answer is in the spirit of providing a solution. On could create an extension which does both the predicate (Where extension) to weed out the items and the action needed upon those items.
Below is an extension named OperateOn which is quite easy to write:
public static void OperateOn<TSource>(this List<TSource> items,
Func<TSource, bool> predicate,
Action<TSource> operation)
{
if ((items != null) && (items.Any()))
{
items.All (itm =>
{
if (predicate(itm))
operation(itm);
return true;
});
}
}
Here is it in action:
var myList = new List<Item>
{ new Item() { UserId = 5, Name = "Alpha" },
new Item() { UserId = 5, Name = "Beta", UnRead = true },
new Item() { UserId = 6, Name = "Gamma", UnRead = false }
};
myList.OperateOn(itm => itm.UserId == 5, itm => itm.UnRead = true);
Console.WriteLine (string.Join(" ",
myList.Select (itm => string.Format("({0} : {1})",
itm.Name,
itm.UnRead ))));
/* Outputs this to the screen
(Alpha : True) (Beta : True) (Gamma : False)
*/
...
public class Item
{
public bool UnRead { get; set; }
public int UserId { get; set; }
public string Name { get; set; }
}
You should be able to just do it in a Select(), remember the lambda is a shortcut for a function, so you can put as much logic in there as you want, then return the current item being enumerated. And... why exactly wouldn't you do this in production code?
messages = messages
.Select(m =>
{
if (m.UserId == 5)
m.Unread = true;
return m;
});

How to perform .Max() on a property of all objects in a collection and return the object with maximum value [duplicate]

This question already has answers here:
How to use LINQ to select object with minimum or maximum property value
(20 answers)
Closed 7 years ago.
I have a list of objects that have two int properties. The list is the output of another linq query. The object:
public class DimensionPair
{
public int Height { get; set; }
public int Width { get; set; }
}
I want to find and return the object in the list which has the largest Height property value.
I can manage to get the highest value of the Height value but not the object itself.
Can I do this with Linq? How?
We have an extension method to do exactly this in MoreLINQ. You can look at the implementation there, but basically it's a case of iterating through the data, remembering the maximum element we've seen so far and the maximum value it produced under the projection.
In your case you'd do something like:
var item = items.MaxBy(x => x.Height);
This is better (IMO) than any of the solutions presented here other than Mehrdad's second solution (which is basically the same as MaxBy):
It's O(n) unlike the previous accepted answer which finds the maximum value on every iteration (making it O(n^2))
The ordering solution is O(n log n)
Taking the Max value and then finding the first element with that value is O(n), but iterates over the sequence twice. Where possible, you should use LINQ in a single-pass fashion.
It's a lot simpler to read and understand than the aggregate version, and only evaluates the projection once per element
This would require a sort (O(n log n)) but is very simple and flexible. Another advantage is being able to use it with LINQ to SQL:
var maxObject = list.OrderByDescending(item => item.Height).First();
Note that this has the advantage of enumerating the list sequence just once. While it might not matter if list is a List<T> that doesn't change in the meantime, it could matter for arbitrary IEnumerable<T> objects. Nothing guarantees that the sequence doesn't change in different enumerations so methods that are doing it multiple times can be dangerous (and inefficient, depending on the nature of the sequence). However, it's still a less than ideal solution for large sequences. I suggest writing your own MaxObject extension manually if you have a large set of items to be able to do it in one pass without sorting and other stuff whatsoever (O(n)):
static class EnumerableExtensions {
public static T MaxObject<T,U>(this IEnumerable<T> source, Func<T,U> selector)
where U : IComparable<U> {
if (source == null) throw new ArgumentNullException("source");
bool first = true;
T maxObj = default(T);
U maxKey = default(U);
foreach (var item in source) {
if (first) {
maxObj = item;
maxKey = selector(maxObj);
first = false;
} else {
U currentKey = selector(item);
if (currentKey.CompareTo(maxKey) > 0) {
maxKey = currentKey;
maxObj = item;
}
}
}
if (first) throw new InvalidOperationException("Sequence is empty.");
return maxObj;
}
}
and use it with:
var maxObject = list.MaxObject(item => item.Height);
Doing an ordering and then selecting the first item is wasting a lot of time ordering the items after the first one. You don't care about the order of those.
Instead you can use the aggregate function to select the best item based on what you're looking for.
var maxHeight = dimensions
.Aggregate((agg, next) =>
next.Height > agg.Height ? next : agg);
var maxHeightAndWidth = dimensions
.Aggregate((agg, next) =>
next.Height >= agg.Height && next.Width >= agg.Width ? next: agg);
And why don't you try with this ??? :
var itemsMax = items.Where(x => x.Height == items.Max(y => y.Height));
OR more optimise :
var itemMaxHeight = items.Max(y => y.Height);
var itemsMax = items.Where(x => x.Height == itemMaxHeight);
mmm ?
The answers so far are great! But I see a need for a solution with the following constraints:
Plain, concise LINQ;
O(n) complexity;
Do not evaluate the property more than once per element.
Here it is:
public static T MaxBy<T, R>(this IEnumerable<T> en, Func<T, R> evaluate) where R : IComparable<R> {
return en.Select(t => new Tuple<T, R>(t, evaluate(t)))
.Aggregate((max, next) => next.Item2.CompareTo(max.Item2) > 0 ? next : max).Item1;
}
public static T MinBy<T, R>(this IEnumerable<T> en, Func<T, R> evaluate) where R : IComparable<R> {
return en.Select(t => new Tuple<T, R>(t, evaluate(t)))
.Aggregate((max, next) => next.Item2.CompareTo(max.Item2) < 0 ? next : max).Item1;
}
Usage:
IEnumerable<Tuple<string, int>> list = new[] {
new Tuple<string, int>("other", 2),
new Tuple<string, int>("max", 4),
new Tuple<string, int>("min", 1),
new Tuple<string, int>("other", 3),
};
Tuple<string, int> min = list.MinBy(x => x.Item2); // "min", 1
Tuple<string, int> max = list.MaxBy(x => x.Item2); // "max", 4
I believe that sorting by the column you want to get the MAX of and then grabbing the first should work. However, if there are multiple objects with the same MAX value, only one will be grabbed:
private void Test()
{
test v1 = new test();
v1.Id = 12;
test v2 = new test();
v2.Id = 12;
test v3 = new test();
v3.Id = 12;
List<test> arr = new List<test>();
arr.Add(v1);
arr.Add(v2);
arr.Add(v3);
test max = arr.OrderByDescending(t => t.Id).First();
}
class test
{
public int Id { get; set; }
}
In NHibernate (with NHibernate.Linq) you could do it as follows:
return session.Query<T>()
.Single(a => a.Filter == filter &&
a.Id == session.Query<T>()
.Where(a2 => a2.Filter == filter)
.Max(a2 => a2.Id));
Which will generate SQL like follows:
select *
from TableName foo
where foo.Filter = 'Filter On String'
and foo.Id = (select cast(max(bar.RowVersion) as INT)
from TableName bar
where bar.Name = 'Filter On String')
Which seems pretty efficient to me.
Based on Cameron's initial answer, here is what I've just added at my enhanced version of SilverFlow library's FloatingWindowHost (copying from FloatingWindowHost.cs at http://clipflair.codeplex.com source code)
public int MaxZIndex
{
get {
return FloatingWindows.Aggregate(-1, (maxZIndex, window) => {
int w = Canvas.GetZIndex(window);
return (w > maxZIndex) ? w : maxZIndex;
});
}
}
private void SetTopmost(UIElement element)
{
if (element == null)
throw new ArgumentNullException("element");
Canvas.SetZIndex(element, MaxZIndex + 1);
}
Worth noting regarding the code above that Canvas.ZIndex is an attached property available for UIElements in various containers, not just used when being hosted in a Canvas (see Controlling rendering order (ZOrder) in Silverlight without using the Canvas control). Guess one could even make a SetTopmost and SetBottomMost static extension method for UIElement easily by adapting this code.
You can also upgrade Mehrdad Afshari's solution by rewriting the extention method to faster (and better looking) one:
static class EnumerableExtensions
{
public static T MaxElement<T, R>(this IEnumerable<T> container, Func<T, R> valuingFoo) where R : IComparable
{
var enumerator = container.GetEnumerator();
if (!enumerator.MoveNext())
throw new ArgumentException("Container is empty!");
var maxElem = enumerator.Current;
var maxVal = valuingFoo(maxElem);
while (enumerator.MoveNext())
{
var currVal = valuingFoo(enumerator.Current);
if (currVal.CompareTo(maxVal) > 0)
{
maxVal = currVal;
maxElem = enumerator.Current;
}
}
return maxElem;
}
}
And then just use it:
var maxObject = list.MaxElement(item => item.Height);
That name will be clear to people using C++ (because there is std::max_element in there).

Categories

Resources