c# multi variable array - c#

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];

Related

which data type can store both number and text at a same time in c#

How I declare a data type that can store both numbers and text at a same time or need a hint on how to do that in c#.
A string like PMXYDe1000071 is a string, nothing else would fit this.
More importantly though, a serial number is a business concept (Domain knowledge comes into play). Like most business concepts there usually isn't a data type.
In the case of a serial number for example there is going to be some meaning to each of the parts of the number:
Let's assume we have
a Facility
a Line
the actual number for the product.
a serial number should be a 3 character code Facility,
A 3 character line,
a 10 digit number
We can represent this in code like so:
public enum Line
{
ABC,
PMV,
DGE,
RAR
}
public enum Facility
{
IWA,
CHI,
DET,
MEX
}
public class Serial
{
public Line Line { get; set; }
public Facility Facility { get; set; }
public int SequenceNumber { get; set; }
public override string ToString()
{
var sequenceString = SequenceNumber.ToString().PadLeft(10, '0');
return $"{Line.ToString()}{Facility.ToString()}{sequenceString }";
}
}
And then in code we use each object like:
var s2 = new Serial();
s2.Facility = Facility.MEX;
s2.Line = Line.PMV;
s2.SequenceNumber = 50014;
Console.WriteLine(s2.ToString());
A serial object to a string gives you a format you want, but all of the business concepts are still safely in their own objects.
The object above in string format is: PMVMEX0000050014 but we can still perform logical operations based off of any one property.

Determining MPG over a range of time with a Dictionary

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?

Linq: Transforming specified column's datatypes and values while preserving unspecified columns

I have a list of Order objects. Order has the properties: int Id, decimal Price, string OrderNumber, string ShipperState, DateTime TimeStamp;
I know which columns I want to transform (Price, TimeStamp) and I want to keep the other columns without needing to specify them.
This example is transforming specified columns but I still need to include the non-transformed columns.
var myList = model.Orders.Select(x => new
{
x.Id,
x.OrderNumber,
// decimal to string
Price = x.Price.ToString("C", new CultureInfo("en-US")),
x.ShipperState,
// DateTime to string
TimeStamp = x.TimeStamp.ToString("MM/dd/yyyy H:mm")
}
If I were to add a column string ShipperCity to the Order class, I would like myList to also have that property without having to go back and update the projection.
An ideal answer would not rely on external libraries, reflection and only be a line or two.
If you do not want to modify the model class as #David suggested you can write extension methods for it like this:
public static class OrderExtensions
{
public static string GetFormattedPrice(this Order order)
=> order.Price.ToString("C", new CultureInfo("en-US"));
public static string GetFormattedTimestamp(this Order order)
=> order.Timestamp.ToString("MM/dd/yyyy H:mm");
}
UPDATE #1
The effect of this alternative is that whereever you wanted to use the transformed order.Price and order.Timestamp there you have to use order.GetFormattedPrice() and order.GetFormattedTimestamp() respectively.
In the question it was not specified that where the data come from and what type of application the data is used in.
For example methods cannot be used in XAML binding and everywhere else where a property is required.
Please note:
In C# (almost) everything is strongly typed hence once the class and the properties in it are defined you cannot set one of its property value to a different type of data and also you cannot change the type of the property. So by default you cannot avoid projection when you need some transformation. If you need all the properties - either the original value or the transformed value - you have to list all of them in the projection.
almost everything except dynamic
You can actually transform the type and the value of a property but only if it is defined as dynamic. For example this works below:
public class Order
{
public int Id { get; set; }
public string OrderNumber { get; set; }
// Original: decimal; Converted: string;
public dynamic Price { get; set; }
public string ShipperState { get; set; }
// Original: DateTime; Converted: string;
public dynamic Timestamp { get; set; }
}
public static class OrderExtensions
{
public static void Transform(this Order order)
{
if (order.Price.GetType() == typeof(decimal))
order.Price = order.Price.ToString("C", new CultureInfo("en-US"));
if (order.Timestamp.GetType() == typeof(DateTime))
order.Timestamp = order.Timestamp.ToString("MM/dd/yyyy H:mm");
}
}
class Program
{
static void Main(string[] args)
{
var originalList = new List<Order>()
{
new Order() { Id = 1, OrderNumber = "1", Price = 100m, Timestamp = DateTime.Now },
new Order() { Id = 2, OrderNumber = "2", Price = 200m, Timestamp = DateTime.Now },
new Order() { Id = 3, OrderNumber = "3", Price = 300m, Timestamp = DateTime.Now }
};
originalList.ForEach(order => order.Transform());
}
}
Although this example works there are some things to know:
dynamic type
This example looks like a hack, maybe it can be considered as a hack. :)
In this example the original Order objects are changed not their projection/clone/etc.
dynamic properties are not allowed in Entity Framework models as you cannot specify the SQL column type for them even using the methods of DbModelBuilder. I did not try it in other use-cases but it seems to be a very restricted possibility.
For dynamic properties there is no IntelliSense, so after typing order.Price. no list would appear with any method or property.
You have to use these properties very carefully as there is no compile-time check. Any typo or other mistake will throw an exception only during run-time.
If this option somehow fits the needs it might be worth implementing the conversion of the string value back to the original type.
That's all the update I could add to my original answer. Hope this is an acceptable answer to your comment.

