我想在C#中对列表(或数组)进行排序,并希望为未排序列表中的每个项目保存新索引。即:
A = 2 3 1
sorted(A) = 1 2 3
indexes = 1 2 0 <-- This is what I need
我在Stack溢出上读到LINQ特别有用,但我找不到具体怎么做。
答案 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();