Modifying an object in a List - c#

How do you modify an object in a List where you don't know what the index will be? Everything I've found about modifying object has been when you know the index that you want to change. So in the code below, it will replace the ServiceRequest at index 0 with the new ServiceRequest 'r' (at least I think it will). But what if you aren't sure what index the user will choose to change?
public class ManagementSystem
{
List<ServiceRequest> Requests = new List<ServiceRequest>();
public void CreateRequest(ServiceRequest r)
{
Requests.Add(r);
}
public void DeleteRequest(ServiceRequest r)
{
Requests.Remove(r);
}
public void ModifyRequest(ServiceRequest r)
{
Requests[0] = r;
}
}

If you want to update an item in a List<T>, first you have to find that item and the list and then replace.
Example:
public void ModifyRequest(ServiceRequest r)
{
var req = Requests.FirstOrDefault(rs => rs.SomeProperty.Equals(r.SomeProperty));
if (req != null)
{
var idx = Requests.IndexOf(req);
Requests[idx] = r;
}
}
Here, I'm assuming Request class has a property SomeProperty, based on which you'll find a matching Request object in the list.
Note that
var index = Requests.IndexOf(r);
will give a non-negative (valid) index only if that item in the list is the same object as r. They have to have same reference for IndexOf() to work.

You should be able to use:
Requests.IndexOf(r) //r being the object you want to modify
to get the index of the object you want to modify, then work out the rest from there

Related

How to find specific object from list in C#?

How is it possible to find a specific object from a list?
Lets say i have a function that takes an object and a list that contains objects of this type and returns the number at which position the specific object is found.
The only way i could think of a solution is to run the list through with a foreach loop, but isn't there a better way?
Thanks
You can use the IndexOf(T item) method:
myList.IndexOf(myItem);
It returns the index of the first occurrence of the item.
The only way i could think of a solution is to run the list through with a foreach loop
Generally, you need a loop (a for or foreach) to find an object in a list. You could use it directly, or through a function that iterates over list elements, but there is going to be a loop. There is no way around it: unless you know something special about the way the elements of the array are arranged, you have to look at them all.
One case of knowing something special about arrangement of elements is knowing that an array is sorted. If this is the case, and when you know the value of the attribute on which the element is sorted, you can find the element much faster by using binary search.
You could use linq expressions
List.where(condition)
Or
List.Select(condition).FirstOrDefault
Based on search condition it will return the item you want.
You can use method IndexOf or if you use a special condition then you can use method
public int FindIndex(Predicate<T> match);
where match is delegate
public delegate bool Predicate<T>(T obj);
In fact it is similar to standard C++ algorithm std::find_if
To see whether object is there You might just need List<T>.Contains method
It states,
Determines whether an element is in the List.
And you need to use it like List<T>.Contains(T type item) , where T is the same type of List and item you need to compare. In your case it's a the type of Object
And to return the index you can use List<T>.IndexOf Method
Searches for the specified object and returns the zero-based index of the first occurrence within the entire List.
Simple Console program
class Program
{
static void Main(string[] args)
{
MyType a = new MyType() { id = 10 };
MyType b = new MyType() { id = 20 };
MyType c = new MyType() { id = 30 };
List<MyType> testList = new List<MyType>();
testList.Add(a);
testList.Add(b);
Console.WriteLine(testList.Contains(a)); // <= Will return true
Console.WriteLine(testList.Contains(c)); // <= Will return false
Console.WriteLine(testList.IndexOf(a)); // <= will return 0 : the index of object a
Console.ReadKey();
}
}
// A simple class
class MyType
{
private int ID;
public int id
{
get { return ID; }
set { ID = value; }
}
}

How to "Dequeue" Element from a List?

