List Collection Class - c#

Hello let me describe i don't have any errors in my code , i am just asking a very basic question here RELATED TO list collection class let see i have a class called customer
class customer
{
public int Id { get; set; }
public string Name { get; set; }
public int Salary { get; set; }
}
in my main method i created an array of customer and initialize those properties that is present in my customer class
static void Main(string[] args)
{
customer[] customers = new customer[3];
customers[0] = new customer
{
Id = 1,
Name = "A",
Salary = 30000
};
customers[1]=new customer
{
Id = 2,
Name = " B",
Salary = 50000
};
customers[2] = new customer
{
Id = 3,
Name = "C",
Salary = 90000
};
List<customer> Cust= new List<customer>(2);
Cust.Add(customers[0]);
Cust.Add(customers[1]);
Cust.Add(customers[2]);
for (int i = 0; i < Cust.Count; i++)
{
customer C = Cust[i];
Console.WriteLine("Id = {0} & Name = {1} & Salary = {2}",C.Id,C.Name,C.Salary);
}
Console.ReadLine();
}
Okay! so this code is working so perfectly nice , but my question is that at last we created a list called cust and add all the custemers in to it , so why is it necessary to make another object with type customer as i did in for loop
customer C = Cust[i];
why can i don't call my code like this
console.WriteLine{Cust[i]}
As far as i know when we create object of the class than we can easily acces the code inside that class with that instance variable . so why not here?

In your for loop, you're not creating a new customer, you're just creating a reference to the existing one:
for (int i = 0; i < Cust.Count; i++)
{
customer C = Cust[i]; //<- not new, just a reference to the customer at index
Console.WriteLine("Id = {0} & Name = {1} & Salary = {2}",C.Id,C.Name,C.Salary);
}
A more concise way to loop is to use foreach instead of for (NOTE: using C# 6.0 string interpolation):
foreach(var c in Cust)
Console.WriteLine($"Id = {c.Id} & Name = {c.Name} & Salary = {c.Salary}");

To do what you were asking to do, you would first need to override the ToString() method on your class. The ToString() method will be called implicitly by Console.WriteLine:
class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public int Salary { get; set; }
public override string ToString()
{
return string.Format("Id = {0} & Name = {1} & Salary = {2}", Id, Name, Salary);
}
}
Now that you have a default way to represent a customer as a string, you can do exactly what you were asking to do:
for (int i = 0; i < Cust.Count; i++)
{
Console.WriteLine(Cust[i]);
}
And, now that you have this, you can do it in an even easier way. The following will implicitly pass each item to Console.WriteLine(), which will call the item's ToString() method:
customers.ForEach(Console.WriteLine);
If for some reason you DON'T want to override the ToString method, you can still access the instance properties using the index without creating a new object, like so:
for (int i = 0; i < Cust.Count; i++)
{
Console.WriteLine("Id = {0} & Name = {1} & Salary = {2}",
Cust[i].Id, Cust[i].Name, Cust[i].Salary);
}

Related

Populate Dictionary

I need to make a "movie ranking" for school.
The language is C#
I want to use a dictionary, where
Key = Movie Name
Value = Assigned stars.
Put it inside a loop and ask for 5 inputs. The code looks something like this:
// Create a dictionary with string key and Int16 value pair
Dictionary<string, Int16> Ranking = new Dictionary<string, Int16>();
for (int i = 1; i < 6; I++)
{
string MovieName = txbPelicula.Text;
Int16 Stars = Convert.ToInt16(txbPuntuacion.Text);
Ranking.Add(MovieName, Stars);
MessageBox.Show("la lista de películas es " + Ranking);
}
Once I exit the loop, sort the Dictionary and Print it ordered by the amount of stars.
Am I well headed?
Some of my classmates used a List, but I don´t understand how will they join Movie Name with it´s Stars.
Thanks
I would use a list of Movie classes or structures. The Movie class needs to contain the name of the movie, and its rating.
class Movie
{
public string Name { get; }
public int Rating { get; set; }
public Movie(string name, int rating)
{
Name = name;
Rating = rating;
}
}
Movie movie = new Movie("Bad movie", 1);
var name = movie.Name;
var rating = movie.Rating;
var movies = new List<Movie>();
//...
foreach (Movie movie in movies.OrderBy(movie => movie.Rating))
{
Console.WriteLine("{0}: {1}", movie.Name, movie.Rating);
}

