Add object to this array of custom Classes C# - c#

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.

Related

How can I create lots of objects with different property values?

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();

I need to name a class object with a string value from an array

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.

Remove duplicates from array of objects

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...
}

How to set Option List value on new CRM 2011 Entity record with Linq?

I'm creating new Entity records in C#. The problem is my early-bound Xrm class is expecting the integer value of the Option List in question, but all I have is the string value of the Option List.
So, this is what I'd like to do. The problem is the "OptionListValue" in question is the integer value. You know; The auto-created one that's huge.
Is the only way for me to do this by finding out the value of that particular option? If so, what API do I use to get it and how do I use it? I'm expecting there's some Linq method of doing so. But I'm possibly assuming too much.
public void CreateNewContactWithOptionListValue(string lastName, string theOptionListValue)
{
using ( var context = new CrmOrganizationServiceContext( new CrmConnection( "Xrm" ) ) )
{
var contact = new Contact()
{
LastName = lastName,
OptionListValue = theOptionListValue // How do I get the proper integer value from the CRM?
};
context.Create( contact );
}
}
Method to do it without using web service:
generate enums for option sets (here is how you can do it)
once you have enum, just parse string value. Something like this:
public void CreateNewContactWithOptionListValue(string lastName, string theOptionListValue)
{
using (var context = new CrmOrganizationServiceContext(new CrmConnection("Xrm")))
{
new_customoptionset parsedValue;
if (!Enum.TryParse<new_customoptionset>(theOptionListValue, out parsedValue))
{
throw new InvalidPluginExecutionException("Unknown value");
}
var contact = new Contact()
{
LastName = lastName,
OptionListValue = new OptionSetValue((int)parsedValue)
};
context.Create(contact);
}
}
Be careful with space in option labels, as they are removed in enums

Trouble passing value array into an Object array

I am trying to pass an array of values into an array of a table object so that I can write them to the database.
My database looks like this ->
tblCaseNotes
CaseNoteID | PersonId | etc, etc
tblCaseNotesContactType
rowguid | CaseNoteID | ContactTypeID
tblMaintItems
itemID | CategoryID
The itemID from the Maint table is what is being written to the tblCaseNotesContactType along with the current CaseNoteID. There can be multiple ContactTypes per CaseNote.
What I have so far is an array of the Values for the CheckListBox ContactType created in my btnNew_Click Event:
// Contact Type check list box
int cTypeCount = chkContactType.CheckedItems.Count;
int [] contactTypes = new int[cTypeCount];
// reusable generic counter for looping thru the check lists
int cMaintCounter = 0;
foreach (int checkedItem in chkContactType.CheckedIndices)
{
contactTypes[cMaintCounter] = (int)chkContactType.GetItemValue(checkedItem);
cMaintCounter++;
}
CurrentCaseNote.AddCNote(Program._CurrentPerson.PersonID, Convert.ToDecimal(tbxTimeSpentUnits.Text), chkIsCaseLog.Checked, Convert.ToDateTime(datContactDate.Text), memContactDetails.Text, contactTypes);
Which I then pass to my CurrentCaseNote object AddCNote method.
public static void AddCNote(int personID, decimal tsUnits, bool isCaseLog, DateTime cDate, string cDetails, int[] cTypes)
{
var caseNoteToAdd = new tblCaseNote
{
CaseNoteID = Guid.NewGuid(),
PersonID = personID,
TimeSpentUnits =tsUnits,
IsCaseLog =isCaseLog,
ContactDate =cDate,
ContactDetails =cDetails,
InsertDate = DateTime.Now,
InsertUser = Environment.UserName
};
tblCaseNotesContactType[] cTypeToAdd = new tblCaseNotesContactType[cTypes.Length];
cTypeToAdd[0].CaseNoteID = caseNoteToAdd.CaseNoteID;
cTypeToAdd[0].ContactTypeID =cTypes[0];
cTypeToAdd[0].rowguid = Guid.NewGuid();
CaseNoteDAL.addCNote(caseNoteToAdd,cTypeToAdd);
It is then passed to the DAL to be written to the local database:
public static void addCNote(tblCaseNote caseNote, tblCaseNotesContactType[] cType)
{
foreach (var type in cType)
{
caseNote.tblCaseNotesContactTypes.Add(type);
}
dcCaseNotes.tblCaseNotes.InsertOnSubmit(caseNote);
//dcCaseNotes.tblCaseNotes.InsertOnSubmit(caseNoteToAdd);
dcCaseNotes.SubmitChanges();
}
It is giving me a NUllReferenceException was unhandled error on this line -->cTypeToAdd[0].CaseNoteID = caseNoteToAdd.CaseNoteID;
Is it because I am only working with the [0]? I just did that to simplify my testing of this. When I step through the code my value array is correct and there is a Guid for caseNoteToAdd.CasNoteID
Can someone give me a few pointers on where I am going wrong here and what might be causing the error? As you can tell from my code I am new to this and I am learning on the fly.
Thanks,
~P
The problem is that you've created an array of reference types, but not populated it.
In other words, after this line:
tblCaseNotesContactType[] cTypeToAdd = new tblCaseNotesContactType[cTypes.Length];
you've got an array of null references - the value of each element is null.
You then need to write:
cTypeToAdd[0] = new tblCaseNotesContactType();
(or a similar statement) before you can start changing its properties.
An alternative would be to use an object initializer and do it in one statement (after creating the array):
cTypeToAdd[0] = new tblCaseNotesContactType
{
CaseNoteID = caseNoteToAdd.CaseNoteID,
ContactTypeID =cTypes[0],
rowguid = Guid.NewGuid()
}:

Categories