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
Related
public class Libro
{
public string Titolo { get; set; }
public string Autore { get; set; }
public string Editore { get; set; }
public int ISBN { get; set; }
public int Pagine { get; set; }
public decimal Prezzo { get; set; }
public int Quantità { get; set; }
not being all of type string, I would not know how to convert the int and decimal values to have as a result a table in which to display a list of books (titles, authors, publishers and price) that I have on a file.txt
public Libro BuildLibro(string input)
{
Libro result = null;
if (!String.IsNullOrEmpty(input))
{
var inputArray = input.Split('*');
if (inputArray.Length >= 6)
{
result = new Libro();
result.Titolo = inputArray[0];
result.Autore = inputArray[1];
result.Editore = inputArray[2];
if (!string.IsNullOrEmpty(inputArray[3]))
{
int.TryParse(inputArray[3], out int num);
result.ISBN= num;
}
if (!string.IsNullOrEmpty(inputArray[4]))
{
int.TryParse(inputArray[4], out int num);
result.Pagine = num;
}
if (!string.IsNullOrEmpty(inputArray[5]))
{
decimal.TryParse(inputArray[5], out decimal num);
result.Prezzo = num/100;
}
if (!string.IsNullOrEmpty(inputArray[6]))
{
int.TryParse(inputArray[6], out int num);
result.Quantità = num;
}
}
}
return result;
}
}
}
Foreword
The question has been edited massively since this answer was posted. I've advised that the edits should be reverted so this answer remains valid for the question as posted prior to the edit on Feb 14th (Revision 3), and a new question posted containing Rev 3's text
Original advice for Revision 2
You've switched tactic when you got to ISBN
ISBN = 4, Pagine = 5, Prezzo = 6, Quantità = 7
Assigning the editore name like this makes sense:
Editore = content[3]
It means "take the fourth element of the content array, which is a string array, and put it in the editors property, which is a string"
I guess you'll have tried the pattern of:
ISBN = content[4]
But this won't have worked out because content is full of strings and ISBN is an int and even if a string purely only full of numerical chars that doesn't mean it's a number. This will have given some error like "there is no implicit conversion..."
I guess then you removed the content and just left a hard coded 4 assigned as the ISBN which will work in that it will compile syntactically, but it is incorrect logically. That will just fix every ISBN at being 4, literally
Instead you should parse the string to an int. it's easy, and we can do it with like:
ISBN = int.Parse(content[4])
Similarly for the decimal there is a decimal.Parse
This may expose other problems like, if one of the values contains some non numerical chars (e.g. ISBNs that have hyphens in) but we can solve that later...
There are many ways to parse information out of a string.
I would sugest using regex. For example with the following regex you could parse three strings separated by a *:
(?'Title'[^*]+)[*](?'Author'[^*]+)[*](?'ISBN'[^*]+)
The Advantage of using regex is, that you check if the string is valid while the string is parsed.
To test a regex I would sugest to use something like https://regex101.com
In your code you can use the C# library:
using System.Text.RegularExpressions;
Regex rx = new Regex(#"(?'Title'[^*]+)[*](?'Author'[^*]+)[*](?'ISBN'[^*]+)",
RegexOptions.Compiled | RegexOptions.IgnoreCase);
// Define a test string.
string text = "Harry Potter*Rowling*12345";
// Find matches.
MatchCollection matches = rx.Matches(text);
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.
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.
I have been agonizing over this problem for a few days now and have no hope left. I'm still in the early stages of learning C#, so excuse me if my explanations or understanding are lacking.
My scenario is that I have a need to access an API and download the data as JSON then deserialize it into a class. At the moment, things work as they should, however every variable is defined as String which means I need to convert and manipulate data that should be int/double on the fly constantly as the API can give "N/A" for these data. The impression I get is relying on everything being string is bad practice.
So how should I implement it? I need to be able to store the data as the correct type while keeping in mind that it could be wrong.
Example of properties with wrong type
public string Title { get; set; }
public string Year { get; set; } // Wanted int. Often has an end year "2010-2014"
public string Metascore { get; set; } // Wanted double. Could be "N/A"
The only way I can imagine solving this is by having two classes: the first one being the original string-only class, then having the second being an almost identical class with the desired properties that uses the data from the original then converts it.
My problem with that is that the class already has a few dozen properties, so duplicating it seems nearly as wasteful as the original problem. Regardless, I would like to know an alternative for future use anyway.
EDIT:
Found a similar question here, though unfortunately it didn't help.
you can deserialize the json to JObject and than load it your self
public class RootObject
{
public RootObject(JObject obj)
{
Title = obj["Title"].ToString();
var year = obj["year"].ToString();
Year = year == "N/A" ? 0 : int.Parse(year);
var metascore = obj["Metascore"].ToString();
Metascore = metascore == "N/A" ? 0 : int.Parse(metascore);
}
public string Title { get; set; }
public int Year { get; set; }
public double Metascore { get; set; }
}
static void Main(string[] args)
{
string json = "{\"Title\":\"test\",\"year\":\"2012\",\"Metascore\":\"N/A\"}";
RootObject root = new RootObject(JObject.Parse(json));
}
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];