I'm programming in C# and I want to instantiate lots of new objects to my application, all of the same type, but with different values for their properties. Example:
Student student1 = new Student();
student1.Name = "James";
student1.Age = 19;
student1.City = "Los Angeles";
Student student2 = new Student();
student2.Name = "Karen";
student2.Age = 20;
student2.City = "San Diego";
Student student3 = new Student();
student3.Name = "Bob";
student3.Age = 20;
student3.City = "Dallas";
This way of coding seems really wrong to me because what if I didn't need 3, but 500 students? What would be the best way to do it then?
I tried to use a for loop for this but that doesn't work because the property values differ.
What is the most efficient way to do this?
Thanks in advance.
In order to do anything with your objects at runtime you will probably want them in a list.
Without reading from a file or database, etc., the most concise way might be :
var Students = new List<Student>{
new Student { Name = "Bob", Age = 22, City = "Denver" },
new Student { Name = "Sally", Age = 33, City = "Boston" },
new Student { Name = "Alice", Age = 12, City = "Columbus" }
};
I don't know your end goal however, is this just mock data, like for a test?
Add constructor to Student like this
Student (string name, int age, string city)
{
Name = name;
Age = age;
City = city;
}
///
Student student1 = new Student("James", 19, "Los Angeles");
Well, if what you mean by more efficient way to do it is just to write less code, you could instanciate them assigning the property's values at once, just like:
Student student1 = new Student() { Name = "James", Age = 19, City = "Los Angeles" };
If you want not just to write less code, but to - let's say - read the data from another source (like a Json list, or a TXT file) you will have to write a loader for it.
Well, it depends what you are going to use it for. If it’s for testing, then you could use a custom built tool to create random Students:
public class RandomStudentCreator
{
private readonly Random rnd = new Random();
private readonly IList<string> cities, names;
private readonly int minAge, maxAge;
public RandomStudentCreator(
IList<string> names,
IList<string> cities,
int minimumInckusiveAge,
int maximumExclusiveAge)
{
//Argument validation here
this.cities = cities;
this.names = names;
minAge = minimumInckusiveAge;
maxAge = maximumExclusiveAge;
}
public Student Next()
{
var student = new Student();
student.Name = names[rnd.Next(names.Count);
student.City = cities[rnd.Next(cities.Count);
Student.Age = rnd.Next(minAge, maxAge);
}
}
If this is production code, then you should be creating students based on:
User input
Some data backend (DB, text file, etc.)
But in any case, you don’t want to create a variable for each student. You probably want a collection of students. Depending on what you want to do with them, the type of collection you need may vary, the framework gives you plenty of options:
Arrays: Student[]
Lists: List<Student>
Queues: Queue<Student>
Stacks: Stack<Student>
Sets: HashSet<Student>
Etc.
And last but not least, you probably want to implement a constructor in Student that takes a name, city and age to make instantiation a little bit more compact than what you currently have:
public class Student
{
public Student(string name,
int age,
string city)
{
Name = name;
Age = age;
City = city;
}
//...
}
var john = new Student(“John”, 19, “LA”);
Programming is not about typing data. Need a lot of data? - Load them from files, databases, servers, through GUI, etc.
You can make a handy constructor, you can make factories and builders, but they are not for creating hundreds of objects in a row. Even if it is historical data, one day you will want to change them, fix something in them. Believe me, it's much easier to separate them from the code and store somewhere else, than to edit hundreds of lines of code later.
If you want 500 students I suggest extracting data to a file, database etc. student1..student499 implementation looks very ugly: let's organize them into array: Student[] students. As an example, let's use the simplest csv file Students.csv solution in the format
name,age,city
E.g.
name,age,city
James,19,Los Angeles
Karen,20,San Diego
Bob,20,Dallas
Having the file completed you can easily read it:
using System.IO;
using System.Linq;
...
Student[] students = File
.ReadLines("Students.csv")
.Where(line => !string.IsNullOrWhiteSpace(line)) // Skip empty lines
.Skip(1) // Skip header
.Select(line => line.Split(','))
.Select(items => new Student() {
Name = items[0],
Age = int.Parse(items[1]),
City = items[2], })
.ToArray();
Related
I would like to rearrange my properties inside a object initializer, by manually defining the new position. I found this Question but the first method only sorts the properties alphabetically, the other uses a different extension method.
new Student { StudentID = 2, Nationality = Nationalities.Austrian, StudentName = "Peter", Age = 21 }
A line like the above, I would like to rearrange using ReSharper or a Built-In Feature to this:
new Student { StudentID = 2, StudentName = "Peter", Age = 21, Nationality = Nationalities.Austrian }
Is there any possibility to achieve this?
im really new to C# so please excuse my sloppy code
public Kid(string name, int age, string location)
{
this.age = age;
this.name = name;
this.location = location;
}
NAL = Console.ReadLine().Split(',');
Console.WriteLine("Name-{0} Age-{1} Location-{2}", NAL);
string name = "Kid" + NAL[0];
Kid [name] = new Kid(NAL[0], Int32.Parse(NAL[1]), NAL[2]);
i need ^ this to work but i dont understand any of the types can some one help explain this to me
All you need to change is the "Kid" collection.
Dictionary<string, Kid> Kids = new Dictionary<string, Kid>();
Kids[name] = new Kid(NAL[0], Int32.Parse(NAL[1]), NAL[2]);
Using the Dictionary you can retrieve by name, adding an entry with the same name will overwrite the previous entry.
You can't dynamically name objects like you want to in C#. You can however implement something that meets your needs using a Dictionary.
var kids = new Dictionary<string, Kid>()
kids.Add(name, new Kid(NAL[0], int.Parse(NAL[1]), NAL[2]);
You can then access the Kid named Sam by doing
kids["KidSam"]
This gives you access to what are essentially named Kid objects without the need to name every object.
Sorry for the incoherent title. I don't know how to concisely explain my problem, which is why I didn't really know how to look it up. I'll explain using an example...
Let's say I have a class:
public class cas
{
public string name { get; set; }
public int num { get; set; }
}
With that class, I make several objects and stick them into a list. For the sake of example, I will make 4:
var list = new List<cas>
{
new cas { name = "firstname", num = 1 },
new cas { name = "firstname", num = 2 },
new cas { name = "lastname", num = 3 },
new cas { name = "lastname", num = 4 }
};
Is there a way to take this List and combine any objects with the same name field?
So, the new list would be 1 object with:
name = "firstname", num = 3,
name = "lastname", num = 7
There's the obvious "long" way to do it, but it would be clunky and expensive (go through the list several times to find like-objects). I was wondering if I was missing any clean way of doing it. I intentionally made a simple example so that the answer would be a proof of concept rather than writing my code for me. My actual problem is more complex than this, but I can't figure out this one aspect of it.
Using Linq, you have a GroupBy Method and a Select Method:
list = list.GroupBy(x=> x.name)
.Select(x=> new cas() { name = x.Key, num = x.Sum(y=> y.num) }).ToList();
Or using Elegant query-syntax:
list = (from item in list
group item by item.name into grouping
select new cas()
{
name = grouping.Key,
num = grouping.Sum(x => x.num)
}).ToList();
Note that to use these methods, you have to add using System.Linq at the top of your source file.
You can use linq, you would have to group them on name property and then sum on the num property of each group like:
var result = list.GroupBy(x=>x.name)
.Select(g=> new cas
{
name = g.Key,
num = g.Sum(x=>x.num)
});
I have a class called Customer that has several string properties like
firstName, lastName, email, etc.
I read in the customer information from a csv file that creates an array of the class:
Customer[] customers
I need to remove the duplicate customers having the same email address, leaving only 1 customer record for each particular email address.
I have done this using 2 loops but it takes nearly 5 minutes as there are usually 50,000+ customer records. Once I am done removing the duplicates, I need to write the customer information to another csv file (no help needed here).
If I did a Distinct in a loop how would I remove the other string variables that are a part of the class for that particular customer as well?
Thanks,
Andrew
With Linq, you can do this in O(n) time (single level loop) with a GroupBy
var uniquePersons = persons.GroupBy(p => p.Email)
.Select(grp => grp.First())
.ToArray();
Update
A bit on O(n) behavior of GroupBy.
GroupBy is implemented in Linq (Enumerable.cs) as this -
The IEnumerable is iterated only once to create the grouping. A Hash of the key provided (e.g. "Email" here) is used to find unique keys, and the elements are added in the Grouping corresponding to the keys.
Please see this GetGrouping code. And some old posts for reference.
What's the asymptotic complexity of GroupBy operation?
What guarantees are there on the run-time complexity (Big-O) of LINQ methods?
Then Select is obviously an O(n) code, making the above code O(n) overall.
Update 2
To handle empty/null values.
So, if there are instances where the value of Email is null or empty, the simple GroupBy will take just one of those objects from null & empty each.
One quick way to include all those objects with null/empty value is to use some unique keys at the run time for those objects, like
var tempEmailIndex = 0;
var uniqueNullAndEmpty = persons
.GroupBy(p => string.IsNullOrEmpty(p.Email)
? (++tempEmailIndex).ToString() : p.Email)
.Select(grp => grp.First())
.ToArray();
I'd do it like this:
public class Person {
public Person(string eMail, string Name) {
this.eMail = eMail;
this.Name = Name;
}
public string eMail { get; set; }
public string Name { get; set; }
}
public class eMailKeyedCollection : System.Collections.ObjectModel.KeyedCollection<string, Person> {
protected override string GetKeyForItem(Person item) {
return item.eMail;
}
}
public void testIt() {
var testArr = new Person[5];
testArr[0] = new Person("Jon#Mullen.com", "Jon Mullen");
testArr[1] = new Person("Jane#Cullen.com", "Jane Cullen");
testArr[2] = new Person("Jon#Cullen.com", "Jon Cullen");
testArr[3] = new Person("John#Mullen.com", "John Mullen");
testArr[4] = new Person("Jon#Mullen.com", "Test Other"); //same eMail as index 0...
var targetList = new eMailKeyedCollection();
foreach (var p in testArr) {
if (!targetList.Contains(p.eMail))
targetList.Add(p);
}
}
If the item is found in the collection, you could easily pick (and eventually modify) it with:
if (!targetList.Contains(p.eMail))
targetList.Add(p);
else {
var currentPerson=targetList[p.eMail];
//modify Name, Address whatever...
}
This is a noob question, I apologize. I have been trying for a while to figure out how to add an object to this array. I have an Employee class, and a Salary class that inherits from Emp, and an Hourly class that does. I created an array like this,
public Employee[] _Employees;
...
Employee[] _Employees = new Employee[]{
new Hourly("1000", "Harry","Potter", "L.", "Privet Drive", "201-9090", "40.00", "12.00"),
new Salary("2201", "A.", "A.", "Dumbledore", "Hogewarts", "803-1230", "1200"),
new Hourly("3330", "R.","Weasley", "R.", "The Burrow", "892-2000", "40", "10.00"),
new Salary("4040", "R.", "R.", "Hagrid", "Hogwarts", "910-8765", "1000")
};
And now I want to add objects to the array that I have read in from a text file. I am doing it like this right now;
if(rstring == "H")
{
string fullName = data.ReadLine();
string empNum = data.ReadLine();
string address = data.ReadLine();
string phoneNum = data.ReadLine();
string hrWorked = data.ReadLine();
string hrRate = data.ReadLine();
string[] splitName = fullName.Split(new Char[] { ' ' });
string fName = splitName[0];
string mName = splitName[1];
string lName = splitName[2];
_MyForm._Employees = new Employee[] {new Hourly ( empNum, fName, mName, lName, address, phoneNum, hrWorked, hrRate ) };
which doesn't give me any error, but when I look at what is stored in the _Employees class, it just has the info from above and nothing else. Let me know if I need to explain myself better. Just let me know if I need to go about it another way, or what I need to do to add the read info to this _Employees class.
Instead of having an array, you should consider using a List<Employee>. This will let you add any number of elements to the list, without recreating it yourself:
private List<Employee> employees = new List<Employee>();
public IList<Employee> Employees { get { return this.employees; } }
// ...
this.employees.Add(new Hourly("1000", "Harry","Potter", "L.", "Privet Drive", "201-9090", "40.00", "12.00"));
// .. Add all
Then, when you want to add a new one, you can use:
_MyForm.Employees.Add(new Hourly ( empNum, fName, mName, lName, address, phoneNum, hrWorked, hrRate));
You'd be better off using a List in this case:
IList<Employee> _Employees;
List has the advantage of having an Add method which can be used to add new objects to the end of the array.
_MyForm._Employees.Add(new Hourly(...))
If you wanted to insert objects at other points in the collection there's also the Insert method which takes an index along with the object to be added.
Use the List class as others have pointed out and the Add method. What you have done here:
_MyForm._Employees = new Employee[]
in your example code is you've re-assigned your reference of MyForm._Employees to a new object of type Employee[], not added to it. Your previous reference containing your other valuable employees will have most likely been garbage collected (if nothing else in scope is referencing it).
I would steer clear of arrays to be honest. See this for more information.