C#基于另一个列表的排序列表

时间:2010-07-28 18:01:55

标签: c# sorting

我的课程有多个List<>包含在其中。它基本上是一个表,每列存储为List<>。每列不包含相同的类型。每个列表的长度也相同(具有相同数量的元素)。

例如:

我有3个List<>对象;一个List,两个List和三个List。

//Not syntactically correct
List<DateTime> one = new List...{4/12/2010, 4/9/2006, 4/13/2008};
List<double> two = new List...{24.5, 56.2, 47.4};
List<string> three = new List...{"B", "K", "Z"};

我希望能够将列表一从最旧到最新排序: one = {4/9 / 2006,4 / 13 / 2008,4 / 12/2010};

为此,我将元素0移到了最后。

然后我想以同样的方式对列表二和三进行排序;将第一个移动到最后一个。

因此,当我对一个列表进行排序时,我希望其他列表中相应索引中的数据也根据一个列表的排序方式进行更改。

我猜我必须以某种方式超载IComparer,但我觉得有一条我没有意识到的捷径。

7 个答案:

答案 0 :(得分:5)

我过去通过保留或创建单独的索引列表来处理此设计。首先对索引列表进行排序,然后使用它对其他列表进行排序(或只是访问)。您可以通过为索引列表创建自定义IComparer来完成此操作。你在IComparer中做的是基于索引比较到键列表。换句话说,您是间接地对索引列表进行排序。类似的东西:

// This is the compare function for the separate *index* list.
int Compare (object x, object y)
{
  KeyList[(int) x].CompareTo(KeyList[(int) y])
}

因此,您将根据键列表中的值对索引列表进行排序。然后,您可以使用该排序的键列表重新排序其他列表。如果不清楚,我会尝试添加一个更完整的例子,当我遇到一个情况发布一个。

答案 1 :(得分:3)

我很遗憾地说,但这感觉就像一个糟糕的设计。特别是因为List&lt; T&gt;在调用其中一个排序操作之前不保证元素顺序(因此插入时遇到问题):

From MSDN

  

不保证列表   排序。您必须对列表进行排序   在执行操作之前(例如   BinarySearch)需要List   待分类。

在许多情况下,你不会因此而遇到麻烦,但你可能会这样做,如果你这样做,它可能是一个非常难以追查的错误。例如,我认为List&lt; T&gt;的当前框架实现。保持插入顺序直到调用sort,但它可能在将来发生变化。

我会认真考虑重构使用其他数据结构。如果您仍希望基于此数据结构实现排序,我将创建一个临时对象(可能使用匿名类型),对此进行排序,并重新创建列表(see this excellent answer以获取解释方法)。< / p>

答案 2 :(得分:3)

这是使用LINQ和投影的方法。第一个查询生成一个数组,其原始索引由datetime值重新排序;在您的示例中,newOrdering数组将具有成员:

{ 4/9/2006, 1 }, { 4/13/2008, 2 }, { 4/12/2010, 0 }

第二组语句通过使用重新排序的索引(换句话说,按顺序,第1项,第2项和第0项)选择项目来生成新列表。

var newOrdering = one
    .Select((dateTime, index) => new { dateTime, index })
    .OrderBy(item => item.dateTime)
    .ToArray();

// now, order each list
one = newOrdering.Select(item => one[item.index]).ToList();
two = newOrdering.Select(item => two[item.index]).ToList();
three = newOrdering.Select(item => three[item.index]).ToList();

答案 3 :(得分:2)

首先,您应该创建一个Data对象来保存所有内容。

private class Data
{
    public DateTime DateTime { get; set; }
    public int Int32 { get; set; }
    public string String { get; set; }
}

然后你可以这样排序。

var l = new List<Data>();
l.Sort(
    (a, b) =>
    {
        var r = a.DateTime.CompareTo(b);
        if (r == 0)
        {
            r = a.Int32.CompareTo(b);
            if (r == 0)
            {
                r = a.String.CompareTo(b);
            }
        }
        return r;
    }
);

答案 4 :(得分:1)

使用通用数组,这可能会有点麻烦。

另一种方法是使用Array.Sort()方法,该方法接受一组键和一组值进行排序。它首先按键升序排序,并确保重组值以匹配此排序顺序。

如果您愿意承担将List<T>转换为数组(然后返回)的费用,您可以利用此方法。

或者,您可以使用LINQ将多个数组中的值组合成一个使用Zip()的匿名类型,使用键字段对匿名类型列表进行排序,然后将其拆分为单独的数组。

如果您想就地执行此操作,则必须编写自定义比较器并创建单独的索引数组以维护项目的新顺序。

答案 5 :(得分:1)

我写了一个排序算法,为Nito.LINQ执行此操作(尚未发布)。它使用一个简单的QuickSort对列表进行排序,并保持任意数量的相关列表同步。 Source code starts here, in the IList<T>.Sort extension method.

或者,如果复制数据不是一个大问题,您可以使用Zip运算符(需要.NET 4.0或Rx)将其投影到LINQ查询中,对其进行排序,然后将每个结果拉出来:

List<DateTime> one = ...;
List<double> two = ...;
List<string> three = ...;
var combined = one.Zip(two, (first, second) => new { first, second })
    .Zip(three, (pair, third) => new { pair.first, pair.second, third });
var ordered = combined.OrderBy(x => x.first);
var orderedOne = ordered.Select(x => x.first);
var orderedTwo = ordered.Select(x => x.second);
var orderedThree = ordered.Select(x => x.third);

当然,最好的解决方案是首先不要分离相关数据。

答案 6 :(得分:0)

我希望这可以提供帮助:

one = one.Sort(delegate(DateTime d1, DateTime d2)
{
    return Convert.ToDateTime(d2).CompareTo(Convert.ToDateTime(d1));
});