如何对列表进行排序,然后对该列表的子集进行排序。

4

我有一组行,每行都是一组字段。很容易找到第1列并按其内容排序:

_lines = lines.OrderBy(l => l.Fields.Find(f => f.ColumnNumber == 1).Content).ToList();

但是,我如何对此列表的子集进行排序?我想按f.ColumnNumber == 3排序,其中(f.ColumnNumber == 1).Content为6。

因此,我的行集合如下:

Col1, Col2, Col3

1, data, data
5, data, data
6, data, Chuck
6, data, Chuck
6, data, Jeffster
6, data, Jeffster
6, data, Grunke
6, data, Gertrude
8, data, data
9, data, data

我想按col1排序,然后仅对col1等于6的col1进行排序,但是按col3排序。
在c#中有没有办法做到这一点?我似乎找不到魔法公式。
编辑:
我的示例数据有点过于简化。有些行有3列,其他行有更多或更少的列。因此,当我使用.ThenBy扩展时,如果我尝试在col 3上对子排序,但是例如第9行只有一列,那么我会得到一个对象引用未设置为对象实例的异常。
以下是更好的示例数据:
Col1, Col2, Col3, Col4

1, data, data, data
5, data, data
6, data, Chuck
6, data, Chuck
6, data, Jeffster
6, data, Jeffster
6, data, Grunke
6, data, Gertrude
8, data, data
9, data

Code 1 lines have 4 columns.
Code 5 lines have 3.
Code 6 lines have 3 - and I need to sort by col 3 alphabetically.
Code 8 lines have 3 columns.
Code 9 lines have 2.

不能保证该列表已经排序。因此,首先需要按第一列1-9对这些行进行排序,然后只需要按第3列对代码6进行排序。

编辑2:

我的类结构有点复杂,所以我尽可能地简化它,希望足够清楚,但看起来情况并非如此,所以让我与您分享类定义:

public class Field : IField
{
    public FieldSpecification Specification { get; set; }
    public string Content { get; set; }
}

public class FieldSpecification
{
        public int ColumnNumber { get; set; }
        public int Length { get; set; }
        public int StartPosition { get; set; }
        public int EndPosition { get { return StartPosition + Length - 1; } }
        public Justification Justification { get; set; }
        public char PadCharacter { get; set; }
}

那么我有一堆符合ILine接口的代码行

public interface ILine
{
    List<IField> Fields { get; set; }
    int Credits { get; }
    int Debits { get; }
    BigInteger Hash { get; }
}

技术上,我展示的是field.ColumnNumber,但应该是field.Specification.ColumnNumber。

目标是根据规格构建固定宽度的文件,规格可能会发生变化。因此,每行都有一组带有规格的字段,然后数据可以进入内容,规格可以帮助进行验证:格式验证。

我希望有一种使用linq对列表子集进行排序的方法,但我可能需要拆解我的最终行集合,对其进行排序,然后重建集合。我希望避免这种情况。


1
请不要在问题标题中包含有关所使用编程语言的信息,除非没有它就没有意义。标签可以起到这个作用。 - Ondrej Janacek
谢谢Ondrej。我以后一定会这样做的。 - Price Jones
你能否告诉我们你的数据的类定义,帮助我们解决问题? - Enigmativity
那么,Fields是什么类型? - Jodrell
不是问题,但...我知道 BigInteger 会帮助避免冲突,但它永远不会像 int 一样快。你应该有一个快速的哈希函数,但准确的相等判断。 - Jodrell
3个回答

3
您可以使用ThenBy扩展功能。
从您期望的输出来看,似乎只需要简单地使用:
 var output = lines.Select(l => new
    {
        Col1 =  int.Parse(l.Fields.Find(f => f.ColumnNumber == 1).Content),
        Col2 =  l.Fields.Find(f => f.ColumnNumber == 2).Content,
        Col3 =  l.Fields.Find(f => f.ColumnNumber == 3).Content
    }).OrderBy(l => l.Col1).ThenBy(l => l.Col3);

如果有必要,只有当Col16时才需要排序子列表。


这样就足够了。

 var output = lines.Select(l => new
    {
        Col1 =  int.Parse(l.Fields.Find(f => f.ColumnNumber == 1).Content),
        Col2 =  l.Fields.Find(f => f.ColumnNumber == 2).Content,
        Col3 =  l.Fields.Find(f => f.ColumnNumber == 3).Content
    }).OrderBy(l => l.Col1).ThenBy(l => l.Col1 == 6 ? l.Col3 : null);

最后一个注意事项,根据不同类型的字段,可能有更好的方法来解决这个问题。


那样会对所有子集进行排序,而不仅仅是他特别想要排序的那个子集。 - Ocelot20
是的,那个方法可以用,但我不确定 OrderBy(x => null) 能否保持顺序。另一个选项是按 Col1 进行分组,然后在各个分组上使用 SelectMany,如果谓词成立,可以选择对每个分组调用 OrderBy。不过,你的解决方案确实更简洁。 - Ocelot20

1
可以通过ThenBy扩展方法来实现;一般来说,这个概念被称为字典序

0
如果我可以假设你的类定义是这样的:
public class Datum
{
    public int ID { get; set; }
    public string[] Cols { get; set; }
}

然后我可以像这样定义数据:

var data = new []
{
    new Datum() { ID = 1, Cols = new [] { "data", "data", "data", }, },
    new Datum() { ID = 5, Cols = new [] { "data", "data", }, },
    new Datum() { ID = 6, Cols = new [] { "data", "Chuck", }, },
    new Datum() { ID = 6, Cols = new [] { "data", "Chuck", }, },
    new Datum() { ID = 6, Cols = new [] { "data", "Jeffster", }, },
    new Datum() { ID = 6, Cols = new [] { "data", "Jeffster", }, },
    new Datum() { ID = 6, Cols = new [] { "data", "Grunke", }, },
    new Datum() { ID = 6, Cols = new [] { "data", "Gertrude", }, },
    new Datum() { ID = 8, Cols = new [] { "data", "data", }, },
    new Datum() { ID = 9, Cols = new [] { "data", }, },
};

然后我可以像这样排序:

var sorted =
    from d in data
    let key =
        String.Format("{0:0000}", d.ID)
        + (d.ID != 6 ? "" : "-" + d.Cols[1])
    orderby key
    select d;

我得到了这些结果:

Results


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接