Undo sort on sorted array in javascript

Your premise here is flawed.

In the end, the sort_order array contains the actions performed when we sorted items.

No, it doesn't; it contains a log of the comparisons performed by the Javascript Array.sort function. The actions it took in response to those comparison results are private to it.

If I want to sort a second array exactly the same way as the first then I can do the following:

This is not guaranteed to work. Even if the two arrays are the same size, Array.sort may not always compare the same elements in the same order each time it's called - it's possible that it's using a randomized algorithm, that it performs comparisons based on other data that are internal to the interpreter, or that it switches between multiple entirely different sort algorithms under some circumstances.

While this code may work for you, right now, in your current web browser, it is likely to fail in surprising ways in other circumstances (possibly in future browsers). Do not use this technique in production code.

The question is, how can i unsort the data_r array?

Make a copy of the array before you sort it.


Storing res[i] = a - b is like journaling the sort() algorithm - but what if it used a random pivot? This code is inherently unreliable unless you write sort() yourself. It's also inefficient.

A better approach, one that will solve both your needs, is to create an array of indices and sort that. This is trivial to invert. Then you can implement a permute function that takes an array of indices, and it achieves a sort or unsort, depending on the input.

If x is from 0:n-1, create an array sort_i of same size, then initialize each sort_i[i] = i.

for(var i = 0; i < n; i++)
    sort_i[i] = i;

Then

sort_i.sort(function (a,b) { return x[a] - x[b]; });

Now you have the indices. To apply to x:

for(var i = 0; i < n; i++)
    sort_x[i] = x[sort_i[i]];

To unsort it, first invert the indices

for(var i = 0; i < n; i++)
    unsort_i[sort_i[i]] = i;

Then apply the indices. Exercise left to question asker.

This approach of sorting an array of integer indices is needed when you don't want to move the original elements around in memory (maybe they are big objects), and many other circumstances. Basically you are sorting pointers. The result is an index to the data, and a reverse index.


See @duskwuff's answer on why your approach doesn't work.

Instead, just introduce a mapping between the original data and the sorted data.

{0:2, 1:3, 2:1, 3:0}

Which means the first element became the third, the second became the last and so on. Below we'll use an array instead of an object.

Why does this map help? You can sort it like another dataset by just using the indizes in it as pointers to the data you're going to compare. And you can apply the mapping easily on other datasets. And you can even reverse that mapping very easily. See it in the code:

// data_r, data_x are arrays with values

var l = data_r.length;
var sort_order = new Array(l);
for (var i=0; i<l; i++) sort_order[i] = i; // initialised as 1-1 mapping

// change the sort_order first:
sort_order.sort(function (a,b) {
    // a and b being indices
    return data_r[a] - data_r[b];
});
// Making a new, sorted array
var data_x_sorted = new Array(l);
for (var i=0; i<l; i++)
    data_x_sorted[ sort_order[i] ] = data_x[i]; // put it to sorted position

If you want to sort the data_x array itself, just use the "apply" algorithm which I showed for data_r.

The question is, how can I undo sort on the data_r array?

Either don't sort it at all, and just make a copy of it which gets sorted (or do nothing at all).

Or use the sort_order to reverse it. You just would need to swap i and newIndex (sortOrder[i]) everywhere. Example for building a new, "unsorted" (old-order) array:

var unsorted = new Array(l);
for (var i=0; i<l; i++)
    unsorted[i] = data_r[ sort_order[i] ]; // take it from its new position