Add values to Dictionary<int,class> not all at once

I'm trying to store data in a dictionary where the key is an ID and the values are of a class type. The class properties are not all added at the same time so I haven't used a constructor - unless there is a way to add new values using a constructor at a different times? The code below compiles, but I get a run time error saying the key has already been added.Thanks for the help.
public class Students
{
public string FirstName { get; set; }
public string SurName { get; set; }
public int Age { get; set; }
public double Score { get; set; }
}
public void cmdTEST_Click(object sender, EventArgs e)
{
Dictionary<int, Students> Data = new Dictionary<int, Students>();
Data.Add(5, new Students { FirstName = "Bob" });
Data.Add(5, new Students { Age = 34 }); // run time error - "key already added"
Data.Add(5, new Students { Score = 62 });
// extract data
double Score5 = Data[5].Score;
double Age5 = Data[5].Age;
}
You are adding same key multiple times which is not allowed. You can add all properties at once like below
Dictionary<int, Students> Data = new Dictionary<int, Students>();
Data.Add(5, new Students { FirstName = "Bob", Age = 34, Score = 62 });
And if you want to add values later you can use key to add values
Data.Add(5, new Students { FirstName = "Bob"});
Data[5].Age = 34;
Data[5].Score = 62;

What is best practice to store a list of an object within another object

I have an object called product and an object called products.
class product
{
public int Id { get; set; }
public string Name { get; set; }
}
class products
{
public List<product> product { get; set; }
}
In the objects products property product i want to store multiple products. This i how i did it in a small prototype solution:
static void Main(string[] args)
{
products productlist = new products();
List<product> listofproduct = new List<product>();
for (int i = 0; i < 10; i++)
{
product p = new product();
p.Id = i;
p.Name = "product with id = " + i.ToString();
listofproduct.Add(p);
}
productlist.product = listofproduct;
foreach (product prod in productlist.product)
{
Console.WriteLine(prod.Name);
}
Console.ReadKey();
}
}
Is this a good way to solve this? Or do you do it in another way in object oriented programming?
If you don't want to use LINQ and keep your current structure, you could eliminate the need to create full instances in your loop, and the need for listofproduct.
for (int i = 0; i < 10; i++) {
productlist.product.Add(new product {
p.Id = i;
p.Name = "product with id = " + i.ToString();
});
}
However, the best practice for writing code should be:
What is the easiest way for me to maintain and understand this code?
So if the way you wrote the code in your question is what makes you feel comfortable, then by all means do it. However, I strongly recommend you follow the conventions laid out by MSDN for Capitalization Conventions.
struct Product
{
public int Id { get; set; }
public string Name { get; set; }
public Product(int id, string name)
{
this.Id=id;
this.Name=name;
}
public void WriteInfo()
{
Console.WriteLine("Id: {0}", Id);
Console.WriteLine("Name: {0}", Name);
}
}
static void Main(string[] args)
{
var productList=new LinkedList<Product>;
productList.AddLast(new Product(1,Apple));
productList.AddLast(new Product(2,Banana));
foreach(var product in productList)
{
product.WriteInfo();
}
}
You could simplify your code this way:
productlist.product = Enumerable.Range(0, 10)
.Select
(
id => new product
{
id = id,
name = $"product with id = {id}"
}
).ToList();
Well, actually you could express the same code in many ways, but the for loop that you've already implemented in your code is fine.
If you want some advise, you should follow .NET coding conventions:
Classes with pascal-casing: Product.
Variables with camel-case: productList. listOfProduct...
A collection of products should be a plural identifier, and properties are pascal-cased: Products.

Sort text file data into an array

