JavaScriptで文字列を並べ替える方法


344

attr文字列型のフィールドに基づいてソートしたいオブジェクトのリストがあります。使ってみた-

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

しかし-、JavaScriptの文字列では動作しないようです。文字列型の属性に基づいてオブジェクトのリストをソートするにはどうすればよいですか?


1
stackoverflow.com/questions/2140627/…を参照JavaScript case insensitive string comparisonしてください
Adrien Be

迅速な「国際化された」ソリューションの場合(これは世界のすべてのアクセントをカバーしない可能性があるため、部分的にしか推測しない)、アクセントを単に無視する、つまり削除することをお勧めします。次に、文字列の比較のみを行います。stackoverflow.comJavascript : remove accents/diacritics in strings
questions

2
面白いことに、Jeff Atwood自身が2007年にこの一般的な問題についてブログに投稿しました。blog.codinghorror.com/ sorting
Adrien Be

回答:


620

String.prototype.localeCompareあなたの例ごとに使用してください:

list.sort(function (a, b) {
    return ('' + a.attr).localeCompare(b.attr);
})

例外を回避するために、a.attrを文字列に強制します。Internet Explorer 6とFirefox 1 以降でlocaleCompareサポートされています。ロケールに関係のない次のコードが使用されている場合もあります。

if (item1.attr < item2.attr)
  return -1;
if ( item1.attr > item2.attr)
  return 1;
return 0;

81
誰かが私と同じように急いで失敗する前に、それはlocalCompareではなくローカルe Compareです。
12

12
最初の解決策は、文字「A」が「z」の後で、「Z」の前に来ると見なします。これは、文字ASCII値で比較を行っているためです。localeCompare()この問題は発生しませんが、数値を理解できないため、ほとんどの言語でのソート比較と同様に、["1"、 "10"、 "2"]が得られます。alphanum /自然ソートアルゴリズムにあなたのUIのフロントエンドのために並べ替えたい場合は、ルックstackoverflow.com/questions/4340227/...またはstackoverflow.com/questions/4321829/...
Dead.Rabit

2
これlocaleCompare()は最新のブラウザーでのみサポートされていることに注意してください。執筆時点ではIE11 +です。developer.mozilla.org/ en-US / docs / Web / JavaScript / Reference /…を参照して
Adrien Be

3
いいえ、つまり、表の最初の行、@ Adrien-IEはlocaleCompare()多くのバージョンの遡りをサポートしていますが、バージョン11までのロケールの指定をサポートしていません。Dead.Rabitがリンクしている質問にも注意してください。
Shog9 2014

3
@ Shog9私の悪いことに、IE6からサポートされているようです!msdn.microsoft.com/en-us/library/ie/s4esdbwz(v=vs.94).aspxの(scroll-down / search to localeCompare()メソッド)を参照してください。ただし、ロケールとオプションの引数を使用しない古い実装(IE11より前に使用されたもの)では使用されるロケールとソート順は完全に実装に依存しています。つまり Firefox、Safari、Chrome、IEはそうです文字列を同じ順序で並べ替えないでください。code.google.com/p/v8/issues/detail?id=459を
Adrien Be

166

更新された回答(2014年10月)

この文字列の自然なソート順に本当に悩まされていたので、この問題を調査するためにかなりの時間をかけました。これがお役に立てば幸いです。

短い話

localeCompare()キャラクターサポートは悪いです、それを使ってください。が指摘したようShog9に、あなたの質問に対する答えは次のとおりです。

return item1.attr.localeCompare(item2.attr);

すべてのカスタムJavaScriptの「自然な文字列の並べ替え順序」の実装で見つかったバグ

かなりたくさんのカスタム実装があり、より正確に「自然な文字列のソート順」と呼ばれる文字列比較を行おうとしています

これらの実装で「遊ぶ」とき、私はいつも奇妙な「自然なソート順」の選択、またはむしろ間違い(または最良の場合は省略)に気づきました。

通常、特殊文字(スペース、ダッシュ、アンパサンド、角かっこなど)は正しく処理されません。

次に、それらがさまざまな場所で混ざって表示されるのを見つけます。

  • 一部は大文字の「Z」と小文字の「a」の間にあります
  • 一部は「9」と大文字の「A」の間にあります
  • 一部は小文字の「z」の後ろになります

スペースの特殊文字(常に最初の文字)を除いて、すべての特殊文字が1つの場所に「グループ化」されることが期待される場合。つまり、数字の前すべて、または数字と文字の間のすべて(小文字と大文字が次々に「一緒に」)、またはすべて文字の後。

私の結論は、かろうじて珍しい文字(つまり、発音記号やダッシュ、感嘆符などの文字を含む文字)を追加し始めると、すべてが一貫した順序を提供できないということです。

カスタム実装に関する調査:

ブラウザのネイティブな「自然な文字列ソート順」の実装 localeCompare()

