I have a RichTextBox
that looks similar to this:
TEXT TEXT-1 227.905 174.994 180 1111
TEXT DIFTEXT 227.905 203.244 180 9999
TEXT DIFTEXT2 242.210 181.294 180 2222
TEXT TEXT-2 236.135 198.644 90 ABC1111
TEXT SOMETEXT 250.610 201.594 0 DDDD
TEXT OTHERTEXT 269.665 179.894 180 4444
TEXT OTHERTEXT 269.665 198.144 180 1111
And I would like to sort it in a special order. Let's say I want to sort it by the last value (column 6) in this sequence:
1111, 2222, 4444, 9999, DDDD
AND then sort it secondly by the 2nd column alphabetically.
So the updated file would look like this NOTE: Since there is no sorting for "ABC1111" it matches the 1111 instead :
TEXT OTHERTEXT 269.665 179.894 180 1111
TEXT TEXT-1 227.905 174.994 180 1111
TEXT TEXT-2 236.135 198.644 90 ABC1111 #See note above
TEXT DIFTEXT2 242.210 181.294 180 2222
TEXT OTHERTEXT 269.665 198.144 180 4444
TEXT DIFTEXT 227.905 203.244 180 9999
TEXT SOMETEXT 250.610 201.594 0 DDDD
And once it is in this format I would like to write it back to the RichTextBox
.
The TextBox class will already give you an array of lines without any work on your part. Array.Sort will allow you to pass in your own code that compares the lines. I can't think of an elegant way to implement awkward sorting rules but the way I'd solve your problem would look something like the code below. Note that you do need to pull out the lines array into a temp variable so you can set it back, otherwise the setter isn't called and the textbox isn't updated.
private static void SortRichTextBox(RichTextBox richTextBox)
{
var lineArray = richTextBox.Lines;
Array.Sort(lineArray, delegate(string a, string b)
{
// I omitted Important corner cases like malformed lines,
// empty strings, and nulls.
// Additional sort cases can be added to the array as needed.
var sortOrder = new[] { "1111", "2222", "4444", "9999", "DDDD" };
var aTokens = a.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
var bTokens = b.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
var aSignifier = Array.FindIndex(sortOrder, item => aTokens[5].Contains(item));
var bSignifier = Array.FindIndex(sortOrder, item => bTokens[5].Contains(item));
// This is where your first rule is being implemented, based on the data
// from the last column. The math is unimportant, what matters is the
// return being positive vs. negative vs. zero.
if (aSignifier != bSignifier)
return aSignifier - bSignifier;
// This is where your second rule is being implemented, as a backup when
// the result from the first rule indicates equality. We're just falling
// through to ordinary string comparison on the second column.
return aTokens[1].CompareTo(bTokens[1]);
});
// We want to call the setter to get the textbox to update.
richTextBox.Lines = lineArray;
}
I tested this, and it looks like it works, at least with your one sample data set.
static void Main(string[] args)
{
string input =
"TEXT TEXT-1 227.905 174.994 180 1111\n" +
"TEXT DIFTEXT 227.905 203.244 180 9999\n" +
"TEXT DIFTEXT2 242.210 181.294 180 2222\n" +
"TEXT TEXT-2 236.135 198.644 90 ABC1111\n" +
"TEXT SOMETEXT 250.610 201.594 0 DDDD\n" +
"TEXT OTHERTEXT 269.665 179.894 180 4444\n" +
"TEXT OTHERTEXT 269.665 198.144 180 1111";
string[] lines = input.Split('\n');
string[][] grid = new string[lines.Length][];
for (int i = 0; i < lines.Length; i++)
{
grid[i] = lines[i].Split((char[])null, StringSplitOptions.RemoveEmptyEntries); // split on whitespace
}
IEnumerable<string[]> results = grid
.OrderBy(gridRow => TransformColumn6(gridRow[5]))
.ThenBy(gridRow => gridRow[1]);
foreach (string[] gridRow in results)
{
Debug.WriteLine(string.Join(" ", gridRow));
}
}
private static string TransformColumn6(string input)
{
if (char.IsDigit(input[0]))
{
// string is a bunch of numbers, use as-is.
return input;
}
int digitIndex = input.IndexOfAny(new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' });
if (digitIndex == -1)
{
// string has no numbers, use as-is, will be sorted in alphabetical order after all the numbers.
return input;
}
// string has a number, remove the stuff at the beginning.
return input.Substring(digitIndex);
}
Results (column formatting added manually):
TEXT OTHERTEXT 269.665 198.144 180 1111
TEXT TEXT-1 227.905 174.994 180 1111
TEXT TEXT-2 236.135 198.644 90 ABC1111
TEXT DIFTEXT2 242.210 181.294 180 2222
TEXT OTHERTEXT 269.665 179.894 180 4444
TEXT DIFTEXT 227.905 203.244 180 9999
TEXT SOMETEXT 250.610 201.594 0 DDDD
If the rules for sorting the last column got much more complicated, then you'd probably want to implement a custom Comparator, instead of using the default string comparison.
I would copy the data to a DataTable and use it to sort by multiple columns and then copy the data back.
http://msdn.microsoft.com/en-us/library/system.data.dataview.sort.aspx
Bearing in mind the ambiguities highlighted in my comment. You could parse the string into an array of objects ( String.Split
or String.Substring
may help you here, depending on the structure of your data).
Then use Array.Sort
(first by the property representing column 2, then by column 6) to sort your array, as per the example here which sorts an array of a custom type.
Create a .ToString
method for your class to convert each object back into the correct format.
Going off your previous question, if you convert them to objects you can use linq to order them.
http://msdn.microsoft.com/en-us/library/bb383982.aspx
Assuming you can get them into objects, you can use from in order by , select
To get around the abc1111 issue, I would create a separate property that you're sorting on that only contains the 1111.
Also, please remember to accept answers.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.