Javascriptのsort()はどのように機能しますか?


94

次のコードは、この配列を番号順に並べ替える方法を教えてください。

var array=[25, 8, 7, 41]

array.sort(function(a,b){
  return a - b
})

計算の結果が...

0未満:「a」は「b」よりも低いインデックスになるようにソートされます。
ゼロ:「a」と「b」は等しいと見なされ、ソートは実行されません。
0より大きい:「b」は「a」よりも低いインデックスになるようにソートされます。

配列ソートコールバック関数は、ソート中に何度も呼び出されますか?

もしそうなら、毎回どの2つの数値が関数に渡されるのか知りたいです。最初に「25」(a)と「8」(b)、次に「7」(a)と「41」(b)が必要だと思いました。

25(a)-8(b)= 17(ゼロより大きいため、「b」を「a」よりも低いインデックスに並べ替えます):8、25

7(a)-41(b)= -34(ゼロ未満なので、「a」を「b」よりも低いインデックスに並べ替えます:7、41

次に、2つの数値セットはどのように相互に関連してソートされますか?

苦労している初心者を助けてください!


5
これが文字化けした意味をなすといいのですが!
cw84 2009

回答:


51

配列ソートコールバック関数は、ソート中に何度も呼び出されますか?

はい

もしそうなら、毎回どの2つの数値が関数に渡されるのか知りたいです

あなたはあなた自身を見つけることができます:

array.sort((a,b) => {
  console.log(`comparing ${a},${b}`);
  return a > b ? 1
               : a === b ? 0 
                         : -1;
});

編集

これは私が得た出力です:

25,8
25,7
8,7
25,41

8
むしろ、console.logをfirebugまたはhtmlDIV要素の.innerHTML + = "comparing" + a + "、" + b + "\ n"に対して実行します。
ジョシュア

2
これはウィキのようなサイトであり、他の回答を編集して改善できることを忘れないでください:)
Pablo Fernandez

7
新しいES6構文に関する注意:array.sort((a,b) => a - b);有効な構文です
Sterling Archer

1
ソート関数が-veを返す場合はスワップし、+ veを返す場合はスワップしませんか??
マヒ2016

2
@ShekharReddyは、引き続き演算子を使用して比較できます。答えを更新しました。
OscarRyz 2018年

44

JavaScriptインタープリターには、ある種のソートアルゴリズムの実装が組み込まれています。ソート操作中に比較関数を数回呼び出します。比較関数が呼び出される回数は、特定のアルゴリズム、並べ替えるデータ、および並べ替え前の順序によって異なります。

一部の並べ替えアルゴリズムは、通常の場合よりもはるかに多くの比較を行うため、既に並べ替えられたリストではパフォーマンスが低下します。事前にソートされたリストにうまく対処できるものもありますが、パフォーマンスが低下するように「だまされて」しまう場合もあります。

単一のアルゴリズムがすべての目的に完全であるとは限らないため、一般的に使用されている多くのソートアルゴリズムがあります。ジェネリックソートに最もよく使用される2つは、クイックソートマージソートです。多くの場合、クイックソートは2つのうちで高速ですが、マージソートには、全体的な選択に適した優れたプロパティがいくつかあります。マージソートは安定していますが、クイックソートは安定していません。どちらのアルゴリズムも並列化可能ですが、マージソートが機能する方法により、並列実装がより効率的になり、他のすべてが同等になります。

特定のJavaScriptインタープリターは、これらのアルゴリズムの1つまたは他の何かを完全に使用する場合があります。ECMAScriptの標準は、アルゴリズムを指定しません準拠した実装が使用する必要があります。それは、安定性の必要性を明確に否定しさえします。


1
FWIW、基本的なクイックソートは、パフォーマンスを低下させるために「だまされる」可能性のあるアルゴリズムの1つです。単純な形式では、すでにソートされているか、完全に逆になっているリストに対してO(N ^ 2)のパフォーマンスがあります。ほとんどのライブラリクイックソートアルゴリズムには、これらの一般的な最悪のシナリオを回避するのに役立つ多くの非自明な最適化があります。
アディサク

