How to replace a list item with direct instance assignment - c#

I have the following code where I'm trying 3 approaches (Cases) to update the first item in a C# list(Note: Dump() is a helper output method in the LINQPad IDE). I would appreciate an explanation as to why Case 2 does not succeed in updating the list while Case 3 does. Both first and list[0] are references to the first item in the list and should behave equivalently when assigned a direct reference. Apparently not...
void Main()
{
Person first = null;
List<Person> list = CreateList(out first);
//Case 1
//This updates the list
first.fname = "Third";
list.Dump(); //outputs third, second
//Case 2
//This does not update the list
list = CreateList(out first);
first= new Person() { fname="Third"};
list.Dump(); //outputs first, second
//Case 3
//This updates the list
list = CreateList(out first);
list[0] = new Person() { fname="Third"};
list.Dump(); //outputs third, second
}
List<Person> CreateList(out Person first)
{
var list = new List<Person>
{
new Person() { fname="First", lname = ""},
new Person() { fname="Second", lname = ""}
};
first = list.Find( x => x.fname == "First");
return list;
}
// Define other methods and classes here
class Person
{
public string fname;
public string lname;
}

The second case doesn't work because you change the reference for the first to the new object using this code:
first= new Person() { fname="Third"};
After this code run, the first is not refer to list object again.
Try to use this for the second case:
list = CreateList(out first);
if(first != null)
first.fname="Third";
list.Dump();
This will set the property of first and the first is still refer to the list item.

when you pass a new object to a reference object
first= new Person() { fname="Third"};
you generate a new object with a new hashcode upon which the object is identified in collection.the list does not find the previous hascode and thus list is not updated.//case 2
but in case 3 you are replacing the instance of object and thereby list updates the new hash
in case 1 you modify only property of an object and the hash remains intact
may be this be explanation to your problem

Related

