web-dev-qa-db-fra.com

Javascript: trie et retourne un tableau d'indices indiquant la position des éléments triés par rapport aux éléments d'origine

Supposons que j'ai un tableau Javascript, comme ceci: 

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

Je veux trier le tableau. Évidemment, je peux simplement faire ceci pour trier le tableau:

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

Mais ce que je veux vraiment, c'est un tableau d'indices indiquant la position des éléments triés par rapport aux éléments d'origine. Je ne sais pas trop comment formuler cela, alors c'est peut-être pourquoi j'ai du mal à comprendre comment le faire. 

Si une telle méthode s'appelait sortIndices (), ce que je voudrais, c'est:

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

"a" était en position 3, "b" était à 0, "c" était à 1 et "d" était à 2 dans le tableau d'origine. Par conséquent, [3, 0, 1, 2].

Une solution serait de trier une copie du tableau, puis de parcourir le tableau trié et de trouver la position de chaque élément dans le tableau d'origine. Mais, cela semble maladroit. 

Existe-t-il une méthode existante qui fait ce que je veux? Sinon, comment voudriez-vous écrire une méthode qui le fasse?

31
Jeremy
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]);
}

Modifier

Vous avez raison à propos de for .. in. Cela briserait si quelqu'un munissait le prototype de matrice, ce que j'observe souvent avec agacement. Ici, c'est avec cela fixé, et enveloppé dans une fonction plus utilisable.

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(","));
40
Dave Aaron Smith

Je voudrais juste remplir un tableau avec des nombres 0..n-1, et trier cela avec une fonction de comparaison.

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
Sly1024

Dave Aaron Smith a raison (je ne peux pas commenter), mais j'estime intéressant d'utiliser Array map () ici.

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});
6
clerbois
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 sur ce que vous pensez d’ajouter des fonctions au prototype Array et de transformer des tableaux en ligne, mais cela permet de trier un tableau de tous les objets pouvant être comparés. Une fonction facultative peut être utilisée pour le tri, un peu comme Array.prototype.sort.

Un exemple,

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
Russ Cam

Vous pouvez accomplir cela avec une seule ligne en utilisant es6 (générer un tableau d'index 0->N-1 et le trier en fonction des valeurs d'entrée).

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)
2
Matt Way

Ceci est une version ES6 plus idiomatique de la réponse de clerbois, voir ses commentaires pour les commentaires:

return test.map((val, ind) => {return {ind, val}})
           .sort((a, b) => {return a.val > b.val ? 1 : a.val == b.val ? 0 : -1 })
           .map((obj) => obj.ind);
0
George Jempty