I need to convert a var in the type List<myStruct>.
I had to use var to order a list of myStruct and I got orderedList2, but now I need to iterate this list and I don't know how to do it.
public struct myStruct
{
public String delivery;
public String articleCode;
public String dex;
public String phase;
public String quantity;
};
List<myStruct> myList;
var orderedList2 = myList.OrderByDescending(x =>
{
DateTime dt;
DateTime.TryParse(x.delivery, out dt);
return x;
});
// now I have to fill the ListView again
foreach(myStruct str in orderedList2)
{
string[] s = new string[5];
s[0] = str.delivery;
s[1] = str.articleCode;
s[2] = str.dex;
s[3] = str.phase;
s[4] = str.quantity;
ListViewItem itl = new ListViewItem(s);
////String s = r["DEXART"].ToString();
////MessageBox.Show(s);
listView1.Items.Add(itl);
}
When the code hits the foreach statement I get an exception.
Either implement IComparable on your struct or you have a mistake in your order lambda:
var orderedList2 = myList.OrderByDescending(x =>
{
DateTime dt;
if( DateTime.TryParse(x.delivery, out dt))
return dt;
return DateTime.MinValue;
});
you can follow it with .ToList() cal to get List<myStruct> orderedList2.
it works:
I'd suggest to make your struct a class (delcare it outside your current class) and implement IComparable like so:
public class myClass : IComparable
{
public String delivery;
public String articleCode;
public String dex;
public String phase;
public String quantity;
// Order by delivery datetime
public int CompareTo(object obj)
{
if (obj.GetType() != typeof(myClass ) || (obj.delivery == delivery))
return 0;
DateTime dtDelivery = DateTime.ParseExact(delivery, "dd-MM-yyyy");
DateTime dtObjDelivery = DateTime.ParseExact((obj as myClass).delivery, "dd-MM-yyyy");
return dt.Delivery.CompareTo(dtObjDelivery);
}
}
You need to instansiate the list so:
List<myStruct> myList;
Should be
List<myStruct> myList = new List<myStruct>;
Without this I belive that myList would be null. Hence the null reference exception.
In your code:
var orderedList2 = myList.OrderByDescending(x =>
{
DateTime dt;
DateTime.TryParse(x.delivery, out dt);
return x;
});
you are returning "x", which is a myStruct. And you are ignoring the dt that you just parsed (without checking whether that succeeded, also).
So for a quick fix of this part, just return dt; to order by "delivery" as date (assuming it always does contain a parsable date).
Related
I have a Data class that looks like the following.
public class Data
{
public string Dt1 { get; set; }
public string Dt2 { get; set; }
public string Dt3 { get; set; }
public string Dt4 { get; set; }
public string Dt5 { get; set; }
}
And a list of class objects of it, with following sample data.
var list = new List<Data>
{
new Data() { Dt1 = "DtA", Dt2 = string.Empty, Dt3 = "-", Dt5 = "DtC" },
new Data() { Dt1 = "DtB", Dt2 = string.Empty, Dt3 = string.Empty, Dt5 = "-" },
new Data() { Dt1 = "DtC", Dt2 = "-", Dt5 = "-" },
new Data() { Dt1 = "DtD", Dt2 = string.Empty, Dt3 = "DtX", Dt5 = string.Empty },
new Data() { Dt1 = "DtE", Dt3 = "-" }
};
I have a list of 'invalid' strings, which are the following.
var invalid = new List<string>() { string.Empty, "-", null };
Now, I'd like to identify property names from the above list, which contain at least one valid string, and then create a List<string> with those property names. If we consider above sample data, you can see that,
All Dt1 values are valid.
All Dt2 values are invalid.
Dt3 has at least one valid value.
Dt4 is never assigned, therefore, all values are invalid.
Dt5 has at least one valid value.
So, my resulting list should be
Dt1, Dt3, Dt5
My approach for this is to write a function that identify if there's at least one valid value in a string, and then check each property of the list using that.
public static bool IsDataValid(List<string> data, List<string> invalid)
{
foreach (var item in data)
{
if (!invalid.Contains(item))
{
return true;
}
}
return false;
}
Then,
var invalid = new List<string>() { string.Empty, "-", null };
var result = new List<string>();
if (IsDataValid(list.Select(x => x.Dt1).ToList(), invalid))
{
result.Add("Dt1");
}
if (IsDataValid(list.Select(x => x.Dt2).ToList(), invalid))
{
result.Add("Dt2");
}
if (IsDataValid(list.Select(x => x.Dt3).ToList(), invalid))
{
result.Add("Dt3");
}
if (IsDataValid(list.Select(x => x.Dt4).ToList(), invalid))
{
result.Add("Dt4");
}
if (IsDataValid(list.Select(x => x.Dt5).ToList(), invalid))
{
result.Add("Dt5");
}
This works, sure, but it's a bit 'ugly' for my taste. Also, my actual Data class has 20+ properties, so I'd have to use 20 if statements, which again smells of bad design.
I was wondering if there's another way, particularly one where I don't have to 'hardcode' if statements. Something like the following where I can iterate the properties of the class and figure out which of them from the list are valid.
foreach (var prop in typeof(Data).GetProperties())
{
// How do I do a `.Select()` here?
}
You can use Type.GetProperties() to get all properties of your type, then use PropertyInfo.Name, PropertyInfo.PropertyType and PropertyInfo.GetValue() to get the names and values of all string properties of each Data item in your collection. With that you can collect the names of all properties with at least one valid value as follows:
public static class ValidationExtensions
{
public static ICollection<string> ValidProperties<TObject, TValue>(this IEnumerable<TObject> items, Predicate<TValue> isValid)
{
var properties = typeof(TObject).GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(p => typeof(TValue).IsAssignableFrom(p.PropertyType))
.Where(p => p.CanRead && p.GetIndexParameters().Length == 0 && p.GetGetMethod() != null)
.ToList();
var set = new HashSet<string>();
foreach (var i in items) // Only iterate through the items once.
foreach (var p in properties)
{
if (set.Contains(p.Name)) // Do not call GetValue() if not necessary, it's expensive.
continue;
if (isValid((TValue)p.GetValue(i)))
set.Add(p.Name);
}
// Return properties in order
return properties.Select(p => p.Name).Where(n => set.Contains(n)).ToList();
}
}
Notes:
If performance is an issue, you might want to build a table of delegates for accessing the property values of a given type using Delegate.CreateDelegate, e.g. as shown in this answer to How do I create a delegate for a .NET property?.
Demo fiddle here.
I have a List method where I need to return my enum values as a list type. The Status datatype is an enum object.
Here is my code:
public List<string> Statusstring
{
get
{
foreach (Status stat in System.Enum.GetValues(typeof(Status)).Cast<Status>().ToList())
{
status = stat;
}
return new List<string> { status.ToString() };
}
}
Here is my enum value:
public enum Status
{
Enable,
Disable
}
This is the error message I am getting:
Cannot implicitly convert type 'System.Collections.Generic.List<char>' to 'System.Collections.Generic.List<string>'
Can someone please help me on this.
There is a built-in construct for this in .NET:
public IEnumerable<string> Statuses => Enum.GetNames(typeof(Status))
If you return IEnumerable<string> instead of a list you can assign this to arrays or lists. And the Enum.GetNames(...) returns as a string[].
The documentation for this method is here
If you have to return List only - Then below is a #hack
if (reader["PRODUCTION_LINE_CODE"].ToString() == "1")
{
return new List<string> { Status.Enable.ToString() };
}
else
{
return new List<string> {Status.Disable.ToString() };
}
Update:- After discussion with OP
if (reader["PRODUCTION_LINE_CODE"].ToString() == "1")
{
return new List<string> { Status.Enable.ToString(), Status.Disable.ToString() };
}
else
{
return new List<string> {Status.Disable.ToString() };
}
What I see are a pretty much very convoluted solutions to a simple problem.
private static List<string> StatusString() => Enum.GetNames(typeof(Status)).ToList();
But since Enum values are not going to change during run time I would go for static member
You can define a static member→
private static List<string> StatusString = Enum.GetNames(typeof(Status)).ToList();
The property is named as Statustring but it is a List<string>?
I guess the codes below might be the proper implementation:
public string Statusstring
{
get
{
string query;
query = "Select PRODUCTION_LINE_CODE from productionlineconfig where ID = '" + ProductionLineConfigs.ProductionLineId + "'";
reader = db.QueryCommand(query);
while (reader.Read())
{
if (reader["PRODUCTION_LINE_CODE"].ToString() == "1")
{
return Status.Enable.ToString();
}
else
{
return Status.Disable.ToString();
}
}
}
Or, if you have to return a List<string> you can try this
public List<string> Statusstring
{
get
{
string query;
query = "Select PRODUCTION_LINE_CODE from productionlineconfig where ID = '" + ProductionLineConfigs.ProductionLineId + "'";
reader = db.QueryCommand(query);
while (reader.Read())
{
if (reader["PRODUCTION_LINE_CODE"].ToString() == "1")
{
return new List<string>().Add(Status.Enable.ToString());
}
else
{
return new List<string>().Add(Status.Disable.ToString());
}
}
}
This should do.
using System;
using System.Linq;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
List<string> list = System.Enum.GetValues(typeof(Status))
.Cast<Status>()
.Select(x => x.ToString())
.ToList();
list.ForEach(Console.WriteLine);
}
}
public enum Status
{
Enable,
Disable
}
First enum to list
var enumList = Enum.GetValues(typeof(Status))
.Cast<int>()
.Select(x => x.ToString())
.ToList();
then cast enumList to list of string
var stringList = enumList.OfType<string>();
return stringList;
How about this:
Enum.GetValues(typeof(Status)).Cast<Status>()
will return an Ienumerable.
Enum.GetValues(typeof(Status)).Cast<Status>().ToList();
let's say i have list with properties like this:
Type ArrayType
String Value
i've got another list like this:
ArrayType=System.Int32
Value=44
Index=0
ArrayType=System.Int32
Value=11
Index=3
ArrayType=System.Int32
Value=7
Index=2
ArrayType=System.Int32
Value=5
Index=1
All values are in string !
I don't know what type will be it, int32 is only example. there will also be floats, strings, etc.
so... i'm creating new array of type which i readed from "ArrayType" property
dynamic newArray = Array.CreateInstance(typeOfArray, arraySize);
I know arraySize of coure
and the Arrays are created properly.
But now i want to copy all values (which are in string) to my new array:
but i have no idea how to cast it. i've tried something like this:
newArray.SetValue(element.Property.Value,element.Index);
it throw an exception that he can't write OBJECT to my array of ints
so then i've tried to cast in somehow:
newArray[element.Index] = Convert.ChangeType(element.Property.Value,element.Property.Type);
but it still can't cast the object
can someone help :) ?
I just made this code example:
var types = new[] { typeof(int), typeof(float), typeof(double) };
var elements = Enumerable.Range(1, 100)
.Select((value, index) => new Element(types[index % types.Length], value.ToString(), index));
var integers = elements.Where(element => element.ArrayType == typeof(int));
var array = Array.CreateInstance(typeof(int), 100);
Console.WriteLine(array.GetType());
foreach(var element in integers)
{
var value = Convert.ChangeType(element.Value, element.ArrayType);
array.SetValue(value, element.Index);
}
foreach(var value in array)
{
Console.WriteLine(value + " " + value.GetType());
}
Element class:
public class Element
{
public Type ArrayType { get; private set; }
public string Value { get; private set; }
public int Index { get; private set; }
public Element(Type type, string value, int index)
{
ArrayType = type;
Value = value;
Index = index;
}
}
Which just works
Ok, now all works :D thanks for help!
foreach (var element in groupedElement)
{
var newInstance = Activator.CreateInstance(element.Property.Type);
newInstance = element.Property.Value;
newArray.SetValue(Convert.ChangeType(newInstance, element.Property.Type, CultureInfo.InvariantCulture), element.Index);
}
I have a class which contains answers for questions
The answers are stored in a string
I have created a method like so:
public void set_answer(object obj){
.....
}
which will take as an arguement a variable, and according to the question type will change it into a string representation, so for example if the answer is a string it will keep it as it is, if it is a DateTime it will change it to a yyyy-MM-dd format plus about 8 other transformations
I wish to create the opposite method, which will return the answer in the original type
I have the following code:
public T? get_answer<T>() {
//this means that the answer has not been set yet
if (string.IsNullOrWhiteSpace(_answer)) return null;
object reply = null;
switch (_answer_type) {
...do stuff here to transform the string to the correct type, which will match T
}
<here is the problem>
}
At the end of this code, the reply variable will contain either null or a variable of type T
how can I return that value?
in my code I will be using it like this :
DateTime? date = question.get_answer<DateTime>();
which should return null if no answer has been set yet or the request was for an incorrect type (i.e. asked for a datetime answer on a Tupple question)
Thanks in advance for any help you can provide
If you have a MyMethod<T> and for the most part it consists of code like if (T is Type1) {...} else if (T is Type2) {...} else if [etc], then very often you shouldn't be using a generic method in the first place.
In such a case I would recommend to use this:
public class MyClass
{
public string _answer;
public DateTime? GetDate()
{
DateTime result;
if (DateTime.TryParse(_answer, out result)) return result;
return null;
}
public int? GetInt()
{
int result;
if (int.TryParse(_answer, out result)) return result;
return null;
}
// etc
}
and to test it:
public void TestIt()
{
var x = new MyClass { _answer = "gaga" };
var d = x.GetDate();
var i = x.GetInt();
// etc
}
Or if you must use a single obj...
public void TestIt()
{
var x = new MyClass { _answer = "gaga" };
object obj = x.GetDate();
if (obj == null) obj = x.GetInt();
// etc
}
Or in one statement:
public void TestIt()
{
var x = new MyClass { _answer = "gaga" };
var obj = (object) x.GetDate() ?? x.GetInt() /* ?? [etc] */ ;
}
Each item/string in my array starts with two letters followed by two or three numbers and then sometimes followed by another letter.
Examples, RS01 RS10 RS32A RS102 RS80 RS05A RS105A RS105B
I tried to sort this using the default Array.Sort but it came back with this...
RS01
RS05A
RS10
RS102
RS105A
RS105B
RS32A
RS80
But I need it like this..
RS01
RS05A
RS10
RS32A
RS80
RS102
RS105A
RS105B
Any Ideas?
Here is sorting with custom comparison delegate and regular expressions:
string[] array = { "RS01", "RS10", "RS32A", "RS102",
"RS80", "RS05A", "RS105A", "RS105B" };
Array.Sort(array, (s1, s2) =>
{
Regex regex = new Regex(#"([a-zA-Z]+)(\d+)([a-zA-Z]*)");
var match1 = regex.Match(s1);
var match2 = regex.Match(s2);
// prefix
int result = match1.Groups[1].Value.CompareTo(match2.Groups[1].Value);
if (result != 0)
return result;
// number
result = Int32.Parse(match1.Groups[2].Value)
.CompareTo(Int32.Parse(match2.Groups[2].Value));
if (result != 0)
return result;
// suffix
return match1.Groups[3].Value.CompareTo(match2.Groups[3].Value);
});
UPDATE (little refactoring, and moving all stuff to separate comparer class). Usage:
Array.Sort(array, new RSComparer());
Comparer itself:
public class RSComparer : IComparer<string>
{
private Dictionary<string, RS> entries = new Dictionary<string, RS>();
public int Compare(string x, string y)
{
if (!entries.ContainsKey(x))
entries.Add(x, new RS(x));
if (!entries.ContainsKey(y))
entries.Add(y, new RS(y));
return entries[x].CompareTo(entries[y]);
}
private class RS : IComparable
{
public RS(string value)
{
Regex regex = new Regex(#"([A-Z]+)(\d+)([A-Z]*)");
var match = regex.Match(value);
Prefix = match.Groups[1].Value;
Number = Int32.Parse(match.Groups[2].Value);
Suffix = match.Groups[3].Value;
}
public string Prefix { get; private set; }
public int Number { get; private set; }
public string Suffix { get; private set; }
public int CompareTo(object obj)
{
RS rs = (RS)obj;
int result = Prefix.CompareTo(rs.Prefix);
if (result != 0)
return result;
result = Number.CompareTo(rs.Number);
if (result != null)
return result;
return Suffix.CompareTo(rs.Suffix);
}
}
}
You can use this linq query:
var strings = new[] {
"RS01","RS05A","RS10","RS102","RS105A","RS105B","RS32A","RS80"
};
strings = strings.Select(str => new
{
str,
num = int.Parse(String.Concat(str.Skip(2).TakeWhile(Char.IsDigit))),
version = String.Concat(str.Skip(2).SkipWhile(Char.IsDigit))
})
.OrderBy(x => x.num).ThenBy(x => x.version)
.Select(x => x.str)
.ToArray();
DEMO
Result:
RS01
RS05A
RS10
RS32A
RS80
RS102
RS105A
RS105B
You'll want to write a custom comparer class implementing IComparer<string>; it's pretty straightforward to break your strings into components. When you call Array.Sort, give it an instance of your comparer and you'll get the results you want.