Trying to understand Generic List in C# [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 9 months ago.
Improve this question
I hope everyone is well.
I have some practice code here that I've been working on and the code works and I have no issues with it however, I don't find that I fully understand what I've written and why it works.
I want to try and be able to understand my work so that I can become a better programmer, I've left comments for the code that I dont fully understand, the rest I am comfortable with.
I would appreciate any one with the spare time to give me a few pointers and some help, thank you very much.
namespace GenericList
{
class Program
{
static void Main(string[] args)
{
/* I understand how objects work, but I dont fully understand
what is happening when I am passig through my constructer
name through the List */
List<Cities> cities = new List<Cities>();
/* I am passing matching arguments through each instance
of my cities List object but I still struggle to
visualise this process */
cities.Add(new Cities(1, "Durban - Home to the largest harbor in Africa"));
cities.Add(new Cities(2, "Johannesburg - The largest city in the country"));
cities.Add(new Cities(3, "Gqebetha - Also known as P.E, the friendly city"));
cities.Add(new Cities(4, "Bloemfontien - Host of the Rose Festival"));
cities.Add(new Cities(5, "Pretoria - South Africa's capital"));
Console.WriteLine("Which city would you like to know an interesting fact for?" +
"\n1) Durban" +
"\n2) Johannesburg" +
"\n3) Gqebetha" +
"\n4) Bloemfontien" +
"\n5) Pretoria" +
"\nEnter the number for the city you want:");
int answer = int.Parse(Console.ReadLine());
bool found = false;
for (int i = 0; i < cities.Count; i++)
{
if (cities[i].Id.Equals(answer))
{
Console.WriteLine("\nANSWER: " + cities[i].City);
found = true;
}
}
if (!found)
{
Console.WriteLine("\nWe couldn't find what you are looking for.");
}
}
}
class Cities
{
int id;
string city;
public Cities(int id, string city)
{
this.id = id;
this.city = city;
}
public int Id { get => id; set => id = value; }
public string City { get => city; set => city = value; }
}
}
List<Cities> cities = new List<Cities>();
Here you are creating a List, which contains only the object of Cities's class.
List is a array type data structure with various utilities(method).
Here you are using Add(), which append a new object to the list (in that case only Cities object as you declare in first line List<Cities>).
cities.Add(new Cities(1, "Durban - Home to the largest harbor in Africa"));
If I break down this line this will be:
// Creating a new Cities object
var newCity = new Cities(1, "Durban - Home to the largest harbor in Africa");
cities.Add(newCity );
Last line from the code block is appending newCity to the list of cities
Hope you understand now, If anything left unclear let me know
i rewrote with linq so you can see as a different approach. more easy reading code
void Main()
{
List<Cities> cities = new List<Cities>();
//add to cities array a new City class, since constructor accept 2 parameters you supply them on new object creation
Cities city;
city = new Cities(1, "Durban - Home to the largest harbor in Africa");
cities.Add(city);
city = new Cities(2, "Johannesburg - The largest city in the country");
cities.Add(city);
city = new Cities(3, "Gqebetha - Also known as P.E, the friendly city");
cities.Add(city);
city = new Cities(4, "Bloemfontien - Host of the Rose Festival");
cities.Add(city);
city = new Cities(5, "Pretoria - South Africa's capital");
cities.Add(city);
//different approach: create list with objects
cities = new List<Cities>()
{
new Cities(1, "Durban - Home to the largest harbor in Africa"),
new Cities(2, "Johannesburg - The largest city in the country"),
new Cities(3, "Gqebetha - Also known as P.E, the friendly city"),
new Cities(4, "Bloemfontien - Host of the Rose Festival"),
new Cities(5, "Pretoria - South Africa's capital")
};
Console.WriteLine("Which city would you like to know an interesting fact for?" +
"\n1) Durban" +
"\n2) Johannesburg" +
"\n3) Gqebetha" +
"\n4) Bloemfontien" +
"\n5) Pretoria" +
"\nEnter the number for the city you want:");
int answer = int.Parse(Console.ReadLine());
var result = cities
.Select((obj, index) => new { index, obj }) //set index for each object
.Where(w => w.index == answer)
.FirstOrDefault();
if (result == null)
Console.WriteLine("\nWe couldn't find what you are looking for.");
else
Console.WriteLine("\nANSWER: " + result.obj.City);
}
class Cities
{
//class properties
public int Id { get; set; }
public string City { get; set; }
//Parameterized Constructor https://www.tutlane.com/tutorial/csharp/csharp-constructors-with-examples#divcspzcst
public Cities(int id, string city)
{
Id = id;
City = city;
}
}
With
List<Cities> cities = new List<Cities>();
you create a new List-object that can store Cities objects. The generic type parameter <Cities> denotes the type of the list items and does not refer to the constructor. As an example, the following code would create a list that could store integer values:
List<int> lst = new List<int>();
The List<T> class is a generic type. The basic operations for a list like adding, removing, enumerating and so on are the same no matter what the type of the list items is. By creating a generic type, you can implement the functionality without knowing which types are used later on when you create an object. You might compare this to a method: when you implement the method, you define the parameters and their types; when you call the method, you supply the values of the parameters.
The code in the following line performs two tasks:
cities.Add(new Cities(1, "Durban - Home to the largest harbor in Africa"));
First, a new object of type Cities is created and initialized through the constructor. Second, it is added to the list. These are two separate steps that can also written like this:
// Create new object of type Cities
var city = new Cities(1, "Durban - Home to the largest harbor in Africa");
// Add newly created object to list
cities.Add(city);
As #BinRohan suggested in the comments, it might be a good idea the rename the Cities class to City because it defines a single city, not a collection of cities.
When you write this line of code
List<Cities> cities = new List<Cities>();
You instantiate an object being a List of "Cities". List is a "generic" type. It means it is able to handle any type and will behave the same. You could have a List or List, you'll manipulate different objects but the behaviour of List remains the same. You are not "passing" Cities to the List constructor, consider List as a type in itself.
It would be equivalent of declaring an array of Cities for example. There is no data in your list yet but it is ready to receive multiple instance of Cities.
Then when you write
cities.Add(New Cities{prop1=value,prop2=value...});
at run time it will do something like
var c = new Cities();
c.prop1=value;
c.prop2=value;
cities.Add(c);
It's kind of a shortcut which also make the code more readable.

How to make a copy of an instance? [duplicate]

