Reputation: 573
Problem:
I have a wpf DataGrid
, which is bound to a list of objects from my custom model class. I need to fullfil following requirements:
I am looking for something simular to this question, but I struggle to convert my list to something that would be compatible with the Enumerable.Except
method.
Here is a little example:
Model:
public class MyModel
{
public MyModel(int mID, string mName, string mScore)
{
ID = mID;
Name = mName;
Score = mScore;
}
public int ID;
public string Name;
public string Score;
}
Trying to add a new model with the lowest free ID to my list
List<MyModel> list = new List<MyModel>();
list.Add(new MyModel(0, "Rodney", "189"));
list.Add(new MyModel(1, "Janet", "169"));
list.Add(new MyModel(2, "John", "110"));
list.Add(new MyModel(3, "Samantha", "160"));
list.Add(new MyModel(4, "Daniel", "156"));
list.Add(new MyModel(5, "Jack", "89"));
list.RemoveAll(x => x.ID == 1);
var result = Enumerable.Range(list.Min(m => m.ID), list.Count).Except(list).First();
list.Add(new MyModel(result, "Carolyn", "159")); //Should be 1
I probably have to use some kind of lambda expressions, as I had to for the list.Min()
Method, but it already took me ages to get this right.
Upvotes: 2
Views: 848
Reputation: 7054
I don't want to iterate through the list each time a new model is added
Actually this means that you can't use Linq methods like Min
, Max
and Except
because they iterate through your collection under hood.
Another thing you should keep in mind is that RemoveAll
also iterate through all items in the list. So you need to change two things: first (already mentioned in Fabjan comment and in Kit answer) is to track all removed identifiers in SortedList
and second is to use a Dictionary<int, MyModel>
to remove your model by key, which is O(1)
complexity instead of O(N)
in case with List
.
You can encapsulate this logic with following Container
class:
public class Container
{
private readonly Dictionary<int, MyModel> items = new Dictionary<int, MyModel>();
private readonly SortedList removedIds = new SortedList();
private int maxId = 0;
public int Add(MyModel model)
{
int id;
if (removedIds.Count > 0)
{
id = (int) removedIds.GetByIndex(0);
removedIds.RemoveAt(0);
}
else
{
id = maxId++;
}
model.ID = id;
items.Add(id, model);
return id;
}
public void RemoveById(int id)
{
if (!items.ContainsKey(id))
throw new ArgumentException(); // or just return
items.Remove(id);
removedIds.Add(id, id);
}
}
Upvotes: 1
Reputation: 21719
To expand on what a commenter posted, use a SortedList, which you can use to track the free IDs. That way you don't have to look for gaps. When you add something to the main list, remove the first integer from the free list and use that value as the ID of your model. When you remove the model from the main list, add it's ID to the free list.
Upvotes: 0
Reputation: 70529
Why make complicated code? You want the number greater than the lowest item that does not exist in the list
var result = list.Min(m => m.ID);
while (list.Any(m => m.ID == result))
result++;
This may not be the fastest code, but at least it is clear.
Upvotes: 2
Reputation: 1704
The issue is that you compare two different kinds of objects. Enumerable.Range(list.Min(m => m.ID), list.Count)
is an enumeration of integers between 0 and 5, and when you use Except(list)
, the list consists of MyModel
objects.
You probably want to extract the ids:
var result = Enumerable.Range(list.Min(m => m.ID), list.Count).Except(list.Select(m => m.ID)).First();
Upvotes: 2