Javascript: Ordena el array y devuelve un array de indicies que indica la posición de los elementos ordenados con respecto a los elementos originales


Supongamos que tengo una matriz Javascript, así:

var test = ['b', 'c', 'd', 'a'];

Quiero ordenar la matriz. Obviamente, puedo hacer esto para ordenar la matriz:

test.sort(); //Now test is ['a', 'b', 'c', 'd']

Pero lo que realmente quiero es una matriz de índices que indique la posición de los elementos ordenados con respecto a los elementos originales. No estoy muy seguro de cómo expresar esto, así que tal vez es por eso que estoy teniendo problemas para averiguar cómo hacerlo.

Si tal método se llama sortIndices (), entonces lo que me gustaría is:

var indices = test.sortIndices();
//At this point, I want indices to be [3, 0, 1, 2].

'a' estaba en la posición 3, 'b' estaba en 0, 'c' estaba en 1 y 'd' era un 2 en la matriz original. Por lo tanto, [3, 0, 1, 2].

Una solución sería ordenar una copia del array, y luego recorrer el array ordenado y encontrar la posición de cada elemento en el array original. Pero, eso se siente torpe.

¿Existe un método que haga lo que quiero? Si no, ¿cómo escribirías un método que haga esto?

Author: Seanny123, 2010-09-17

5 answers

var test = ['b', 'c', 'd', 'a'];
var test_with_index = [];
for (var i in test) {
    test_with_index.push([test[i], i]);
}
test_with_index.sort(function(left, right) {
  return left[0] < right[0] ? -1 : 1;
});
var indexes = [];
test = [];
for (var j in test_with_index) {
    test.push(test_with_index[j][0]);
    indexes.push(test_with_index[j][1]);
}

Editar

Ustedes tienen razón sobre for .. in. Eso se romperá si alguien mastica el prototipo de la matriz, lo que observo a menudo. Aquí está con eso fijo, y envuelto en una función más utilizable.

function sortWithIndeces(toSort) {
  for (var i = 0; i < toSort.length; i++) {
    toSort[i] = [toSort[i], i];
  }
  toSort.sort(function(left, right) {
    return left[0] < right[0] ? -1 : 1;
  });
  toSort.sortIndices = [];
  for (var j = 0; j < toSort.length; j++) {
    toSort.sortIndices.push(toSort[j][1]);
    toSort[j] = toSort[j][0];
  }
  return toSort;
}

var test = ['b', 'c', 'd', 'a'];
sortWithIndeces(test);
alert(test.sortIndices.join(","));
 33
Author: Dave Aaron Smith,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-09-16 21:04:20

Simplemente llenaría una matriz con números 0..n-1, y ordenar que con una función de comparación.

var test = ['b', 'c', 'd', 'a'];
var len = test.length;
var indices = new Array(len);
for (var i = 0; i < len; ++i) indices[i] = i;
indices.sort(function (a, b) { return test[a] < test[b] ? -1 : test[a] > test[b] ? 1 : 0; });
console.log(indices);
 19
Author: Sly1024,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-01-21 13:13:04

Dave Aaron Smith tiene razón (no puedo comentar), sin embargo creo que es interesante usar Array map() aquí.

var test = ['b', 'c', 'd', 'a'];
// make list with indices and values
indexedTest = test.map(function(e,i){return {ind: i, val: e}});
// sort index/value couples, based on values
indexedTest.sort(function(x, y){return x.val > y.val ? 1 : x.val == y.val ? 0 : -1});
// make list keeping only indices
indices = indexedTest.map(function(e){return e.ind});
 4
Author: clerbois,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-12-15 23:35:50
Array.prototype.sortIndices = function (func) {
    var i = j = this.length,
        that = this;

    while (i--) {
        this[i] = { k: i, v: this[i] };
    }

    this.sort(function (a, b) {
        return func ? func.call(that, a.v, b.v) : 
                      a.v < b.v ? -1 : a.v > b.v ? 1 : 0;
    });

    while (j--) {
        this[j] = this[j].k;
    }
}

YMMV sobre cómo se siente al agregar funciones al prototipo de matriz y mutar matrices en línea, pero esto permite ordenar una matriz de cualquier objeto que se pueda comparar. Toma una función opcional que se puede usar para ordenar, como Array.prototype.sort.

Un ejemplo,

var test = [{b:2},{b:3},{b:4},{b:1}];

test.sortIndices(function(a,b) { return a.b - b.b; });

console.log(test); // returns [3,0,1,2]
 2
Author: Russ Cam,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-09-16 23:09:11

Puede lograr esto con una sola línea usando es6 (generando una matriz de índice 0->N-1 y ordenándola en función de los valores de entrada).

var test = ['b', 'c', 'd', 'a']

var result = Array.from(Array(test.length).keys())
                  .sort((a, b) => test[a] < test[b] ? -1 : (test[b] < test[a]) | 0)
 1
Author: Matt Way,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-02-13 02:20:40