This question already has answers here:
Cloning queue in c#
(2 answers)
Closed 2 years ago.
In this following code, what I want to do is to add a new value to my queue and then calculate the average of my current queue. But I don't want to just perform the calculation directly on my current queue itself. Instead I want to first copy the content of the current queue to a new queue and perform the calculation on the new queue. But I know that since classes are reference variables, so if I perform change anything within the new queue, the original one will change along. So how do I make a copy of the original queue and make changes on it without changing the original one?
public class MovingAverage {
public int size;
public Queue<int> que = new Queue<int>();
public Queue<int> copy = new Queue<int>();
/** Initialize your data structure here. */
public MovingAverage(int size)
{
this.size = size;
}
public double Next(int val)
{
if (que.Count == size) que.Dequeue();
que.Enqueue(val);
copy = que;
double sum = 0;
while(copy.Count != 0){
sum = sum + copy.Dequeue();
}
return sum / que.Count;
}
}
To make a copy of a Queue you can pass an enumerable collection to the constructor of a new instance
var newQueue = new Queue<int>(existingQueue);
As your Queue contents are ints (not reference types) they will not be connected to the original elements but beware, if you did this with a Queue full of class of eg Person, that each Queue instance would reference the same Persons and changing eg the Name property of the head Person would mean that each Queue sees the change
var q1 = new Queue<Person>( new [] { new Person() { Name = "Joe" } } );
var q2 = new Queue<Person>(q1);
q1.Peek().Name = "Jane";
Console.WriteLine(q2.Peek().Name); //prints Jane
//in essence, in memory, you have this:
q1 --head--> Person{ Name = "Joe" }
q2 --head----^
Two queue instances, but both of them have a reference to the same Person;
If you alter something about the Person, such as their Name, both Queues see it
You are free to change something about the Queue itself, as they are different instances:
var q1 = new Queue<Person>( new[] { new Person() { Name = "Joe" }, new Person() { Name = "Jane" } } );
var q2 = new Queue<Person>(q1); //both queues have two people in
Console.WriteLine(q1.Peek().Name); //Joe
Console.WriteLine(q2.Peek().Name); //Also Joe
q1.Dequeue(); //bye bye Joe
Console.WriteLine(q1.Peek().Name); //Jane
Console.WriteLine(q2.Peek().Name); //Joe
But the different queues point to the same Persons so you cannot mutate the Person objects independently. To do that, you'll have to clone/make anew the Person objects too, perhaps like:
var q2 = new Queue<Person>(q1.Select(p => new Person { Name = p.Name }));

Is there a way to check if any existing object contains a specific string paramater?

Alright, so here- I have three objects.
public static module foo = new module("John", "A");
public static module bar = new module("Nick", "B");
public static module qux = new module("Alex", "C");
This is my class for the object,
public class module
{
string moduleName { get; set; }
string moduleType { get; set; }
public module(string gName, string gType)
{
moduleName = gName;
moduleType = gType;
}
public string print()
{
return gName;
}
}
My question is; how would I check if a user input (string) matches a paramater such as moduleType and returns said module?
For example;
string gInput = Console.ReadLine();
Let's say the user inputs "A", how could I make a method that would check every object's moduleTypeand if a module does have the moduleType "A", which in this case is foo; would return "John" by using the .print() method.
How could I approach this?
You could save those objects into a list
List<module> list = new List<module> {
new module("John", "A"),
new module("Nick", "B"),
new module("Alex", "C")
};
and then filter out by the user input:
var obj = list.Where(m => m.moduleType == userInput).FirstOrDefault();
Hope it helps.
Using your code as is, you can compare with each object's ModuleType property to see which one matches.
var gInput = Console.ReadLine();
if (foo.ModuleType == gInput)
Console.WriteLine(foo.ModuleName);
if (bar.ModuleType == gInput)
Console.WriteLine(bar.ModuleName);
if (qux.ModuleType == gInput)
Console.WriteLine(qux.ModuleName);
But this smells of bad design. It doesn't make much sense to have a bunch of pre-initialized object instances as class properties. A better design would be to have these objects as a list, and see which one in the list - if at all - match the input.
var list = new List<Module>
{
new Module("John", "A"),
new Module("Nick", "B"),
new Module("Alex", "C")
};
var gInput = Console.ReadLine();
Console.WriteLine(list.FirstOrDefault(x => x.ModuleType == gInput)?.ModuleName);
EDIT: LINQ Crash Course
The FirstOrDefaul(x => x.ModuleType == gInput) is using LINQ to get this done. Let's break it down.
The list in question is of type Module.
When you do list.FirstOrDefault() without anything within the brackets, it will return the first item in the list, and failing that (in case there were no items in the list), either null or default value for that data type. For example, if the list were a simple int list, then it'll return the default value of int which is zero in case list is empty. But this is a list of Module, so if it were empty it'll return null. But in our case we have a non-empty list so it will return an instance of Module, the first one in the list.
Now, we can do more than just returning the first item. We can return the first item that matches a condition. In your case you want to find the item where ModuleType matches user input.
So we use
list.FirstOrDefault(x => x.ModuleType == gInput)
Here, x refers to any item in the list of type Module (kind of similar to mathematical usage of X for unknown terms in an equation). So this reads in English like, find item x from the list such that ModuleType of x is equivalent to gInput.
So if it exists, it will return an object of type Module whose ModuleType matches user input.

Order by field with lambda expression