3
JavaScriptCoreは、ソートされる配列を変更するコンパレータ関数に直面して決定論的に動作する必要があるため、実際にはソートにAVLツリーを使用します。
olliej 2009


11

値のペアは、一度に1ペアずつ比較されます。比較されるペアは実装の詳細です。すべてのブラウザーで同じになるとは限りません。コールバックは何でもかまいません(したがって、文字列やローマ数字など、1,0、-1を返す関数を思い付くことができるものなら何でもソートできます)。

JavaScriptのソートで覚えておくべきことの1つは、JavaScriptが安定していることが保証されていないということです。


5

配列ソートコールバック関数は、ソート中に何度も呼び出されますか?

はい、まさにそれです。コールバックは、必要に応じて配列内の要素のペアを比較して、要素の順序を決定するために使用されます。比較関数の実装は、数値の並べ替えを処理する場合に異例ではありません。中詳細スペックまたは上のいくつかの他のより読みやすいサイト。


4

配列ソートコールバック関数は、ソート中に何度も呼び出されますか?

これは比較ソートであるため、N個のアイテムが与えられた場合、クイックソートのような高速ソートでは、コールバック関数を平均(N * Lg N)回呼び出す必要があります。使用されるアルゴリズムがバブルソートのようなものである場合、コールバック関数は平均(N * N)回呼び出されます。

比較ソートの呼び出しの最小数は(N-1)であり、これはすでにソートされたリストを検出するためだけです(つまり、スワップが発生しない場合はバブルソートの早い段階で)。


3

深い知識

結果が負の場合、aはbの前にソートされます。

結果が正の場合、bはaの前にソートされます。

結果が0の場合、2つの値のソート順は変更されません。

注意:

このコードは、sortメソッドの内部のステップバイステップのビューです。

出力:

let arr = [90, 1, 20, 14, 3, 55];
var sortRes = [];
var copy = arr.slice();		//create duplicate array
var inc = 0;	//inc meant increment
copy.sort((a, b) => {
	sortRes[inc] = [ a, b, a-b ];
	inc += 1;
	return a - b;
});
var p = 0;
for (var i = 0; i < inc; i++) {
	copy = arr.slice();
	copy.sort((a, b) => {
		p += 1;
		if (p <= i ) {
			return a - b;
		}
		else{
			return false;
		}
	});
	p = 0;
	console.log(copy +' \t a: '+ sortRes[i][0] +' \tb: '+ sortRes[i][1] +'\tTotal: '+ sortRes[i][2]);
}


0

このコードを実行します。最初から最後まで、正確なステップバイステップの並べ替えプロセスを確認できます。

var array=[25, 8, 7, 41]
var count = 1;
array.sort( (a,b) => { 
console.log(`${count++}). a: ${a} | b: ${b}`);
return a-b;
});
console.log(array);

コピーしてコンソールに貼り付けたところ、未定義が返されました。
iPzard

0

Array#sortとそのコンパレータの動作を明確にするために、プログラミングコースの開始時に教えられたこの素朴な挿入ソートを検討してください。

const sort = arr => {
  for (let i = 1; i < arr.length; i++) {
    for (let j = i; j && arr[j-1] > arr[j]; j--) {
      [arr[j], arr[j-1]] = [arr[j-1], arr[j]];
    }
  }
};

const array = [3, 0, 4, 5, 2, 2, 2, 1, 2, 2, 0];
sort(array);
console.log("" + array);

アルゴリズムとしての挿入ソートの選択を無視して、ハードコードされたコンパレータに焦点を合わせますarr[j-1] > arr[j]。これには、議論に関連する2つの問題があります。

  1. >オペレータは、配列の要素がありますが、オブジェクトがに応答しないようにソートすることがあります多くのペアに呼び出された>(私たちが使用した場合と同じが本当だろう合理的な方法で-)。
  2. 数値を使用している場合でも、ここで焼き付けられている昇順の並べ替え以外の配置が必要になることがよくあります。

comparefnよく知っている引数を追加することで、これらの問題を修正できます。