.NET Data Annotations for String formatting

I'm working on a ASP.NET MVC project and need some help for String formatting with data annotations.
So I have a String prop Id that has to be in this form: nnn/nnnnn/nnn (where n is for decimal value) AND the 3 last digits has to be: 999-the first 3 digits.
For example: 123/12345/876
Can someone help me with this and understanding how the formatting actually works?
Thanks :-)
In relation to your recent comment:
[DisplayFormat(DataFormatString = "{0;###/####")]
I believe, however I don't think you can do the logic required on the last part of the ID.
Hene the below solution. You could always just make the property {get; have that logic
...
int id = 123;
int middleId = 12345;
string propertyId = string.Format("{0}/{1}/{2}",id,middleId,GetTrailingId(id).ToString());
...
private int GetTrailingId(int prefixId)
{
const int upperLimit = 999;
return upperLimit - prefixId;
}
I found the answer: As I'm a beginner in C#, I wasn't aware that there was a ValidationAttribute class which allows me to make my own DataAnnotation to validate the ID. This is what I did:
Make a class IDVerification that enhirits of ValidationAttribute
Use this class in my Main class where I use ID by writing [IDVerification] above it.
If someone wants to see the class, it's here.
var input = "123/12345/876";
var ints = input.Split('/').Select(int.Parse).ToArray();
Debug.Assert(ints[0] + ints[2] == 999);
It's not entirely clear to me exactly what you're trying to accomplish but this snippet might be helpful. If you provide more information, we might be able to help more. This snippet formats the decimals as if they were integers and assumes there are two numbers being output. Was the first slash supposed to represent the decimal point and so in actuality, there was only one number involved in that string?
class MyClass
{
public decimal Id { get; set; }
public decimal OtherNumber { get; set; }
public override string ToString()
{
return string.Format("{0:000}/{1:0000}/{2:000}", this.Id, this.OtherNumber, 999.0m - this.Id );
}
static void Main(string[] args)
{
Console.WriteLine(new MyClass { Id = 123.0m, OtherNumber = 1234.0m }); // 123/1234/876
Console.WriteLine(new MyClass { Id = 12.3m, OtherNumber = 123.4m }); // 012/1234/987
}
}
For more information about formatting strings, see https://msdn.microsoft.com/en-us/library/system.string.format(v=vs.110).aspx

When searching for a row, counts come back as 0

I am currently doing the following to try and get back a particular row in a data table based on the date time. The issue I am running into is when I run the code I continuously get 0 back for my number of rows and I am not sure as to why. Here is some code to that maybe someone can pick apart.
var myDay = new AnalysisDay(uniqueDate.Date, null, null, null, null);
var daysList = new List<AnalysisDay>();
foreach(var uniqueDate in uniqueDateList)
{
var myDay = new AnalysisDay(uniqueDate.Date, null, null, null, null);
var curDate = uniqueDate;
//This is the area where the rows come back 0 everytime "Start Date Time"
//occasionally has DBNull values
var curDatesRows = analysisInfo.Tables[0].Select("StartDateTime = #{0}#", curDate.ToShortDateString()));
}
This is the custom class that is referenced throughout,
public class AnalysisDay
{
public DateTime? Date { get; set; }
public DateTime? Time { get; set; }
public string Name { get; set; }
public int? CarNumber { get; set; }
public int? RunId { get; set; }
public ObservableCollection<AnalysisDay> Items { get; set; }
public AnalysisDay(DateTime? date, DateTime? time, string strName, int? number, int? runId)
{
Date = date;
Time = time;
Name = strName;
CarNumber = carNum;
RunId = runId;
Items = new ObservableCollection<AnalysisDay>();
}
}
If I have left anything out or need to provide more information please let me know and I will update this as soon as possible.
Something about the comparison must be failing. I usually use LINQ to query instead:
var curDatesRows
= dt.Rows.Cast<DataRow>()
.Where(x => x.Field<DateTime?>("StartDateTime").HasValue
&& x.Field<DateTime>("StartDateTime").Date == curDate.Date);
I tested this out on some sample data - it will filter out rows with a null value.
Note that if curDate has a time portion, and it doesn't match exactly what's in the table, then you won't get any rows unless you use .Date to drop the time portion when doing the comparison.
It looks like you are passing a short date string as the filter against a column containing date time values (which include time components). I believe that either no date in the startDateTime column will match the filter, or only values where the time component is all zeros (which is unlikely, unless you are modifying the times before persisting.
Without additional information, it's hard to tell. Can you show us some sample data from the table? Specifically, the startDateTime column?

Categories

Resources