localeCompare()(ロケールとオプション引数なし)最古の実装は、IE6 +によってサポートされている、参照http://msdn.microsoft.com/en-us/library/ie/s4esdbwz(v=vs.94).aspx((localeCompareまでスクロールし) 方法)。組み込みのlocaleCompare()方法では、国際文字や特殊文字を含めて、並べ替えの作業が大幅に改善されます。このlocaleCompare()メソッドを使用する唯一の問題は、「使用されるロケールとソート順は完全に実装に依存する」ということです。つまり、stringOne.localeCompare(stringTwo)などのlocaleCompareを使用する場合、Firefox、Safari、Chrome、IEでは文字列の並べ替え順序が異なります。

ブラウザネイティブ実装に関する調査:

「文字列自然ソート順」の難しさ

堅固なアルゴリズム(つまり、一貫しているが幅広い文字をカバーする)を実装することは、非常に困難な作業です。UTF8が含まれ、2000の以上の文字カバー120の以上のスクリプト(言語)。最後に、このタスクにはいくつかの仕様があります。これは「Unicode照合アルゴリズム」と呼ばれ、http://www.unicode.org/reports/tr10/にあります。あなたは私が投稿したこの質問でこれに関する詳細情報を見つけることができます/software/257286/is-there-any-language-agnostic-specification-for-string-natural-sorting-order

最終結論

したがって、私が遭遇したjavascriptカスタム実装によって提供される現在のレベルのサポートを考えると、このすべての文字とスクリプト(言語)のサポートに近づくことはないでしょう。したがって、ブラウザのネイティブのlocaleCompare()メソッドを使用する方がよいでしょう。はい、ブラウザ間で一貫性がないという欠点がありますが、基本的なテストでは、はるかに広い範囲の文字をカバーしているため、確実で意味のある並べ替え順序が可能です。

で指摘されているようShog9に、あなたの質問への答えは:

return item1.attr.localeCompare(item2.attr);

参考文献:

Shog9の良い答えのおかげで、私は「正しい」方向に私を信じました


38

回答(最新のECMAScript)

list.sort((a, b) => (a.attr > b.attr) - (a.attr < b.attr))

または

list.sort((a, b) => +(a.attr > b.attr) || -(a.attr < b.attr))

説明文

ブール値を数値にキャストすると、次のようになります。

  • true -> 1
  • false -> 0

次の3つのパターンを検討してください。

  • xはyより大きい:(x > y) - (y < x)-> 1 - 0->1
  • xはyと等しい:(x > y) - (y < x)-> 0 - 0->0
  • xはyより小さい:(x > y) - (y < x)-> 0 - 1->-1

(代替)

  • xはyより大きい:+(x > y) || -(x < y)-> 1 || 0->1
  • xはyと等しい:+(x > y) || -(x < y)-> 0 || 0->0
  • xはyより小さい:+(x > y) || -(x < y)-> 0 || -1->-1

したがって、これらのロジックは、典型的なソートコンパレーター関数と同等です。

if (x == y) {
    return 0;
}
return x > y ? 1 : -1;

1
このトリックを使用した以前の回答にコメントしたように、コードのみの回答は、それらがどのように機能するかを説明することにより、より役立つものにすることができます。
Dan Dascalescu、2018年

追加された説明
mpyw 2018年

これがlocaleCompareより良いか悪いかについてコメントできますか?
Ran Lottem

3
@RanLottem localeCompareと標準比較では異なる結果が得られます。あなたはどちらを期待していますか? ["A", "b", "C", "d"].sort((a, b) => a.localeCompare(b))大文字と小文字を区別しないアルファベット順に並べ替え、["A", "b", "C", "d"].sort((a, b) => (a > b) - (a < b))コードポイント順に並べ替え
mpyw

そうですね、それが一番のこだわりのようです。パフォーマンスの違いについて何か考えはありますか?
Ran Lottem

13

ここでは>または<および==を使用する必要があります。したがって、解決策は次のようになります。

list.sort(function(item1, item2) {
    var val1 = item1.attr,
        val2 = item2.attr;
    if (val1 == val2) return 0;
    if (val1 > val2) return 1;
    if (val1 < val2) return -1;
});

1
余談ですが、これは文字列と数値の比較を処理しません。例: 'Z' <9(false)、 'Z'> 9(false false ??)、 'Z' == 9(false false !!)。JavaScriptでの愚かなNaN ...
加藤

7

ネストされた三項矢印関数

(a,b) => (a < b ? -1 : a > b ? 1 : 0)

7

文字列はJavaScriptで直接比較できるので、これは仕事をします

list.sort(function (a, b) {
    return a.attr > b.attr ? 1: -1;
})

ソート関数の減算は、非アルファベット(数値)ソートが必要な場合にのみ使用され、もちろん文字列では機能しません


6

私は長い間これに悩まされていたので、私はついにこれを調査し、物事がそれらの方法である理由のこの長い風の理由をあなたに与えます。

スペックから:

Section 11.9.4   The Strict Equals Operator ( === )

The production EqualityExpression : EqualityExpression === RelationalExpression
is evaluated as follows: 
- Let lref be the result of evaluating EqualityExpression.
- Let lval be GetValue(lref).
- Let rref be the result of evaluating RelationalExpression.
- Let rval be GetValue(rref).
- Return the result of performing the strict equality comparison 
  rval === lval. (See 11.9.6)