const sort = (arr, comparefn) => {
  for (let i = 1; i < arr.length; i++) {
    for (let j = i; j && comparefn(arr[j-1], arr[j]) > 0; j--) {
      [arr[j], arr[j-1]] = [arr[j-1], arr[j]];
    }
  }
};

const array = [3, 0, 4, 5, 2, 2, 2, 1, 2, 2, 0];
sort(array, (a, b) => a - b);
console.log("" + array);

sort(array, (a, b) => b - a);
console.log("" + array);

const objArray = [{id: "c"}, {id: "a"}, {id: "d"}, {id: "b"}];
sort(objArray, (a, b) => a.id.localeCompare(b.id));
console.log(JSON.stringify(objArray, null, 2));

これで、単純なソートルーチンが一般化されました。このコールバックがいつ呼び出されたかを正確に確認して、最初の一連の懸念に答えることができます。

配列ソートコールバック関数は、ソート中に何度も呼び出されますか?もしそうなら、毎回どの2つの数値が関数に渡されるのか知りたいです

以下のコードを実行すると、はい、関数が何度も呼び出され、console.logどの番号が渡されたかを確認するために使用できることがわかります。

const sort = (arr, comparefn) => {
  for (let i = 1; i < arr.length; i++) {
    for (let j = i; j && comparefn(arr[j-1], arr[j]) > 0; j--) {
      [arr[j], arr[j-1]] = [arr[j-1], arr[j]];
    }
  }
};

console.log("on our version:");
const array = [3, 0, 4, 5];
sort(array, (a, b) => console.log(a, b) || (a - b));
console.log("" + array);

console.log("on the builtin:");
console.log("" + 
  [3, 0, 4, 5].sort((a, b) => console.log(a, b) || (a - b))
);

あなたが尋ねる:

次に、2つの数値セットはどのように相互に関連してソートされますか?

用語を正確にa及びbませんセット番号の- (あなたの例では、彼らしている数字)の配列ですばらしいスタイルオブジェクト。

真実は、実装に依存するため、どのようにソートされているは問題ではありません。挿入ソートとは異なるソートアルゴリズムを使用した場合、コンパレータはおそらく異なる数値のペアで呼び出されますが、ソート呼び出しの最後に、JSプログラマーにとって重要な不変条件は、結果の配列が次のようにソートされることです。コンパレーターは、指定したコントラクトに準拠した値を返すと想定します(<0の場合a < b、0の場合a === b、> 0の場合a > b)。

仕様に違反しない限り、ソートの実装を自由に変更できるのと同じ意味で、ECMAScriptの実装は、言語仕様の範囲内でソートの実装を自由に選択できるためArray#sort、異なるコンパレータ呼び出しが生成される可能性があります。さまざまなエンジンで。ロジックが特定の比較シーケンスに依存しているコードを作成することはありません(また、コンパレータが最初に副作用を生成することもありません)。

たとえば、V8エンジン(執筆時点)は、配列が事前に計算された要素数よりも大きい場合にTimsortを呼び出し、小さな配列チャンクに対してバイナリ挿入ソートを使用します。ただし、以前は不安定なクイックソートを使用していたため、異なるシーケンスの引数とコンパレータへの呼び出しが行われる可能性がありました。

ソートの実装が異なれば、コンパレータ関数の戻り値の使用方法も異なるため、コンパレータがコントラクトに準拠していない場合、これにより予期しない動作が発生する可能性があります。例については、このスレッドを参照してください。


-2
var array=[25, 8, 7, 41]

array.sort(function(a,b){
  console.log(`a = ${a} , b = ${b}`);
  return a - b
});

出力

  • a = 8、b = 25
  • a = 7、b = 8
  • a = 41、b = 7
  • a = 41、b = 8
  • a = 41、b = 25

私のブラウザ(Google Chromeバージョン70.0.3538.77(公式ビルド)(64ビット))では、最初の反復で、引数aは配列の2番目の要素であり、引数bは配列の最初の要素です。

比較関数が返す場合

  1. bよりも負の値はaに移動します。
  2. aよりも正の値はbに進みます。
  3. 0(ゼロ)aとbの両方がそのまま残ります。
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.