I'm working on a homework problem for my computer science class. A cities census data is on a text file holding records for its citizens. Each line will have four fields(age, gender, marital status, and district) of different data types separated by a comma. For example, 22, F, M, 1.
How should I approach this? My thoughts are that I should use two 1D arrays, one for age and one for district. I need to be able to later count how many people are in each district, and how many people are in different age groups for the whole city.
How do I read each line and get the info I want into each array?
edit**
This is what I've managed to do so far. I'm trying to separate my data from fields into four different arrays. This is where I'm stuck.
FileStream fStream = new FileStream("test.txt", FileMode.Open, FileAccess.Read);
StreamReader inFile = new StreamReader(fStream);
string inputRecord = "";
string[] fields;
int[] ageData = new int[1000];
string[] genderData = new string[1000];
string[] maritalData = new string[1000];
int[] districtData = new int[1000];
inputRecord = inFile.ReadLine();
while (inputRecord != null)
{
fields = inputRecord.Split(',');
int i = 0;
ageData[i] = int.Parse(fields[0]);
genderData[i] = fields[1];
maritalData[i] = fields[2];
districtData[i] = int.Parse(fields[3]);
i++;
inputRecord = inFile.ReadLine();
}
edit 2**
First question, I've decided to use the below code to find out how many citizens are in each district of the census data.
for (int x = 1; x <= 22; x++)
for (int y = 0; y < districtData.Length; y++)
if (districtData[y] == x)
countDist[x]++;
for (int x = 1; x <= 22; x++)
Console.WriteLine("District " + x + " has " + countDist[x] + " citizens");
In my .Writeline when x reaches two digits it throws off my columns. How could I line up my columns better?
Second question, I am not quite sure how to go about separating the values I have in ageData into age groups using an if statement.
It sounds like each of the four fields have something in common... they represent a person surveyed by the census. That's a good time to use a class along the lines of
public class Person
{
public int Age { get; set; }
public string Gender { get; set; }
public string MaritalStatus { get; set; }
public int District { get; set; }
}
Then, just read in all of the lines from the text file (if it's small, it's fine to use File.ReadAllLines()), and then create an instance of Person for each line in the file.
You can create a
List<Person> people;
to hold the Person instances that you parse from the text file.
Since the lines appear to be separated by commas, have a look at String.Split().
UPDATE
The attempt in your edit is pretty close. You keep creating a new i and initializing it to 0. Instead, initialize it outside your loop:
int i = 0;
while (inputRecord != null)
{
fields = inputRecord.Split(',');
Also you may want to trim excess spaces of of your input. If the fields are separated with ", " rather than just "," you will have excess spaces in your fields.
genderData[i] = fields[1].Trim();
maritalData[i] = fields[2].Trim();
How about this?
List<string[]> o = File.ReadAllLines(#"C:\TestCases\test.txt").Select(x => x.Split(',')).OrderBy(y => y[0]).ToList();
Each person is a string array in the list.
Each property is a index in the array eg: age is first.
The above code reads all lines comma delimits them orders them by age and adds them to the list.
public static class PersonsManager
{
public static PersonStatistics LoadFromFile(string filePath)
{
var statistics = new PersonStatistics();
using (var reader = new StreamReader(filePath))
{
var separators = new[] { ',' };
while (!reader.EndOfStream)
{
var line = reader.ReadLine();
if (string.IsNullOrWhiteSpace(line))
continue; //--malformed line
var lParts = line.Split(separators, StringSplitOptions.RemoveEmptyEntries);
if (lParts.Length != 4)
continue; //--malformed line
var person = new Person
{
Age = int.Parse(lParts[0].Trim()),
Gender = lParts[1].Trim(),
MaritalStatus = lParts[2].Trim(),
District = int.Parse(lParts[3].Trim())
};
statistics.Persons.Add(person);
}
}
return statistics;
}
}
public class PersonStatistics
{
public List<Person> Persons { get; private set; }
public PersonStatistics()
{
Persons = new List<Person>();
}
public IEnumerable<Person> GetAllByGender(string gender)
{
return GetByPredicate(p => string.Equals(p.Gender, gender, StringComparison.InvariantCultureIgnoreCase));
}
//--NOTE: add defined queries as many as you need
public IEnumerable<Person> GetByPredicate(Predicate<Person> predicate)
{
return Persons.Where(p => predicate(p)).ToArray();
}
}
public class Person
{
public int Age { get; set; }
public string Gender { get; set; }
public string MaritalStatus { get; set; }
public int District { get; set; }
}
Usage:
var statistics = PersonsManager.LoadFromFile(#"d:\persons.txt");
var females = statistics.GetAllByGender("F");
foreach (var p in females)
{
Console.WriteLine("{0} {1} {2} {3}", p.Age, p.Gender, p.MaritalStatus, p.District);
}
I hope it helps.

Why isn't this Sum() in my index working?

I have the following test:
public class ListingEventTest
{
public ListingEventTest()
{
Artists = new List<ArtistTest>();
}
public List<ArtistTest> Artists { get; set; }
public string Id { get; set; }
public string Name { get; set; }
public double Popularity { get; set; }
}
public class ArtistTest
{
public string Id { get; set; }
public Stat Stats { get; set; }
}
public class Stat
{
public double Popularity { get; set; }
}
public class ArtistsWithStats_ByName : AbstractIndexCreationTask<ListingEventTest>
{
public ArtistsWithStats_ByName()
{
Map = listingEvents => from listingEvent in listingEvents
let artists = LoadDocument<ArtistTest>(listingEvent.Artists.Select(x => x.Id))
select new
{
Popularity = artists.Average(x => x.Stats.Popularity),
listingEvent.Name
};
}
}
[TestFixture]
public class IndexCanDoSums
{
[Test]
public async void WhenListingEventArtistsHaveStatsIndexReturnsPopularity()
{
var store = new EmbeddableDocumentStore
{
UseEmbeddedHttpServer = true,
Configuration =
{
RunInUnreliableYetFastModeThatIsNotSuitableForProduction = true,
RunInMemory = true,
}
}.Initialize();
IndexCreation.CreateIndexes(typeof(ArtistsWithStats_ByName).Assembly, store);
using (var session = store.OpenAsyncSession())
{
for (int i = 0; i < 10; i++)
{
var le = new ListingEventTest
{
Name = "test-" + i
};
await session.StoreAsync(le);
for (int j = 0; j < 2; j++)
{
var artist = new ArtistTest
{
Stats = new Stat
{
Popularity = 0.89d
}
};
await session.StoreAsync(artist);
le.Artists.Add(artist);
}
await session.SaveChangesAsync();
}
}
Thread.Sleep(2000);
using (var session = store.OpenAsyncSession())
{
var query = session
.Advanced.AsyncLuceneQuery<ListingEventTest>("ArtistsWithStats/ByName");
var result = await query.ToListAsync();
result.First().Popularity.Should().NotBe(0);
}
}
}
When I query this index Popularity is always 0.
Any ideas?
Some funny things going on here.
First, you are storing ArtistTest under the ListingEventTest document, not as separate documents, so in your index there is no need to call LoadDocument, you could just do:
from listingEvent in listingEvents
from artist in listingEvent.Artists
select ...
Second, a Map-only index is a lot like a SQL index where you're just calling out the columns you want to be able to query on. Here, you're doing a calculation on a set of buried properties and you have a top-level property where you want to store that information, but how that ends up working is that your calculated property value goes into the Lucene index (so you could query by Popularity if you want) but the data that is returned is straight from the unaltered document. The map defines what goes into Lucene, which points to the document id, and then the document store returns the documents as the results.
This could be modified somewhat by calling Store(x => x.Popularity) in the index's constructor, which would store the value to be recalled later, but honestly offhand I'm not sure if your calculated value or the document's value (which is zero) would win.
Given that, it becomes pretty confusing to have a document property for the sole purpose of trying to fill it during indexing, which is why it's usually a better option to have a class that represents the mapped state, and then implementing AbstractIndexCreationTask<TDocument, TReduceResult> where the TReduceResult class would only contain the result of your mapping, namely the Name and Popularity columns.
Then when you query from, you can use .ProjectFromIndexFieldsInto<T>() to get your results from the stored index results, not from the document store.

Categories

Resources