How to update an entire object in a list C#? - c#

I have a list of object and I want to replace one of the objects in the list with the new object:
public Parent AppendParentChildren(Request request)
{
var Children = request.Parent.Children.ToList();
if (Children.Any(x => x.TrackingNumber == request.Child.TrackingNumber))
{
//Here I want to replace any Children that have the same tracking number in the list with the new Child passed in
}
else
{
Children.Add(request.Child);
}
request.Parent.Children = Children;
return request.Parent;
}
public class Request
{
public Parent Parent { get; set; }
public Child Child { get; set; }
}
public class Parent
{
public IEnumerable<Child> Children {get;set;}
}
If I try and use it in a loop:
public static class Extension
{
public static void Update<T>(this List<T> items, T newItem)
{
foreach (var item in items)
{
//this
item = newItem;
}
}
}
item is read only, so I cannot replace the object in the list.
Any suggestions?

You can't change the member of a foreach iteration because foreach implements the IEnumerable type which is read-only.
A solution would be to cast the list of items inside the extension method as a List (which is read-writable). Then you'd want to identify which item(s) in the list you are replacing and update them. Below is what the Update extension method would look like (assuming you're in a situation where you can use LINQ)
public static class Extension
{
public static void Update<T>(this List<T> items, T newItem)
{
var childList = items as List<Child>;
var newChildItem = newItem as Child;
var matches = childList.Where(x => x.TrackingNumber == newChildItem.TrackingNumber).ToList();
matches.ForEach(x => childList[childList.IndexOf(x)] = newChildItem);
}
}
I've put a working example (albeit slightly bloated) on dotnetfiddle
https://dotnetfiddle.net/MJ5svP
It's also worth noting that although it looks like you're altering childList, this is actually referenced back to the original list not creating a copy (more info on this here)

Related

Change state of List<T> inside extension method

How do I change the state of List which is part of view model in extension method so the view model reflects this change without having to re assign the value
Code:
//ViewModel:
//This line should modify the change internally wihtout having to reassign like
// ..Selected =model.CheckBoxList.Selected.RemoveWhere(c => c.SelectedValue == null)
model.CheckBoxList.Selected.RemoveWhere(c => c.SelectedValue == null)
Extension Method :
public static IEnumerable<T> RemoveWhere<T>(this IEnumerable<T> source, Predicate<T> predicate)
{
var x = source.ToList();
x.RemoveAt(1);
source = x;
return source;
}
Update:
public class CheckBoxList
{
public CheckBoxList()
{
//Selected = new List<CheckBoxListViewModel>();
}
[XmlElement("SelectedItem")]
public List<CheckBoxListViewModel> Selected { get; set; }
}
[XmlRoot(ElementName = "MyClass")]
public class CheckBoxListViewModel
{
public string SelectedValue { get; set; }
public string SelectedDescription { get; set; }
}
Assign a parameter has no effect on the expression you pass to the method.
You instead want to mutate the existing instance. To do that, you need to accept a mutable type; namely, List<T>.
List<T> already has a RemoveAll() function which does exactly that, so you don't need to do anything at all.

Add an object to a list within a method, no matter what type they are

i have to add an object to a list. but first, i have to check if that object (the object.name property) does already exist in the list and do some other tests. this all works fine, but i have to do it with different objects and their lists. so what i would like to have is a method, which takes the object and the list as parameters, and does all the magic, no matter of what type the object and the list is.
what i have so far:
the list and the object:
ProductList = new ObservableCollection<Product>();
private Product _newProduct = new Product();
public Product NewProduct
{
get { return _newProduct; }
set
{
if (_newProduct == value)
return;
_newProduct = value;
RaisePropertyChanged("NewProduct");
}
}
searching if the object does exist:
bool productFound = false;
for (int i = 0; i < ProductList.Count; i++)
{
if (ProductList[i].Name == NewProduct.Name)
{
productFound = true;
break;
}
}
after some other tests, the object is added to the list:
ProductList.Add(NewProduct.Clone());
instead of writing that code for all my lists (5 different types), i would like to call a method and pass the object and the list. the method then performs the tests and adds the object to the list, no matter what type they are. how can i do this?
i am looking for something like this:
Save(NewProduct, ProductList);
Declare an interface and implement it in all entities you need:
interface INamedEntity : ICloneable
{
stirng Name { get; }
}
class Product : INamedEntity { /* ... */ }
Then you can re-write code like this:
void Save<T>(List<T> entities, T entity)
where T: INamedEntity
{
// searching if the object does exist:
var isExists = entities.Any(e => e.Name == entity.Name);
// after some other tests, the object is added to the list:
entities.Add(entity.Clone());
}
you could use an interface which has the common properties and methods
interface IItem
{
public string Name {set; get;}
}
then your classes should implement the interface, for example
class Product: Item { ...}
Now you can use the following general method to save the items
public void Save(IItem item, List<IItem> list)
{
if (!list.Any(x => x.Name == item.Name))
list.Add(item);
}

Add to a readonly collection in a constructor?

