web-dev-qa-db-ja.com

Javascript:配列をソートし、元の要素に対するソートされた要素の位置を示すインデックスの配列を返します

次のようなJavaScript配列があるとします。

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

配列をソートしたい。明らかに、配列をソートするためにこれを行うことができます:

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

しかし、私が本当に欲しいのは、元の要素に対するソートされた要素の位置を示すインデックスの配列です。言い方がよくわからないので、どうしてか分かりません。

そのようなメソッドがsortIndices()と呼ばれた場合、私が望むのは:

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

元の配列では、「a」は位置3、「b」は0、「c」は1、「d」は2でした。したがって、[3、0、1、2]。

1つの解決策は、配列のコピーを並べ替えてから、並べ替えられた配列を循環させて、元の配列内の各要素の位置を見つけることです。しかし、それは不格好に感じます。

私がしたいことをする既存の方法はありますか?そうでない場合、これを行うメソッドをどのように記述しますか?

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

編集

君たちは正しいfor .. in。誰かがアレイのプロトタイプを変更すると、それはうまくいきません。ここでは、それが修正され、より使いやすい関数にまとめられています。

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(","));
41

配列に0..n-1の数値を入力し、それを比較関数で並べ替えるだけです。

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);
27
Sly1024

Dave Aaron Smithは正しいですが、ここではArray map()を使用するのは興味深いと思います。

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});
8
clerbois

これはクレボアの答えのより慣用的なES6バージョンです。コメントについては彼/彼女を参照してください。

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);
3
Dexygen

これは、es6を使用して1行で実現できます(0->N-1インデックス配列と入力値に基づいたソート)。

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)
3
Matt Way
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では、Arrayプロトタイプに関数を追加し、配列をインラインで変更する方法について感じていますが、これにより、比較可能な任意のオブジェクトの配列を並べ替えることができます。これは、Array.prototype.sortのように、ソートに使用できるオプションの関数を取ります。

例、

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