Reputation: 137682
I'd like to sort a list, and save a map from the old positions of the elements to their new positions?
For example, if the list to sort were
var words = "once upon a midnight dreary".Split();
// once upon a midnight dreary
Then the sorted list would be
var orderedWords = words.OrderBy(w => w).ToArray();
// a dreary midnight once upon
Then because 'once' moved from position 0 to position 3, the map would be
0 => 3, 1 => 4, 2 => 0, 3 => 2, 4 => 1
So that, for all i
words[i] = orderedWords[map[i]]
How can I do this? I expect it should be possible in the same time complexity as ordinary sorting, ie. O(n log n)
I tried:
var map = words.Select((word, index) => new { Word = word, Index = index}).OrderBy(x => x.Word).Select(x => x.Index);
But this gives
> 2, 4, 3, 0, 1
Which fails the identity I gave above
for(int i = 0; i < words.Length; i++)
Assert.AreEqual(words[i], orderedWords[map[i]]);
Upvotes: 3
Views: 394
Reputation: 460238
You have to use the new index after the OrderBy
:
var map = words
.Select((word, index) => new { Word = word, Index = index })
.OrderBy(x => x.Word)
.Select((x, NewIndex) => new { x.Word, x.Index, NewIndex });
Result:
[0] { Word = "a", Index = 2, NewIndex = 0 }
[1] { Word = "dreary", Index = 4, NewIndex = 1 }
[2] { Word = "midnight", Index = 3, NewIndex = 2 }
[3] { Word = "once", Index = 0, NewIndex = 3 }
[4] { Word = "upon", Index = 1, NewIndex = 4 }
Update: acc. comment: "how do I get the 'old index to new index' data structure out?"
You could use a Dictionary:
var map = words
.Select((word, index) => new { Word = word, OldIndex = index })
.OrderBy(x => x.Word)
.Select((x, NewIndex) => new { x.Word, x.OldIndex, NewIndex })
.ToDictionary(x => x.OldIndex, x => x.NewIndex);
for (int i = 0; i < words.Length; i++)
{
Console.WriteLine("Word: {0} \tOldIndex: {1} \tNewIndex: {2}", words[i], i, map[i]);
}
Result:
Word: once OldIndex: 0 NewIndex: 3
Word: upon OldIndex: 1 NewIndex: 4
Word: a OldIndex: 2 NewIndex: 0
Word: midnight OldIndex: 3 NewIndex: 2
Word: dreary OldIndex: 4 NewIndex: 1
Upvotes: 3