I have a List of cards called _deck:
private List<String> _deck = new List<String> {"2h", "3h", "4h", ... }
And then I want to remove a card from the List and save into a variable. I'm trying to do:
String p1FirstCard = _deck.RemoveAt(0);
but I'm getting the error
Cannot convert type void to String
In C# List is there something like push/pop but which does that at the "head" or "start" of the List? (Push/pop works at the "tail" or "end" of the list.)
If not, how should I do remove the first element but save it in a variable?
If you want to dequeue the first element, you could simply use a Queue<T>.
class Program
{
static void Main(string[] args)
{
var _deck = new Queue<String>();
_deck.Enqueue("2h");
_deck.Enqueue("3h");
_deck.Enqueue("4h");
_deck.Enqueue("...");
var first = _deck.Dequeue(); // 2h
first = _deck.Dequeue(); // 3h
}
}
If you want to pop the last element, you could use a Stack<T>.
class Program
{
static void Main(string[] args)
{
var _deck = new Stack<String>();
_deck.Push("2h");
_deck.Push("3h");
_deck.Push("4h");
_deck.Push("...");
var first = _deck.Pop(); // ...
first = _deck.Pop(); // 4h
}
}
You can do it in two steps:
String p1FirstCard = _deck[0];
_deck.RemoveAt(0);
You can write your own extension helper method (I added an index to Pop, as #Fredou suggested:
static class ListExtension
{
public static T PopAt<T>(this List<T> list, int index)
{
T r = list[index];
list.RemoveAt(index);
return r;
}
}
and then call
String p1FirstCard = _deck.PopAt(0);
P.S. The name can be a bit confusing. Pop usually removes the last element, not the first one.
Building on AlexD's answer, I added a couple more extension methods:
public static class ListExtensionMethods
{
public static T PopAt<T>(this List<T> list, int index)
{
var r = list[index];
list.RemoveAt(index);
return r;
}
public static T PopFirst<T>(this List<T> list, Predicate<T> predicate)
{
var index = list.FindIndex(predicate);
var r = list[index];
list.RemoveAt(index);
return r;
}
public static T PopFirstOrDefault<T>(this List<T> list, Predicate<T> predicate) where T : class
{
var index = list.FindIndex(predicate);
if (index > -1)
{
var r = list[index];
list.RemoveAt(index);
return r;
}
return null;
}
}
If you want a direct equivalent to pop(), you'll have to write your own, because I don't think a List has a "Remove from end and return". However, there are both the Queue (first in, first out) and the Stack (first in, last out) classes instead of just a List.
There's also the LinkedList class which lets you add to or remove from both the beginning or the end, but the provided RemoveFirst() and RemoveLast() methods don't automatically return the item being removed - you'd need to write an extension method like AlexD's to do that.
All of these deal with removing things from the beginning or the end of the list. If you just want to remove an arbitrary item from the middle of a List, there's always List.Remove(item) which removes a specific item from the list (rather than by position).
private List<String> _deck = new List<String> {"2h", "3h", "4h", ... }
//Save into variable first
String p1FirstCard = _deck[0];
//Now just remove it
_deck.RemoveAt(0);
RemoveAt(int) doesn't return anything.

Remove item from List and get the item simultaneously

In C# I am trying to get an item from a list at a random index. When it has been retrieved I want it to be removed so that it can't be selected anymore. It seems as if I need a lot of operations to do this, isn't there a function where I can simply extract an item from the list? the RemoveAt(index) function is void. I would like one with a return value.
What I am doing:
List<int> numLst = new List<int>();
numLst.Add(1);
numLst.Add(2);
do
{
int index = rand.Next(numLst.Count);
int extracted = numLst[index];
// do something with extracted value...
numLst.removeAt(index);
}
while(numLst.Count > 0);
What I would like to do:
List<int> numLst = new List<int>();
numLst.Add(1);
numLst.Add(2);
do
{
int extracted = numLst.removeAndGetItem(rand.Next(numLst.Count));
// do something with this value...
}
while(numLst.Count > 0);
Does such a "removeAndGetItem" function exist?
No, as it's a breach of pure function etiquette, where a method either has a side effect, or returns a useful value (i.e. not just indicating an error state) - never both.
If you want the function to appear atomic, you can acquire a lock on the list, which will stop other threads from accessing the list while you are modifying it, provided they also use lock:
public static class Extensions
{
public static T RemoveAndGet<T>(this IList<T> list, int index)
{
lock(list)
{
T value = list[index];
list.RemoveAt(index);
return value;
}
}
}
public static class ListExtensions
{
public static T RemoveAndGetItem<T>(this IList<T> list, int iIndexToRemove}
{
var item = list[iIndexToRemove];
list.RemoveAt(iIndexToRemove);
return item;
}
}
These are called extension methods, call as new List<T>().RemoveAndGetItem(0).
Things to consider in the extension method
Exception handling with the index that you pass, check that the index is withing 0 and the count of the list before doing this.

Sort List without creating new variable

I'm attempting to use Enumerable.OrderBy to sort a List because ultimately I want to be able to sort by more than a single field. At the moment it only appears to work if I create a new variable var to hold the results view which means (I think) the types need to be re-cast.
Is there a method to sort a List by more than 1 field whilst retaining the original List variable and types? I.e. I'd rather end up with variable _orderedbins of type List<orderedbins>
Below is what I currently have but everything from var test = ... onwards seems a bit wrong.
public class orderedBins
{
public string Bin { get; set; }
public int Order { get; set; }
}
List<orderedbins> _orderedbins = new List<orderedbins>();
foreach (string item in splitbins)
{
string[] spbinSetting = item.Split(',');
bool bchecked = bool.Parse(spbinSetting[1]);
int border = int.Parse(spbinSetting[2]);
if (bchecked == true)
{
_orderedbins.Add(new orderedbins { bin = spbinSetting[0], Order = border });
}
}
var test =_orderedbins.OrderBy(x => x.Order);
foreach (var item in test)
{
string f = item.Bin;
int g = item.Order;
}
You know, you can perform multiple sub-sorts for an order by...
lst.OrderBy(x => x.Prop1).ThenBy(x => x.Prop2).ThenByDescending(x => x.Prop3)...
Just add a .ToList(); and introduce it with a variable, to have the result in a list variable.
EDIT:
Great suggestion by Willem, for more readability:
from x in lst
order by x.Prop1, x.Prop2, x.Prop3
select x
You can create a new sorted list without creating a new variable using
list = list.OrderBy(item => item.Field1).ThenBy(item => item.Field1).ToList();
It will still create an entirely new list though (it's not actually much of a problem to add a new variable; those are cheap. Creating a new list, doing this, is fine as long as the list isn't very large.
If you need to sort the list in place then you'll want to use a custom comparer with the List's sort method:
public class MyComparer : IComparer<MyClass>
{
public int Compare(MyClass x, MyClass y)
{
if(x.Field1 != y.Field1)
return x.Field1.CompareTo(y.Field1)
else
return x.Field2.CompareTo(y.Field2);
}
}
List<MyClass> list = new List<MyClass>();
//Populate list
list.Sort(new MyComparer());
As others suggested, using Linq's OrderBy(...).ToList() would be a cleaner way, but this will give you a new instance of the list.
To retain the original instance, consider to use List<T>.Sort():
_orderedbins.Sort(new Comparison<orderedBins>((obj1, obj2) =>
{
int result = obj1.Order.CompareTo(obj2.Order);
return result != 0 ? result : obj1.Bin.CompareTo(obj2.Bin);
}));
This will do the trick:
_orderedbins = _orderedbins.OrderBy(x => x.Order).ToList();
...but there's no real issue creating a new variable/reference.
I think this will do it (it's already a list of orderbins so no casting is required):
_orderbins = _orderbins.OrderBy(x => x.Order).ToList();

How can i cast into a ObservableCollection<object>

How can i cast
from ObservableCollection<TabItem> into ObservableCollection<object>
this doesnt work for me
(ObservableCollection<object>)myTabItemObservableCollection
you should copy like this
return new ObservableCollection<object>(myTabItemObservableCollection);
Basically, you can't. Not now, and not in .NET 4.0.
What is the context here? What do you need? LINQ has Cast<T> which can get you the data as a sequence, or there are some tricks with generic methods (i.e. Foo<T>(ObservalbleCollection<T> col) etc).
Or you can just use the non-generic IList?
IList untyped = myTypedCollection;
untyped.Add(someRandomObject); // hope it works...
you could use IEnumerable.Cast<T>()
You can't. ObservableCollection<TabItem> does not derive from ObservableCollection<object>.
If you explain why you would want to perhaps we can point out an alternative interface you can use.
thanx for all answers, but I think I have solve this problem self with a "helpermethode".
Perhaps has any a better method or a linq statement for this.
private void ConvertTabItemObservableCollection()
{
Manager manager = this.container.Resolve<Manager>();
foreach (var tabItem in manager.ObjectCollection)
{
TabItemObservableCollection.Add((TabItem)tabItem);
}
}
None of the examples I have found have worked for me, I have cobbled together the below code and it seems to work. I have a hierarchy that is created by deserializing an XML file and I am able to loop through all the objects in the hierarchy, but you can adapt this to just loop through one ObservableCollection and get the objects as objects and not strongly typed.
I want to add a PropertyChangingEventHandler to every property in the hierarchy so that I can implement undo/redo functionality.
public static class TraversalHelper
{
public static void TraverseAndExecute(object node)
{
TraverseAndExecute(node, 0);
}
public static void TraverseAndExecute(object node, int level)
{
foreach (var property in node.GetType().GetProperties())
{
var propertyValue = node.GetType().GetProperty(property.Name).GetGetMethod().Invoke(node, null); // Get the value of the property
if (null != propertyValue)
{
Console.WriteLine("Level=" + level + " : " + property.Name + " :: " + propertyValue.GetType().Name); // For debugging
if (property.PropertyType.IsGenericType && property.PropertyType.GetGenericTypeDefinition() == typeof(ObservableCollection<>)) // Check if we are dealing with an observable collection
{
//var dummyvar = propertyValue.GetType().GetMethods(); // This was just used to see which methods I could find on the Collection
Int32 propertyValueCount = (Int32)propertyValue.GetType().GetMethod("get_Count").Invoke(propertyValue, null); // How many objects in the collection
level++;
for (int i = 0; i < propertyValueCount; i++) // Loop over all objects in the Collection
{
object properyValueObject = (object)propertyValue.GetType().GetMethod("get_Item").Invoke(propertyValue, new object[] { i }); // Get the specified object out of the Collection
TraverseAndExecute(properyValueObject, level); // Recursive call in case this object is a Collection too
}
}
}
}
}
}
The method is just called like this
TraversalHelper.TraverseAndExecute(object);
If you just want to create a collection of objects you just need this bit of code
ObservableCollection<Field> typedField = migration.FileDescriptions[0].Inbound[0].Tables[0].Table[0].Fields[0].Field; // This is the strongly typed decalaration, a collection of Field objects
object myObject = typedField; // Declare as object
Int32 propertyValueCount = (Int32)myObject.GetType().GetMethod("get_Count").Invoke(myObject, null); // How many objects in this Collection
for (int i = 0; i < propertyValueCount; i++) // Loop over all objects in the Collection
{
object properyValueObject = (object)myObject.GetType().GetMethod("get_Item").Invoke(myObject, new object[] { i }); // Get the specified object out of the Collection, in this case a Field object
// Add the object to a collection of objects, or whatever you want to do with object
}
You can Cast it as INotifyCollectionChanged;
Like:
if (myTabItemObservableCollection is INotifyCollectionChanged collection)
{
Do Stuff
}

Categories

Resources