2010-07-28 153 views
6

我有一个包含多个列表的列表<>。它基本上是一张表,每列与列一起存储为列表<>。每列不包含相同的类型。每个列表也是相同的长度(具有相同数量的元素)。基于另一个列表的C#排序列表

例如:

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

//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"}; 

我希望能够进行排序列表中的一个从最旧到最新: 一个= {2006年4月9日,2008年4月13日,2010/4/12};

所以要做到这一点,我把元素0移到了最后。

然后我想用同样的方法对列表中的两个和三个进行排序;将第一个移动到最后一个。

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

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

回答

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; 
    } 
); 
+0

所以我遍历每个列表的整体,并从数据创建一个数据结构。然后我对数据进行排序,然后将其放回列表中。这是有道理的,但似乎并不高效。它至少会工作,这比我目前的工作要好。谢谢。编辑:我不知道有3个项目,所以我不能使用一个结构。列表可能工作,但我不得不担心在大型铸造? – 2010-07-28 18:12:33

+0

@John - 你是否收到来自不可控制源的这些列表? – ChaosPandion 2010-07-28 18:13:56

+0

是的,我收到的List <>对象不能改变;他们可以在适当位置排序。 – 2010-07-28 18:15:07

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

+1。这是一个很好的方法。我的个人图书馆里确实有这种“间接清单”;它对很多事情都很有用。示例代码[这里](http://nitolinq.codeplex.com/SourceControl/changeset/view/57003#1161747)。 – 2010-07-28 18:17:54

1

使用泛型数组可能会有点麻烦。

一种替代方法是使用Array.Sort()方法,该方法使用一组键和一组值来排序。它首先按照升序对关键数组进行排序,并确保重新组织数组以匹配此排序顺序。

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

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

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

3

我很遗憾地说,但这感觉像一个糟糕的设计。特别是由于清单<牛逼>不保证元素顺序在被称为排序操作的一个前(所以你必须在插入时有问题):

From MSDN

这份名单是不能保证是 排序。在执行需要对列表 进行排序的操作(例如 BinarySearch)之前,必须对列表 进行排序。

在很多情况下,你不会因此而陷入困境,但是你可能会这样做,如果你这样做,它可能是一个非常难以追查的错误。例如,我认为列表<T>的当前框架实现维护插入顺序,直到调用排序,但它将来可能会更改。

我会认真考虑重构使用另一个数据结构。如果你仍然想基于这个数据结构来实现排序,我会创建一个临时对象(可能使用匿名类型),对其进行排序,然后重新创建列表(see this excellent answer以获得解释)。

+0

我同意有更好的设计。过去遇到过的主要情况是我没有原始数组的所有权,而且他们太大而无法制作单独的副本。如果您拥有所有权,则通过所有手段将其重新分解为具有所有数据的IComparable类或结构。 – TechNeilogy 2010-07-28 18:19:20

+0

我不能改变这一点,但这里是应用程序。基本上List <>对象包含数据,并且数据传递给数学函数。数学函数然后将结果传回并附加到表类。以行方式存储这些东西并使用LINQ投影列会更好吗?然而,这使插入更复杂。 – 2010-07-28 18:36:46

+0

是的,我当然认为最好是按行存储。我不明白插入是如何更复杂的;但是再一次,我不知道整个应用程序和你的限制。 – driis 2010-07-28 22:02:01

0

我希望这能帮助:

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

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

或者,如果复制的数据是不是一个巨大的关注,你能预料到它使用zip操作LINQ查询(需要.NET 4.0或Rx),为了它,然后拉每个结果出来:

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); 

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

2

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

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

第二组语句生成通过使用重新排序索引(换句话说拾取项新的列表,2项1和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(); 
+0

我喜欢这个解决方案,但效率高吗?我不太了解导演的表现; – 2010-07-28 18:34:26

+0

只有一种方法可以找出 - :-) – 2010-07-29 02:42:33