How can I do a two level sort with LINQ in C#
I have a List<string>
object that looks like this:
option 1
line a
line b
line c
option 3
line x
line z
line y
option 2
a1
a4
a2
a3
All the secondary details are indented one space. Is it possible to sort this list using LINQ? I only know about the very basic ordering and don't really know where to start when it comes to trying to sort the secondary lines that have strings with a space in t开发者_开发百科he first column.
Here's what I want the List to look like after sorting
option 1
line a
line b
line c
option 2
a1
a2
a3
a4
option 3
line x
line y
line z
I just updated the question. I had List<string>
but it got chopped when published.
Use:
yourEnumerable.OrderBy(y => y.Key).ThenBy(y => y.Prop2)
See the documentation at MSDN: http://msdn.microsoft.com/en-us/library/bb534966.aspx and http://msdn.microsoft.com/en-us/library/bb534743.aspx
UPDATE: Try the code below to parse and sort, it's somewhat pseudo:
string lineRead;
string current;
var dictionary = new Dictionary<string, List<string>>);
while((lineRead = reader.ReadLine())
{
if(!lineRead.StartsWith(" "))
{
dictionary.Add(lineRead, new List<string>());
current = lineRead;
}
else
{
dictionary[current].Add(lineRead);
}
}
dictionary.Keys.ToList().ForEach(y => dictionary[y] = dictionary[y].OrderBy(x => x).ToList());
dictionary.OrderBy(y => y.Key);
While there's no built-in way to do this, LINQ provides a simple, elegant answer:
static IEnumerable<string> Sort(IEnumerable<string> unsorted)
{
string option = null;
return
from line in unsorted
let isSubitem = line.StartsWith(" ")
let parent = isSubitem ? option : option = line
orderby parent, isSubitem, line
select line;
}
The key is being able associate a sub-item (a line starting with a space) with its parent option (a line with no space at the beginning). We do this by first declaring a local variable option
which will always hold the current parent option.
Within the LINQ expression, the first thing to do is tell it to iterate over the items in the unsorted
input, giving each string the name line
. Then we determine whether it's a sub-item, and put that result in isSubItem
.
Now comes the key -- putting the parent option into parent
. If the line is a sub-item, it just uses the stored option
. If it's not, the option = line
part stores the current line as option
and assigns it to parent
.
Then it's just a simple matter of sorting, first by the parent option, then by whether a line is a parent (to make sure that the parent always shows up before its children), and then by the sub-items.
If you want to use the shorter lambda-based syntax and don't want the readibility of extra names, you can do it quite simply in just a coule lines:
static IEnumerable<string> Sort(IEnumerable<string> unsorted)
{
string option = null;
return
unsorted
.OrderBy(line => line.StartsWith(" ") ? option : option = line)
.ThenBy(line => line.StartsWith(" ") ? line : null);
}
Something like:
.OrderBy(x => x.option)
.ThenBy(x=>x.secondary)
list.OrderBy(<a delegate that will sort your array>).ThenBy(<a delegate that will sort your array again>)
is that what you wnat?
well I answered it based on your question and this is how the delegate will be, your first delegate will basically group the items under each of the options and then sort it and the next delegate will sort each of the items that fall under the option tag..
An alternate approach would be to store this in a dictionary: as follows: Key Value(List) option1 a1, a2 etc option2 as1,as2 etc etc.
then you will sort the value and the key and then if you want put it into a list if needed only
精彩评论