使用LINQ排序后,获取集合中项目的新索引

时间:2016-02-24 13:13:07

标签: c# arrays linq list sorting

我想在C#中对列表(或数组)进行排序,并希望为未排序列表中的每个项目保存新索引。即:

A = 2 3 1

sorted(A) = 1 2 3

indexes = 1 2 0 <-- This is what I need

我在Stack溢出上读到LINQ特别有用,但我找不到具体怎么做。

5 个答案:

答案 0 :(得分:21)

注意:

我犯了一个误读OP问题的可怕错误(并且因为收到了这么多的赞成而感到尴尬)。我希望(理想情况下)OP接受其中一个答案是正确的。为了公正地对待这些人,我会修改这个答案以使其正确。

可以携带两个扩展方法,一个用于旧索引,一个用于新索引(这是OP想要的)。

cell

称之为,

public static IEnumerable<int> OldIndicesIfSorted<T>(this IEnumerable<T> source) where T : IComparable<T>
{
    return source
        .Select((item, index) => new { item, index })
        .OrderBy(a => a.item)
        .Select(a => a.index);
}

public static IEnumerable<int> NewIndicesIfSorted<T>(this IEnumerable<T> source) where T : IComparable<T>
{
    return source
        .OldIndicesIfSorted()
        .Select((oldIndex, index) => new { oldIndex, index })
        .OrderBy(a => a.oldIndex)
        .Select(a => a.index);
}

这两种方法都是懒惰的,但理想情况下//A = [2, 3, 1]; A.NewIndicesIfSorted(); // should print [1, 2, 0] 应该根据Matthew Watson的答案来编写,这种答案效率更高。我和Mat的答案的好处是可以很好地处理重复的条目。

答案 1 :(得分:8)

您可以使用LINQ select:

执行此操作
var a =new [] {2,3,1};

var sorted = a.OrderBy (x => x).ToList();
var indexes = a.Select (x => sorted.IndexOf(x));

如果这是一个巨大的列表而不是这个简单的列表,它可能会有点低效,但确实会返回您期望的内容。

答案 2 :(得分:4)

执行此操作的一种方法如下:

int[] a = {2, 3, 1};
int[] b = Enumerable.Range(0, a.Length).ToArray();
int[] c = new int[a.Length];

Array.Sort(a, b);

for (int i = 0; i < b.Length; ++i)
    c[b[i]] = i;

Console.WriteLine(string.Join(", ", c)); // Prints 1, 2, 0

它使用Array.Sort()的重载,它使用来自不同数组的值对一个数组进行排序。

我认为这是相当有效的:它使用O(N log(N))排序和O(N)重新映射。 (其他正确的解是O(N log(N))加O(N ^ 2))

它仅适用于数组(因为List没有等效的Array.Sort())。

使用Linq的替代方案(并且有效地修改了@nawfal的答案):

int[] a = {2, 3, 1};

var b = a
    .Select((item, index) => new { item, index })
    .OrderBy(x => x.item)
    .Select(x => x.index)
    .ToArray();

int[] c = new int[b.Length];

for (int i = 0; i < b.Length; ++i)
    c[b[i]] = i;

Console.WriteLine(string.Join(", ", c)); // Prints 1, 2, 0

这里至关重要的是两种方法中的额外映射步骤:

int[] c = new int[b.Length];

for (int i = 0; i < b.Length; ++i)
    c[b[i]] = i;

这实际上是反向查找映射。

答案 3 :(得分:1)

另一个有效的解决方案:

var input = new List<int> { 2, 3, 1 };
var result = input.Select(x => input.OrderBy(n => n).ToList().IndexOf(x));

Console.WriteLine(String.Join(" ", result));  // 1 2 0

答案 4 :(得分:0)

在阅读其他答案时,我对您想要的indexes订单感到有些困惑,但是如果您希望它们按照您在问题中发布的顺序排列1 2 0,则可以使用此解决方案。试试这个:

var A = new int[] { 2, 3, 1 };
var indexes = new int [A.Length];

var sorted = A.OrderBy(n => n)
              .Select((n, i) => 
                      { 
                          indexes[Array.IndexOf(A, n)] = i;

                          return n; 
                      })
              .ToArray();