それでは、11.9.6に進みます

11.9.6   The Strict Equality Comparison Algorithm

The comparison x === y, where x and y are values, produces true or false. 
Such a comparison is performed as follows: 
- If Type(x) is different from Type(y), return false.
- If Type(x) is Undefined, return true.
- If Type(x) is Null, return true.
- If Type(x) is Number, then
...
- If Type(x) is String, then return true if x and y are exactly the 
  same sequence of characters (same length and same characters in 
  corresponding positions); otherwise, return false.

それでおしまい。文字列に適用された三重の等号演算子は、引数がまったく同じ文字列(対応する位置に同じ長さと同じ文字)である場合にtrueを返します。

したがって===、異なるソースから到着した可能性があるが、最終的には同じ値になることがわかっている文字列を比較する場合に機能します。これは、コード内のインライン文字列の一般的な十分なシナリオです。たとえば、という名前の変数がconnection_stateあり、次のどの状態になっているのかを知りたい場合は['connecting', 'connected', 'disconnecting', 'disconnected']、を直接使用できます===

しかし、それだけではありません。11.9.4のすぐ上に、短いメモがあります。

NOTE 4     
  Comparison of Strings uses a simple equality test on sequences of code 
  unit values. There is no attempt to use the more complex, semantically oriented
  definitions of character or string equality and collating order defined in the 
  Unicode specification. Therefore Strings values that are canonically equal
  according to the Unicode standard could test as unequal. In effect this 
  algorithm assumes that both Strings are already in normalized form.

うーん。今何?外部から取得された文字列は奇妙なユニコードになる可能性があり、おそらくそうなるでしょう===localeCompare救出に来る:

15.5.4.9   String.prototype.localeCompare (that)
    ...
    The actual return values are implementation-defined to permit implementers 
    to encode additional information in the value, but the function is required 
    to define a total ordering on all Strings and to return 0 when comparing
    Strings that are considered canonically equivalent by the Unicode standard. 

もう帰れます。

tl; dr;

JavaScriptの文字列を比較するにはlocaleCompare、;を使用します。たとえば内部プログラム定数であるため、文字列に非ASCIIコンポーネントがないことがわかっている場合は、これ===も機能します。


0

最初の質問の操作では、次の操作を実行しています。

item1.attr - item2.attr

したがって、それらが数値であると想定します(つまり、item1.attr = "1"、item2.attr = "2")。型を確認することを条件として、 "==="演算子(または他の厳密なエバリュエーター)を引き続き使用できます。以下はうまくいくはずです:

return parseInt(item1.attr) - parseInt(item2.attr);

それらがalphaNumericの場合は、localCompare()を使用してください。


0
list.sort(function(item1, item2){
    return +(item1.attr > item2.attr) || +(item1.attr === item2.attr) - 1;
}) 

サンプルのしくみ:

+('aaa'>'bbb')||+('aaa'==='bbb')-1
+(false)||+(false)-1
0||0-1
-1

+('bbb'>'aaa')||+('bbb'==='aaa')-1
+(true)||+(false)-1
1||0-1
1

+('aaa'>'aaa')||+('aaa'==='aaa')-1
+(false)||+(true)-1
0||1-1
0

3
コードのみの回答は、それらがどのように機能するかを説明することにより、より役立つものにすることができます。
Dan Dascalescu 2018年

-2
<!doctype html>
<html>
<body>
<p id = "myString">zyxtspqnmdba</p>
<p id = "orderedString"></p>
<script>
var myString = document.getElementById("myString").innerHTML;
orderString(myString);
function orderString(str) {
    var i = 0;
    var myArray = str.split("");
    while (i < str.length){
        var j = i + 1;
        while (j < str.length) {
            if (myArray[j] < myArray[i]){
                var temp = myArray[i];
                myArray[i] = myArray[j];
                myArray[j] = temp;
            }
            j++;
        }
        i++;
    }
    var newString = myArray.join("");
    document.getElementById("orderedString").innerHTML = newString;
}
</script>
</body>
</html>

1
これがあなたの答えの質問をどのように解決するかについての情報をいくつか追加してください。コードのみの回答は歓迎されません。ありがとうございました。
wayneOS

ここでは、文字列内の文字を並べ替えますが、これは要求されたものではありません。あなたはこの"のArray.sort"などstr.split( "")を使用して、単純にソート。ソート().join( "")を達成することができます
Alejadro Xalabarder

-2
var str = ['v','a','da','c','k','l']
var b = str.join('').split('').sort().reverse().join('')
console.log(b)

このコードは問題を解決する可能性がありますが、これが問題を解決する方法と理由の説明含めると、投稿の品質が向上し、おそらくより多くの投票が得られます。あなたが今尋ねている人だけでなく、あなたが将来の読者のための質問に答えていることを忘れないでください。回答を編集して説明を追加し、適用される制限と前提を示してください。
デイブ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.