8
  1. I have an array. I sort it.
  2. I get a second array which is already sorted based on the first one.

I need to reverse the sorting on the second array.

For example, if the first array (unsorted) is: [9, 5, 3, 0, 2] then I want to to sort it, so that it becomes [0, 2, 3, 5, 9].

Then I receive the second array sorted based on the first one, for example ["home", "car", "train", "pc", "mouse"]. I need it to become ["mouse, "pc", "train", "home", "car"].

I can't make a copy of the array.


I have the following code:

//data_r is an array with values

var i = 0;
var sort_order = new Array();

data_r.sort(function (a,b) {
    var res = a[0] - b[0];
    
    sort_order[i] = res;
    i++;
    
    return res;
});

In the end, the the sort_order array will contain the actions performed when we sorted items. If I want to sort a second array exactly the same way as the first then I can do the following:

//data_x is an array with values

var i = 0;
data_x.sort(function (a,b) {
    i++;
    return sort_order[i-1];
});

Now the data_x array is sorted exactly the same way as the data_r array.

How can I undo sort on the data_r array?

The following code is incorrect:

var unsort = new Array();

for(var i = 0; i < data_r.length; i++)
    unsort[i] = sort_order[i]*(-1);//-1 so we perfom the oposite action
double-beep
  • 5,031
  • 17
  • 33
  • 41
Zen 8000k
  • 332
  • 1
  • 4
  • 11

7 Answers7

12

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.

  • Yes, and the idea of a randomized sort process is not outlandish. – Pointy Jul 11 '13 at 16:17
  • @Pointy: Indeed - a randomized pivot for quicksort is a widely recommended practice, for instance. –  Jul 11 '13 at 16:18
  • First of all, are you sure about the "it's possible that it's using a randomized algorithm."? If the arrays are the same size, it works for me. It makes no sence to use a randomized algorithm. Now, i don't exactly know what kind of data it contains but those data are numbers, either positive, negative or zero. Finaly, I have to way to copy the array **as the site is very complex** on that part. – Zen 8000k Jul 11 '13 at 16:22
  • @Zen8000k The point is, nobody **can** be sure. [The specification](http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.11) doesn't say anything about a particular implementation; it doesn't even guarantee that the sort is stable. – Carsten Jul 11 '13 at 16:26
  • 1
    @Zen8000k Have a look at [this answer](http://stackoverflow.com/a/236534/1071311). Hell, even within WebKit, different sorting algorithms are used. – Carsten Jul 11 '13 at 16:28
  • I explained on my post why i can't copy the array. I see your point in the random part also. – Zen 8000k Jul 11 '13 at 16:29
5

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.

Erik Olson
  • 1,154
  • 8
  • 18
  • Great idea, i will check it out! – Zen 8000k Jul 11 '13 at 16:43
  • +1 for the approach, though the code samples are not that good (and you don't need the `unsort_indices` array at all). I've rolled my own answer with better ones :-) – Bergi Jul 11 '13 at 17:15
4

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
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Why return data_r[a][0] - data_r[b][0]; Why you treat data_r as a 2 dim array? – Zen 8000k Jul 11 '13 at 17:22
  • @Zen8000k: OP did that in his code as well. Seeing his second edit now I'm not sure whether it's correct though. – Bergi Jul 11 '13 at 17:32
  • OK, removing the `[0]`s from my code. It's quite the same as the one in the fiddle now ;-) – Bergi Jul 11 '13 at 17:40
  • @Zen8000k: In your original code, you did `sort` the `data_r` array. That's what this snippet does as well, it applies the `sort_order` mapping on the `data_r` array. It's just like constructing the `data_x_sorted` array, only that it works in-place. – Bergi Jul 11 '13 at 17:49
  • @Zen8000k: Whoops, big misconception. I realized that what I was trying to do has a much bigger complexity than I saw at first, see the question [In-place array reordering?](http://stackoverflow.com/q/7365814/1048572). It better stays commented out :-) – Bergi Jul 12 '13 at 12:52
1

While this question is 8 years old at this point, I came across it when trying to find the same solution to the problem and I was unable to find a suitable, performant, and intuitive way of doing so, so I wrote one myself.

Please take a look at the sort-unwind library. If ranks is a list of indexes that would rank an array in order...

import unwind from 'sort-unwind'

const suits = ['♥', '♠', '♣', '♦']
const ranks = [2, 0, 3, 1]

const [sortedSuits, tenet] = unwind(ranks, suits)
// sortedSuits <- ['♠', '♦', '♥', '♣']
// unwind <- [1, 3, 0, 2]

You can then use the tenet variable that's returned to unsort an array and restore the original ordering.

const names = ['spades', 'diamonds', 'hearts', 'clubs']
const [tenetNames, tenetRanks] = unwind(tenet, names)
// tenetNames <- ['hearts', 'spades', 'clubs', 'diamonds']
// tenetRanks <- [2, 0, 3, 1]
Philihp Busby
  • 4,389
  • 4
  • 23
  • 19
  • I'm not sure I've ever needed this, but it's a nice idea. I don't see the need for the Ramda pipeline, though. I'm the founder of Ramda and a big fan, so it's not that. But this looks like a perfectly good implementation: `const unwind = (ranks, xs) => [ranks.map (x => ranks [ranks [x]]), ranks .map ((_, i) => xs [ranks .indexOf (i)])]`. It works for your example: `unwind ([2, 0, 3, 1], ['♥', '♠', '♣', '♦']) //=> [[1, 3, 0, 2], ["♠", "♦", "♥", "♣"]]`. Calling again reverses it: `unwind ([1, 3, 0, 2], ["♠", "♦", "♥", "♣"]) //=> [[2, 0, 3, 1], ["♥", "♠", "♣", "♦"]]`. – Scott Sauyet Mar 11 '21 at 16:51
0

The sort function just returns a number which can be positive,zero, or negative telling it if the current element goes before,has same weight, or goes after the element it is comparing it too. I would imagine your sort order array is longer than your data_r array because of the number of comparisons you make. I would just make a copy of data_r before you sort it and then set data_r equal to that array when you want it unsorted.

Legion
  • 796
  • 7
  • 12
0

If you have a lot of these arrays to maintain, it might be as well to convert array1 into an array of objects, each one containing the value and its original position in the array. This keeps everything together in one array.

var array1 = [9, 5, 3, 0, 2];
var array2 = ["home", "car", "train", "pc", "mouse"];

var sort = function(array){
    var indexed_objects =  array.map(function(value, index){
        return {index: index, value: value};
    });
    indexed_objects.sort(function(a,b){
        return a.value <= b.value ? -1 : 1;
    });
    return indexed_objects;
};

var sorted1 = sort(array1);
sorted1; // [{index: 3, value:0}, {index: 4, value: 2}, ...]

And now, given an array of sorted objects, we can write a function to unsort any other array accordingly:

var unsort = function(array, sorted_objects){
    var unsorted = [];
    sorted_objects.forEach(function(item, index){
        unsorted[item.index] = array[index];
    });
    return unsorted;
};

var array2_unsorted = unsort(array2, sorted1);
array2_unsorted; // ["mouse", "pc", "train", "home", "car"]
1983
  • 5,882
  • 2
  • 27
  • 39
  • 1
    Having comparefn return boolean values doesn't seem to sort arrays with 11+ elements in Chrome correctly. Remember to always return a number as specified. – Robert Dec 07 '14 at 21:44
0

v1 = [0,1,2,3,4,5,6]
q = v1.length
b = []

for(i=0;i<q;i++){
 r = parseInt(Math.random()*v1.length)
 b.push(v1[r])
 a = v1.indexOf(v1[r])
 v1.splice(a,1)
}
Kcireh
  • 1
  • 1
  • You may want to consider adding some text that explains what your code sample does, which could be more useful to others. Also, if you haven't already, make sure to look around [help] for more info on how SO works. – 0xCursor Jan 18 '19 at 23:31