I am trying to order a list of lists by one of the fields.
I have two lists in one grouped list. The first contains the Id, the second contains the count. I was successfully able to group the list of lists by Id and reformat it.
for (int i = 0; i < GroupedListofLists.Count; i++)
{
tempo_Id.Add(GroupedListofLists[i][0]);
tempo_count.Add(GroupedListofLists[i][1]);
}
GroupedListofLists.Clear();
GroupedListofLists.Add(tempo_Id);
GroupedListofLists.Add(tempo_count);
If I print out the GroupedListofLists I will have distinct Ids (GroupedListofLists[0]) each with their count in the second(GroupedListofLists [1]).
Now when I try to sort this list with lambda expression I have a problem. I tried these two methods:
GroupedlistofLists.Sort( (a, b) => Convert.ToDouble(a[idx]).CompareTo(Convert.ToDouble(b[idx])));
AND
GroupedlistofLists = GroupedlistofLists.OrderBy(x => Convert.ToDouble(x[idx])).ToList();
A problem arises.
In the first method no matter what values I used for the variable 'idx', a will be assigned the values of GroupedListofLists[0] and b the values of GroupedListofLists[1].
In the second, no matter what values I use for the variable 'idx' (0 or 1), x will always contain the values for GroupedListofLists[0]. Which are the Id value and I need to sort them by Count, so GroupedListofLists[1].
I hope I was clear.
Thank you in advance.
I don't exacly know what you're trying to do. But i'll do a guess:
// Build some List you want to sort
List<string> myList = new List<string>() { "3", "1", "2" };
// Sort the List
myList = myList.OrderBy(a => Convert.ToDouble(a));
The Lambda-Expression in OrderBy needs to select a value which will be ordered in default manner. The Lambda-Expression will be run for every item in your list. The results will be used as order-key. you don't need any index.
Sort can be used so sort complex types.
Better version should work on a List with correct class and type. A Convert within a lambda-expression should be avoided!
// Some example-class with multiple properties
public class MyItem
{
public string Name { get; set; }
public double Value { get; set; }
}
// Some test-data
List<MyItem> myList = new List<MyItem>()
{
new MyItem() { Name = "One", Value = 1 },
new MyItem() { Name = "Three", Value = 3 },
new MyItem() { Name = "Two", Value = 2 }
}
// select the value you want to use for ordering the list.
myList = myList.OrderBy(item => item.Value);
// Expected output: 1, 2, 3
foreach(MyItem item in myList)
{
Console.WriteLine(item.Value + ", " + item.Name);
}
// Expected output: 3, 2, 1
myList = myList.OrderBy(item => item.Name);
foreach(MyItem item in myList)
{
Console.WriteLine(item.Value + ", " + item.Name);
}
(I got no Visual Studio installed, so check for typos ;))

Update All References to Cached Object in Dictionary

I have a simple cache of objects:
Dictionary<int, Person> personCache = ...
personCache.Add(1, new Person(){ID = 1, Name = "John"});
personCache.Add(2, new Person(){ID = 2, Name = "Mary"});
personCache[1].Manager=personCache[2];
(In reality, I have proper encapsulation of the dictionary)
So now John's manager is set to Mary. However, if I want to replace Mary with a new instance of person, if I do
personCache[2] = new Person(){ID = 2, Name = "Kate"});
References to Mary are not replaced with references to Kate - i.e. John's manager is not updated.
I can see why this is - the dictionary has a reference to Kate, but John still holds a reference to Mary.
Can anyone suggest a way such that
personCache[2] = new Person(){ID = 2, Name = "Kate"});
Would have the 'expected' result and replace all references to Mary with a reference to Kate?
I think I need to obtain the reference stored in personCache[2] and change that reference to a new person.
Thank you
Ryan
Why not just search for the Manager directly and updated it where it points to the old value
Person oldPerson = personCache[2];
Person newPerson = new Person() { ID = 2, Name = "Kate" };
personCache[2] = newPerson;
foreach (var pair in personCache) {
if (pair.Value.Manager == oldPerson) {
pair.Vaulue.Manager = newPerson;
}
}
"... Would have the 'expected' result "
I believe it already has what most people would consider to the the expected result.
One way to achieve what you appear to want is to make the Name property of your Person class writable, then do:
personCache[2].Name = "Kate";
Another approach would be to store only the Id of the manager in your Person object, rather than a reference to the Manager. I.e. instead of:
personCache[1].Manager = personCache[2];
You could have:
personCache[1].ManagerId = 2;
And you could, for example, have an extension method to get the Manager for a Person:
public static Person GetManager(this Person person)
{
if (person == null) throw new ArgumentNullException("person");
Person manager;
personCache.TryGetValue(person.ManagerId, out manager);
return manager; // returns null if not found in cache
}

Categories