I have encountered a problem on how to define a two dimensional list(or array). Here is my data:
line1 line2 ... lineN
element1: name, phone, addr element1: name, phone, addr
element2: name, phone, addr element2: name, phone, addr
... ...
elementN: name, phone, addr elementN: name, phone, addr
The problem is that we don't know the exact lines and elements in each line. I tried to define the class People, containing members of name, phone, addr, etc, and use:
list<People> info = new list<People>();
That only defined one line - how can I define multiple lines?
People person = new People;
person.Address = "Address";
person.Phone = "11111";
List<People>() info = new List<People>();
info.Add(person);
will add a new instance of your People class (although Person would be a better name)
or potentially better would be a Dictionary with a suitable key
I'm not really sure what you are after but how about a list of dictionary, i.e. List<Dictionary<string, object>>? This will allow you to represent each person as a dictionary with a differing number of attributes.
Ok, after your question was edited is this what you are after: List<List<People>>?
You should have something like:
class SomePeople
{
SomePeople()
{
people = new list<People>();
}
public list<People> people;
}
List<SomePeople> info = new List<SomePeople>();
Or something like that, you get the point...
Try to use indexers here is the link from Microsoft Development Center http://msdn.microsoft.com/en-us/library/6x16t2tx.aspx || http://msdn.microsoft.com/en-us/library/2549tw02.aspx
Another alternative would be to use a list of arrays, so you would declare :
List<People[]> peopleList = new List<People[]>();
And then use it like so :
peopleList.Add(new People[2]
{
new People { name = "name", phone = "123"...},
new People { name = "name", phone = "123"...}
});
peopleList.Add(new people[3]
{
// x 3 initializers
});
Hope that is equally as helpful!
Create a PeopleCollection class that extend Collection:
public class PeopleCollection : Collection<People>
{
}
It provides the base class for a generic collection. From msn documentation: The Collection class provides protected methods that can be used to customize its behavior when adding and removing items, clearing the collection, or setting the value of an existing item. (http://msdn.microsoft.com/en-us/library/ms132397.aspx)
Then you can define a list of People Collection, or a Dictionary, it depends on your object's access need. Like:
List morePeoples = new List();
Here you can put more "line", as you have described. Each "line" (list element) contains a collection of peoples.
Related
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();
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.
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...
}
If I have list of Author objects like
List<Author> authors = new List<Author>{
new Author { Id = 1, Name = "John Freeman"};
new Author { Id = 1, Name = "Adam Kurtz"};
};
this example is actually wrapped in static method which returns list of authors.
Inside my other object there is property Authors of type List<Author>.
Now I want to assign second author from the list to Authors property.
I thought that I can use Authors = GetAuthors()[1].ToList();
but I cannot access ToList() on index specified author.
to clarify
private static List<Author> GetAuthors() return list of authors (example above).
var someObject = new SomeObject()
{
Authors = // select only Adam Kurtz author using index
// and assign to Authors property of type List<Author>
};
If I understand you correctly, you want a List<Author> with a single author in it. Using ToList() on a single Author object is then not valid syntax.
Try this: Authors = new List<Author>() { GetAuthors()[1] };
You can't assign a single author to list<Author>, so you will have to create a list (of single author) to assign it..
Authors = new List<Author>() {GetAuthor()[1]};
I don't know, why you want to take based on index, ideally you should write a query based on ID of author to get value, so that it won't create any issue in future..
Like: Authors = new List<Author>() {GetAuthor().FirstOrDefault(x=>x.ID==2)};
A sollution with LINQ would be
GetAuthors().Skip(1).Take(1)
Edit: Ignore all that. You're working with lists.
What you actually need is to use GetRange:
GetAuthors().GetRange(1,1);
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.