Is there a c# language construct that will allow me to add items to a readonly collection property in a constructor? I want to do something like this:
public class Node{
public IList<Node> Children {get; protected set;}
public Node(){
Children = new ObservableList<Node>();
}
}
... in my code somewhere...
var node = new Node {Children.Add(new Node())};
(not a great example, but I hope this gets the idea across)...
UPDATE
OK sorry I need to be clearer. I didn't write this Node class, and I cannot change it. I am asking if there is a c# language concept that will allow me to add to the readonly collection in the parameterless constructor in the second snippet, assuming the Node class is not changeable.
Try this. It is definitely possible to add elements on construction
var node = new Node
{
Children =
{
new Node(),
new Node()
}
};
If you have a property of type List that is get only, that only means you can't set that property, you can still add things to the list.
You could however expose an IEnumerable property instead and have a constructor that takes a list(or another IEnumerable more likely).
Property initializers do not work since the compiler will just rewrite them to regular property assignments.
I'd do this:
public class Node{
public IEnumerable<Node> Children {get; private set;}
public Node(IEnumerable<Node> children){
Children = children.ToList();
}
}
if you can't change the Node class, I suggest writing a helper class similar to this:
public static Node Create(IEnumerable<Node> children)
{
var n = new Node();
foreach (var c in children)
n.Children.Add(c);
return n;
}
To use the collection initializer syntax from you second code snippet your Node class must implement IEnumerable and have a public method with the signature
void Add(Node child)
Hence such a class cannot offer the immutability you desire. I think the best solution to your problem would be to do this
public class Node
{
public readonly IEnumerable<Node> Children;
public Node(IEnumerable<Node> children)
{
Children = children;
}
}
or if you do not like the deferred execution of IEnumerable:
public class Node
{
public readonly ReadOnlyCollection<Node> Children;
public Node(IEnumerable<Node> children)
{
Children = new ReadOnlyCollection<Node>(children);
}
}
You can add a backing field to the "Children" property, then just populate the backing field during construction.
Like so
public class Node
{
private IList<Node> _Children;
public IList<Node> Children { get { return _Children; } }
public Node(IList<Node> children)
{
_Children = children;
}
}
Then you can do this
var node = new Node((new ObservableList<Node>()).Add(new Node()));

Iteration of an object, that may have parent, or child of it's own same type

I have a class that may have a parent, or list of children of the same type of it's own. The following code snippet should explain my scenario.
public abstract class X{
public virtual List<X> ChildItems { get; set; }
public virtual X ParentItem { get; set; }
}
I would like to know if there is a particularly efficient method to traverse the objects from an object of type X, checking if the object has a parent, or children starting from bottom up.
public static void SaveSetup(X obj) {
//logic here
}
Any help is appreciated.
What you are dealing with is a tree structure (or possibly many disconnected tree structures). A tree structure has a root element. Usually, a tree structure is traversed starting from the root. If you want to start from any element in the tree, I suggest you to first get the root element and then traverse in the usual manner.
The easiest way to traverse a recursive structure is to use recursive method, i.e., a method that calls itself.
public abstract class X
{
public virtual List<X> ChildItems { get; set; }
public virtual X ParentItem { get; set; }
// Method for traversing from top to bottom
public void Traverse(Action<X> action)
{
action(this);
foreach (X item in ChildItems) {
item.Traverse(action);
}
}
// Get the root (the top) of the tree starting at any item.
public X GetRootItem()
{
X root = this;
while (root.ParentItem != null) {
root = root.ParentItem;
}
return root;
}
}
Now you can save the setup with
X root = item.GetRootItem();
root.Traverse(SaveSetup);
Example with lambda expression. Prints every item of the tree assuming that ToString() has been overridden to return a meaningful string.
root.Traverse(x => Console.WriteLine(x));
Traverse from given object to root (ParentItem = null)
public static void SaveSetup(X obj) {
while (obj != null)
{
// logic here
obj = obj.ParentItem;
}
}

Building a tree using a list of objects

I have a list of objects with property id and parent_id.
I want to build a tree to link up those children and parents.
1 parent may have several children and there is an object which will be the ancestor of all objects.
What's the fastest algorithm to implement that?
I use C# as programming language, but other languages are also okay.
Something like that should do the trick :
public List<Node> MakeTreeFromFlatList(IEnumerable<Node> flatList)
{
var dic = flatList.ToDictionary(n => n.Id, n => n);
var rootNodes = new List<Node>();
foreach(var node in flatList)
{
if (node.ParentId.HasValue)
{
Node parent = dic[node.ParentId.Value];
node.Parent = parent;
parent.Children.Add(node);
}
else
{
rootNodes.Add(node);
}
}
return rootNodes;
}
(assuming that ParentId is a Nullable<int>, and is null for root nodes)
You could use a dictionary:
var dict = new Dictionary<Id, Node>();
foreach (var item in items)
{
dict[item.Id] = new Node(item);
}
foreach (var item in items)
{
dict[item.ParentId].AddChild(dict[item.Id]);
}
I much prefer this kind of structure. By maintaining a single list (you may want to use a dictionary or similar for speed) of items and passing it into the GetChildItems function you have greater flexibilty and ease of sorting, adding, removing, saving to a db etc.
You only really need the GetChildItem function when you are rendering the list to a view and you want the tree structure for easy editing as you say. In this case you can have a view model with the full list and the item which is passed into each item view
public class Item
{
public string Id { get; set; }
public string ParentId { get; set; }
public IEnumerable<Item> GetChildItems(List<Item> allItems)
{
return allItems.Where(i => i.Id == this.ParentId);
}
}
public class Tree
{
public List<Item> Items { get; set; }
public IEnumerable<Item> RootItems(List<Item> allItems)
{
return allItems.Where(i => i.ParentId == null);
}
}
Note: the class structure above is designed to mimic the traditional complex object pattern. these days you would prob just have GetChildItems(List allItems, Item parentItem) in the view model

Categories