Determining MPG over a range of time with a Dictionary - c#

A group of friends are tracking the miles per gallon for each of their cars. Each time one of them fills up their gas tank, they record the following in a file:
His or her name, The type of car they drove, How many miles driven since they last filled up, How many gallons purchased at this fill up, Date of the fill.
Their data is formatted as a comma separate value (csv) file with the following format for each row:(#person,carName,milesDriven,gallonsFilled,fillupDate)
Miles are recorded as floating-point numbers and gallons as integers.
Task is to create a program that allows members of this group to determine the miles per gallon (MPG) of each of their cars during a specific time range. Note: person may have more than one so a time range query might need to output data for one or more cars.
Query: GetRangeMPG(PersonName, StartDate, EndDate)
Returns list of objects containing (CarName, MPG)
MPG is calculated as (total miles traveled during time period)/(total gallons filled during time period.
Trying to figure out the best way to store this kind of data, this is what I have below:
Storing the csv data in a dictionary with PersonName as the key and List of Objects of type Car. GetRangeMPG method would search through this dictionary with the PersonName argument provided and try to find the MPG for the cars that the person owns.
class Car
{
public string CarName { get; set; }
public double MilesDriven { get; set; }
public int GallonFilled { get; set; }
public DateTime DateFilled { get; set; }
}
class MPGCalc
{
Dictionary<string, List<Car>> log = new Dictionary<string, List<Car>>();
public void LoadData(string inp)
{
string[] lines = inp.Split(new[] { Environment.NewLine },StringSplitOptions.None);
string personName, carName;
double milesDriven;
int gallonsFilled;
DateTime fillupDate;
List<Car> carList = new List<Car>();
foreach(var line in lines)
{
string[] info = line.Split(',');
//(#person,carName,milesDriven,gallonsFilled,fillupDate)
personName = info[0];
carName = info[1];
Double.TryParse(info[2], out milesDriven);
int.TryParse(info[2], out gallonsFilled);
DateTime.TryParse(info[4], out fillupDate);
if(log.ContainsKey(info[0]))
{
log.TryGetValue(personName, out carList);
carList.Add(new Car {CarName = personName, MilesDriven = milesDriven, GallonFilled = gallonsFilled, DateFilled = fillupDate});
log[personName] = carList;
}
else
{
carList.Add(new Car {CarName = personName, MilesDriven = milesDriven, GallonFilled = gallonsFilled, DateFilled = fillupDate});
log.Add(personName, carList);
}
}
}
How is my progress so far? Is this best approach to tackle problems similar to these?

Related

Take console input and sort it from highest to lowest

As the title say, I want to take inputs from the user via the console and use a list or array to sort them, then output the list.
Basically, the list is to have three variables, one string and two integers
(example:
infexis, 20, 19
john, 24, 3
jane, 17, 28
and the list should be output in the same format, though just sorted based on the first number, so john would be first, then infexis, then jane. the name and second number doesnt matter in the sorting.)
I'm really new to c#, and I'm wondering how to take user input and add it to a list with no specified amount of variables, (so the list can take 2 entries, or 12, not just a specified number.)
Any help would be appreciated!
I highly suggest you introduction of class instead of string array in which you'll store user's input.
Example:
public class Person
{
public string Name { get; set; }
public int FirstNumber { get; set; }
public int SecondNumber { get; set; }
}
public static void Main()
{
//list of persons
List<Person> people = new List<Person>();
//Your piece of business logic ...
//Creating person object
var person = new Person();
person.Name = Console.ReadLine();
person.FirstNumber = Console.ReadLine();
person.SecondNumber = Console.ReadLine();
people.Add(person);
//order by FirstNumber (needed to add: System.Linq namespace)
var orderedPeople = people.OrderBy(p => p.FirstNumber);
//output
foreach(var p in orderedPeople)
{
Console.WriteLine(String.Format("{0} {1} {2}", p.Name, p.FirstNumber, p.SecondNumber));
}
}
Note: I didn't test this code, so you can expect some compile time bugs, but you can get broader way how to handle your problem easier.
Also, you can expand your class to meet additional requirement, like some other (optional) properties (i.e. user inputs)

Use C# Linq Lambda to combine fields from two objects into one, preferably without anonymous objects

I have a class setup like this:
public class Summary
{
public Geometry geometry { get; set; }
public SummaryAttributes attributes { get; set; }
}
public class SummaryAttributes
{
public int SERIAL_NO { get; set; }
public string District { get; set; }
}
public class Geometry
{
public List<List<List<double>>> paths { get; set; }
}
and i take a json string of records for that object and cram them in there like this:
List<Summary> oFeatures = reportObject.layers[0].features.ToObject<List<Summary>>();
my end goal is to create a csv file so i need one flat List of records to send to the csv writer i have.
I can do this:
List<SummaryAttributes> oAtts = oFeatures.Select(x => x.attributes).ToList();
and i get a nice List of the attributes and send that off to csv. Easy peasy.
What i want though is to also pluck a field off of the Geometry object as well and include that in my final List to go to csv.
So the final List going to the csv writer would contain objects with all of the fields from SummaryAttributes plus the first and last double values from the paths field on the Geometry object (paths[0][0][first] and paths[0][0][last])
It's hard to explain. I want to graft two extra attributes onto the original SummaryAttributes object.
I would be ok with creating a new SummaryAttributesXY class with the two extra fields if that's what it takes.
But i'm trying to avoid creating a new anonymous object and having to delimit every field in the SummaryAttributes class as there are many more than i have listed in this sample.
Any suggestions?
You can select new anonymous object with required fields, but you should be completely sure that paths has at least one item in each level of lists:
var query = oFeatures.Select(s => new {
s.attributes.SERIAL_NO,
s.attributes.District,
First = s.geometry.paths[0][0].First(), // or [0][0][0]
Last = s.geometry.paths[0][0].Last()
}).ToList()
Got it figured out. I include the X and Y fields in the original class definition. When the json gets deserialized they will be null. Then i loop back and fill them in.
List<Summary> oFeatures = reportObject.layers[0].features.ToObject<List<Summary>>();
List<Summary> summary = oFeatures.Select(s =>
{
var t = new Summary
{
attributes = s.attributes
};
t.attributes.XY1 = string.Format("{0} , {1}", s.geometry.paths[0][0].First(), s.geometry.paths[0][1].First());
t.attributes.XY2 = string.Format("{0} , {1}", s.geometry.paths[0][0].Last(), s.geometry.paths[0][1].First());
return t;
}).ToList();
List<SummaryAttributes> oAtts = summary.Select(x => x.attributes).ToList();

Assigning value to a two dimensional array

I have a client server program. The client are the students so I've made a seat plan that generates two dimensional button arrays.
Button[,] btnSeat = new Button[8, 6];
How can I assign the client's hostname to each button once a client is online? Assuming there is already a hostname. Assign the first user who goes online to the first array[0,0] and the second to array[0,1] and so on....?
This is the layout of my seat plan
I also need to retrieve array[x,y] that is equals to that hostname. Please help, thank you.
Here is an example of what I think would be a better data structure:
For the Computer, you need to hold some information, so create a class for the computer:
public class Computer
{
public string HostName { get; set; }
public int Row { get; set; }
public int Column { get; set; }
//Whatever else you want to add, like
public bool IsActive { get; set; }
public bool IsScrewingAround { get; set; }
}
Then you can use a dictionary to store the data:
public Dictionary<string, Computer> _computers =
new Dictionary<string, Computer>(StringComparer.OrdinalIgnoreCase);
And you add computers to it like:
_computers.Add("someHostName", new Computer()
{
HostName = "someHostName",
Row = 0,
Column = 4,
IsActive = true,
IsScrewingAround = false
});
Then you can look up Computers by host name in an O(1) operation like:
var cpu = _computers["hostName"];
Or get a list of computers that are screwing around:
var screwOffs = _computers.Values.Where(c => c.IsScrewingAround);
Want all the computers in row 4?
var row4 = _computers.Values.Where(c => c.Row == 4);
This gives you an advantage over an array that it can grow based on the number of Computers added to it in case your room shape changes.
Really this could be a List<Computer> if you don't need the O(1) look-up for the host name, which I'm guessing would be fine given the relatively small number of items you have in the classroom.

c# multi variable array

Is there a way in c# to have an array of multiple variables?
for instance, I have data for a stock:
Date |Open |High |Low |Close
10-01-2012| 10.00| 11.01| 9.56| 10.56
10-02-2012| 10.56| 10.99| 9.21| 9.99
10-03-2012| 9.99 | 10.12| 9.78| 10.11
What I would like to do, is create an array which takes a DateTime and a String variable, and outputs a double/string.
So, if I wanted to get the Open price of the stock on 10-01-2012, I could say
DateTime Day = Convert.ToDateTime("10-01-2012");
double openPrice = MyArray[Day,"Open"];
and it would return 10.00, as either a double or a string.
What is the best way to do this? Is this even possible with an array? If not, what other methods can I use? I have been thinking about this for a while, and I'm not sure the best way to structure this array/object
Thanks for any help!
Possibly better to make a single class to contain your data, and create an array or List of those;
class DailyPrice
{
DateTime Date { get; set; }
decimal Open { get; set; }
decimal Close { get; set; }
decimal High { get; set; }
decimal Low { get; set; }
}
static class Program
{
static void Main()
{
List<DailyPrice> prices = new List<DailyPrice>();
prices.Add(new DailyPrice() { Date = DateTime.Today, Open = 11.11M, Close=... });
prices.Add(new DailyPrice() { Date = DateTime.Today, Open = 12.14M, High=... });
...
}
}
Incidentally, due to precision problems when performing arithmetic with the double type in C#, it's safer to user decimal for monetary values (which I assume is what you have here).
You could use a dictionary within a dictionary, as follows:
var stocks = new Dictionary<DateTime, Dictionary<String, Double>>
Then, to access a price:
Double price = stocks[Day]["Open"]
as far as I know you can't do it with arrays, but you can achieve this with a Dictionnary<>:
Dictionnary<DateTime, double[]>
that way you will be able to have your values "indexed" by day, and then on position 0 of the double array you would have your "Open" value
to get the "10.00" value you need you would have to do this:
openvalue = mydic[Day][0];

comparing objects and returning list

I have a class definition that looks like this:
public class MyObjectModel
{
public int ObjectID { get; set; }
//for when the user's data is split in 2 fields
public string FirstName { get; set; }
public string LastName { get; set; }
//for when the user's data is all in one field
public string FirstLastName { get; set; }
}
I have a list of these MyObjectModel and I want to sort them by name with a custom sort process because that involves checking if the data contains a LastName (in this case sort using the LastName) or just FirstLastName (in this case I'm going to break the string and sort on the second term, if there's one, or just the whole string if there's only one word.)
I'm not sure on two things:
should I use IComparer or IComparable?
Once it determines the order of the sort (I can do that), how do I make it so that the output of the method is a list of ints representing ObjectID.
Use Linq:
List<MyObjectModel> objects = new List<MyObjectModel>();
List<int> ids = objects.OrderBy(o => FunctionWhichReturnsNameForSort(o)).Select(o => o.ObjectID).ToList();
FunctionWhichReturnsNameForSort can be implemented in another class, or an extension, or as a member.
// should be in a static helper class
public static string NameForSort(this MyObjectModel obj)
{
if (!string.IsNullOrEmpty(obj.LastName)) return obj.LastName;
return obj.FirstLastName.Split(... // your splitting logic goes here
}
var ids = objects.OrderBy(o => o.NameForSort()).Select(o => o.ObjectID).ToList();
When you really need this strange double solution then you will run into this and similar problems more often. As a more general solution, consider putting the business logic for names in a few read-only properties:
//for when the user's data is split in 2 fields
public string FirstName { get; set; }
public string LastName { get; set; }
//for when the user's data is all in one field
public string FirstLastName { get; set; }
public string FullName
{
get { ... } // pick from LastName, FirstName, FirstLastName
}
public string SortName
{
get { ... } // pick from LastName, FirstLastName
}
Once it determines the order of the sort (I can do that), how do I make it so that the output of the method is a list of ints representing ObjectID
result = MyObjectList
.OrderBy(m => m.SortName) // sort on SortName
.Select(m => m.ObjectID) // select the Id
.ToList();
If this sorting is specific to one use case, it can be achieved using LINQ:
var sortedIds = models.OrderBy(SecondName).Select(m => m.ObjectId).ToList();
private static string SecondName(MyObjectModel model)
{
if (!string.IsNullOrWhitespace(model.LastName)) return model.LastName;
return model.FirstLastName.Split(' ').Last();
}
While you can use LINQ, as others have suggested, that would involve creating a brand new list, not mutating the existing list. That may or may not be preferable. If you want to sort the list itself that's easy enough too:
List<string> list = new List<string>(){"a","b","c"};
list.Sort((a,b)=> a.CompareTo(b));
Just take your list, call Sort, and pass in a lambda that takes two items and returns an integer indicating which is greater. In your case, just call some method on a and b to get a string and then use CompareTo or string.Compare on those two strings.

Categories

Resources