I have a listview and I am trying to sort it based on a column. I have the columnclick event etc working, and it sorts, but I have the following problem:
I can't seem to add items to the listview as integers. This is a problem as if I have a column of ints that I had to use ToString() on, the sort puts 10 ahead of 2.
Does anyone know how I can add items as int's so that the sort has the desired functionality. Also, not all columns are int, there are some string columns and I'd like the sort to work on those too.
For reference, I used the following tutorial for the sort code: http://support.microsoft.com/kb/319401
Cheers
You can create a sorter class that implements IComparer and assign it to the ListViewItemSorter property of the ListView.
IComparer has a method Compare. Two ListViewItem instances are passed to that method. You need to read the column value, then parse it to int and return the correct comparison result (int based instead of string based).
You can create your own ListViewItem class that creates the string value for the column but also holds the original int value to avoid the int.Parse call in the comparer.
Untested example:
public class MyItemComparer : IComparer
{
public int Compare(object x, object y)
{
ListViewItem xItem = (ListViewItem)x;
ListViewItem yItem = (ListViewItem)y;
int a = int.Parse(xItem.SubItems[0]);
int b = int.Parse(yItem.SubItems[0]);
return a.CompareTo(b);
}
}
You can detect if the selected column has numbers.
Write this in the compare function
int intX = 0, intY = 0;
if(int.TryParse(listviewX.SubItems[ColumnToSort].Text, out intX)
&& int.TryParse(listviewY.SubItems[ColumnToSort].Text, out intY))
{
return intX.CompareTo(inty);
}
Maybe is problem if some column contains numbers and text.
Related
I have a jagged or a simple 2d rectangular array (doesn't matter which one is sortable in anyway, I know how to convert between them back and forth). The array is in string format, since the first column is text, but the second column contains double values only. How could I sort the array in an ascending order based on their values not the alphabetical order?
The values would be for example these numbers: 10.0368; 27.2023; 15.21; 4.886
public static void Sort<T>(T[][] data, int col)
{
Comparer<T> comparer = Comparer<T>.Default;
Array.Sort<T[]>(data, (x, y) => comparer.Compare(x[col], y[col]));
Array.Reverse(data);
}
I used this extension method until now, but as it seems to me, it only sorts in alphabetical order. => 10.0368; 15.21; 27.2023; 4.886 (1; 1; 2; 4)
How I would like it to work would be obviously this: 4.886; 10.0368; 15.21; 27.2023.
I tried to find an answer on Google, and in some of the books I have about C#, but I only found the answer for arrays that would have only numbers in it.
If you need any more in depth details to solve the problem, I will provide you with the info you need.
I used this extension method until now, but as it seems to me, it only sorts in alphabetical order.
This is true, because you said earlier that your array is in string format. Hence, your
Comparer<T> comparer = Comparer<T>.Default;
gives you a string comparator, which compares based on lexicographic order.
If you know that the second element of the array contains a double, you can rewrite your code as follows:
public static void Sort<T>(T[][] data, int col) {
var comparer = Comparer<double>.Default;
Array.Sort<T[]>(
data
, (x, y) => comparer.Compare(
double.Parse(y[col].ToString())
, double.Parse(x[col].ToString())
)
);
}
x[col].ToString() does nothing because x[col] is already a string, but it lets you keep your code generic, instead of rewriting as non-generic string[][] method.
Note that you do not have to reverse the result if you reverse parameters that you pass to the comparator.
I have an unmanaged API function and below mentioned is it's equivalent c# code...
Myfunction(unit handle, int index, out bool flag,out int value, out string name);
Here the variable index varies from 0 to 59. I am able to fetch the data individually. I mean I can pass value to the variable index from a TextBox and I am getting the corresponding outputs. But how to collect the values in an array fashion. Each time I don't want to give index input I simply want to display all the values in a ListBox... How to achieve this?
Before we start, this is not a multidimensional array. This is a simple linear array with one index.
Create a struct to hold the values for one item:
struct MyItem
{
bool flag;
int value;
string name;
}
Then have a function return an array of these:
MyItem[] GetItems()
{
MyItem[] result = new MyItem[ItemCount];
// Populate result
return result;
}
Alternatively you might well store the data in a generic collection like List<MyItem>. Fundamentally the key is to creat a structure that can contain a single item, and then operate on collections of items.
I need to create a multidimensional guard List three values, X, Y and Z, and I need a List that is because once the value is queried, the array must be removed.
The query would look something like this: List [0] [0] = X, List [0] [a] = Y and List [0] [2] = X, so that I can remove only the index 0 and he already remove all the other three.
If you need to create a multidimensional list, you can always create a list of lists like so:
var multiDimensionalList = new List<List<string>>{
new List<string>{"A","B","C"},
new List<string>{"D","E","F"},
new List<string>{"G","H","I"},
};
Console.WriteLine(multiDimensionalList[2][1]); // Prints H
multiDimensionalList[2].RemoveAt(1);
Console.WriteLine(multiDimensionalList[2][1]); // Prints I
multiDimensionalList[2][1] = "Q";
Console.WriteLine(multiDimensionalList[2][1]); // Prints Q
Be aware though that attempting to replace a value that doesn't exist by way of assignment will throw an exception:
multiDimensionalList[2][5] = "R"; // Throws an ArgumentOutOfRangeException
Your question is very hard to understand, but perhaps what you are looking for can be accomplished with an array of arrays? This is how multidimensional arrays are implemented in some languages anyways.
In your case you might be using a List of List's: List>. And this would satisfy your requirement to remove "all the other three" by removing the first element in the outer List<> object.
I'm sorry but your question is a little bit hard to understand, but I will take a stab at it. Please don't interchange the words Array and List as they are different yet related ideas in C#. I believe that you mean Array with your use of [] brackets. Although you might want to consider using lists as they have a nice way to remove certain elements from a list by using the element. the MSDN has some good information as to how you might proceed.
List(T).Remove method
the list will restructure it self to remove or add elements as desired.
I am not sure I am following your logic as you are using both strings and integers as your second indexer, and referencing X twice, but not Z. Assuming these are typos, I am going to take a guess at what you want.
Have you considered a custom type with X, Y, AND Z properties, and an indexer to give you the behavior you described:
You also don't mention what types your values are, so I am using object, but feel free to substitute your own type (or a generic type)
public class MyType
{
private object[] backingArray = new object[3];
public object this[int index]
{
get { return backingArray[index]; }
set { backingArray[index] = value; }
}
public object X
{
get { return backingArray[0]; }
set { backingArray[0] = value; }
}
public object Y
{
get { return backingArray[1]; }
set { backingArray[1] = value; }
}
public object Z
{
get { return backingArray[2]; }
set { backingArray[2] = value; }
}
}
You could then use it like this:
List<MyType> list = new List<MyType>();
list = PopulateList(); // fill list with values
var x = list[0][0];
var y = list[0][1];
var z = list[0][2];
Of course, this implementation depends on your 2nd dimension always consisting of 3 elements. If it will not be consistent, then one of the other answers abound for your needs.
I'm writing a class to store some kind of a table-structure.
Now each column in this table structure has a name, and an index.
Now each row in this column will be looped through, and the data will 90% of the cases be requested using the name of the column rather then the index of it.
So what's a good data structure to store the columns, so that it can retrieve the index very quickly based upon the name. Right now I'm using a simple string[], but I wonder if there are faster ways to do this.
Parts of the code:
private string[] _columns;
private int _width;
private int getIndex(string columnName)
{
for (int i = 0; i < _width; i++)
{
if (_columns[i] == columnName) return i;
}
return -1;
}
The names of the columns will be constant after they've been set, and they're mostly about 10-16 characters long.
Thanks in advance.
Since you are usually going to access columns by the name, this sounds like a good place to use a Map (Dictionary class in C#) that maps Strings to Columns (String arrays). That would allow O(1) access for the name rather than the current O(n) in the above code.
The disadvantage is that you wouldn't be able to access directly by column index anymore. However, this is simple to solve--just keep your list of column names and use those to index! You can then call
_columnsMap[_columns[index]]
if you ever need to index by number, and it is still O(1) time.
Use a Dictionary<string,int> to store the names of the columns against their ID.
Using your example (which misses how _columns is populated):
private IDictionary<string,int> _columns;
private int _width;
private int getIndex(string columnName)
{
return _columns[columnName];
}
I have a fairly complex scenario and I need to ensure items I have in a list are sorted.
Firstly the items in the list are based on a struct that contains a sub struct.
For example:
public struct topLevelItem
{
public custStruct subLevelItem;
}
public struct custStruct
{
public string DeliveryTime;
}
Now I have a list comprised of topLevelItems for example:
var items = new List<topLevelItem>();
I need a way to sort on the DeliveryTime ASC. What also adds to the complexity is that the DeliveryTime field is a string. Since these structs are part of a reusable API, I can't modify that field to a DateTime, neither can I implement IComparable in the topLevelItem class.
Any ideas how this can be done?
Thank you
Create a new type that implements IComparer and use an instance of it to compare the objects.
public class topLevelItemComparer : IComparer<topLevelItem>
{
public int Compare(topLevelItem a, topLevelItem b)
{
// Compare and return here.
}
}
You can then call Sort() like this:
var items = new List<topLevelItem>();
// Fill the List
items.Sort(new topLevelItemComparer());
It sounds like you need to get canonicalized date sorting even though your date is represented as a string, yes? Well, you can use LINQ's OrderBy operator, but you will have to parse the string into a date to achieve correct results:
items = items.OrderBy(item => DateTime.Parse(item.subLevelItem.DeliveryTime))
.ToList();
Update:
I've added this in for completeness - a real example of how I use ParseExact with Invariant culture:
var returnMessagesSorted = returnMessages.OrderBy((item => DateTime.ParseExact(item.EnvelopeInfo.DeliveryTime, ISDSFunctions.GetSolutionDateTimeFormat(), CultureInfo.InvariantCulture)));
return returnMessagesSorted.ToList();
You can always implement a separate IComparer class, it's not fun, but it works well:
public class TopLevelItemComparer : IComparer<topLevelItem>
{
public int Compare( topLevelItem x, topLevelItem y )
{
return DateTime.Parse(x.subLevelItem.DeliveryTime).CompareTo(
DateTime.Parse(y.subLevelItem.DeliveryTime) );
}
}
items.Sort( new TopLevelItemComparer() );
Be aware that most Sort() methods in the .NET framework accept an IComparer or IComparer<T> which allows you to redefine the comparison semantics for any type. Normally, you just use Comparer<T>.Default - or use an overload that essentially supplies this for you.
Using LINQ:
items = items.OrderBy(item => item.subLevelItem.DeliveryTime).ToList();
If you want to perform an in-place sort then you can use the Sort overload that takes a Comparison<T> argument and pass an anonymous function/lambda:
items.Sort((x, y) => DateTime.Parse(x.subLevelItem.DeliveryTime).CompareTo(
DateTime.Parse(y.subLevelItem.DeliveryTime)));
If you prefer to create a new sorted sequence rather than an in-place sort then LINQ's OrderBy is probably the way to go, as others have already mentioned.
Having had this problem before I once implemented a LambdaComparer that did the compare based on an arbitrary lambda expression. Not exact code but something along these lines:
public class LambdaComparer : IComparer<T>
{
private Func<T,T,int> _func;
public LambdaComparer(Func<T,T,int> function)
{
_func = function;
}
public int Compare(T x, T y)
{
return _func(x,y);
}
}
Big advantage of this is you get a nice reusable chunk of code.
To sort the items list itself:
Comparison<topLevelItem> itemComparison = (x, y) => {
DateTime dx;
DateTime dy;
bool xParsed = DateTime.TryParse(x.subLevelItem.DeliveryTime, out dx);
bool yParsed = DateTime.TryParse(y.subLevelItem.DeliveryTime, out dy);
if (xParsed && yParsed)
return dx.CompareTo(dy);
else if (xParsed)
return -1; // or 1, if you want invalid strings to come first
else if (yParsed)
return 1; // or -1, if you want invalid strings to come first
else
// simple string comparison
return x.subLevelItem.DeliveryTime.CompareTo(y.subLevelItem.DeliveryTime);
};
items.Sort(itemComparison);
This approach has the advantage of:
Sorting the list in place (that is, if you actualy want the list sorted in-place)
Sorting by actual DateTime values, rather than strings, BUT...
Not throwing an exception if a string does not represent a valid DateTime (basically, all the invalid strings will end up on one side of the list)