54

私は次のような配列を持っています。

["IL0 Foo", "PI0 Bar", "IL10 Baz", "IL3 Bob says hello"]

そして、次のように表示されるように並べ替える必要があります。

["IL0 Foo", "IL3 Bob says hello", "IL10 Baz", "PI0 Bar"]

ソート機能を試しました。

function compare(a,b) {
  if (a < b)
     return -1;
  if (a > b)
    return 1;
  return 0;
}

しかし、これは順序を与えます

["IL0 Foo", "IL10 Baz", "IL3 Bob says hello", "PI0 Bar"]

動作する正規表現を考えようとしましたが、頭を悩ませることはできません。
それが役立つ場合、フォーマットは常に2文字、xの数字、そして任意の数の文字になります。

4

8 に答える 8

109

これは「自然ソート」と呼ばれ、次のようにJSに実装できます。

function naturalCompare(a, b) {
    var ax = [], bx = [];

    a.replace(/(\d+)|(\D+)/g, function(_, $1, $2) { ax.push([$1 || Infinity, $2 || ""]) });
    b.replace(/(\d+)|(\D+)/g, function(_, $1, $2) { bx.push([$1 || Infinity, $2 || ""]) });
    
    while(ax.length && bx.length) {
        var an = ax.shift();
        var bn = bx.shift();
        var nn = (an[0] - bn[0]) || an[1].localeCompare(bn[1]);
        if(nn) return nn;
    }

    return ax.length - bx.length;
}

/////////////////////////

test = [
    "img12.png",
    "img10.png",
    "img2.png",
    "img1.png",
    "img101.png",
    "img101a.png",
    "abc10.jpg",
    "abc10",
    "abc2.jpg",
    "20.jpg",
    "20",
    "abc",
    "abc2",
    ""
];

test.sort(naturalCompare)
document.write("<pre>" + JSON.stringify(test,0,3));

逆の順序で並べ替えるには、引数を入れ替えるだけです。

test.sort(function(a, b) { return naturalCompare(b, a) })

または単に

test = test.sort(naturalCompare).reverse();
于 2013-03-18T14:33:08.317 に答える
19

String#localeCompareで使用できますoptions

感度

文字列のどの違いがゼロ以外の結果値につながるか。可能な値は次のとおりです。

  • "base":ベース文字が異なる文字列のみが等しくないと比較されます。例:a ≠ b、、a = áa = A
  • "accent":ベース文字またはアクセントと他の発音区別符号が異なる文字列のみが等しくないと比較されます。例:a ≠ b、、a ≠ áa = A
  • "case":ベース文字または大文字と小文字が異なる文字列のみが等しくないと比較されます。例:a ≠ b、、a = áa ≠ A
  • "variant":ベース文字、アクセント、その他の発音区別符号が異なる文字列、または大文字と小文字が等しくないものとして比較されます。他の違いも考慮に入れることができます。例:a ≠ b、、a ≠ áa ≠ A

デフォルトは、使用法「sort」の「variant」です。使用法「検索」はロケールに依存します。

数値

「1」<「2」<「10」のように、数値照合を使用するかどうか。true可能な値はとですfalse。デフォルトはですfalse。このオプションは、optionsプロパティまたはUnicode拡張キーを使用して設定できます。両方が提供されている場合、optionsプロパティが優先されます。このプロパティをサポートするために実装は必要ありません。

var array = ["IL0 Foo", "PI0 Bar", "IL10 Baz", "IL3 Bob says hello"];

array.sort(function (a,b) {
    return a.localeCompare(b, undefined, { numeric: true, sensitivity: 'base' });
});

console.log(array);

于 2017-07-24T11:03:21.677 に答える
5
var re = /([a-z]+)(\d+)(.+)/i;
var arr = ["IL0 Foo", "PI0 Bar", "IL10 Baz", "IL3 Bob says hello"];
var order = arr.sort( function(a,b){
    var ma = a.match(re),
        mb = b.match(re),
        a_str = ma[1],
        b_str = mb[1],
        a_num = parseInt(ma[2],10),
        b_num = parseInt(mb[2],10),
        a_rem = ma[3],
        b_rem = mb[3];
    return a_str > b_str ? 1 : a_str < b_str ? -1 : a_num > b_num ? 1 : a_num < b_num ? -1 : a_rem > b_rem;  
});
于 2013-03-18T14:29:48.913 に答える
3

georgのソリューションはとても気に入りましたが、数字の前に並べ替えるにはアンダースコア( "_")が必要でした。彼のコードを変更した方法は次のとおりです。

var chunkRgx = /(_+)|([0-9]+)|([^0-9_]+)/g;
function naturalCompare(a, b) {
    var ax = [], bx = [];
    
    a.replace(chunkRgx, function(_, $1, $2, $3) {
        ax.push([$1 || "0", $2 || Infinity, $3 || ""])
    });
    b.replace(chunkRgx, function(_, $1, $2, $3) {
        bx.push([$1 || "0", $2 || Infinity, $3 || ""])
    });
    
    while(ax.length && bx.length) {
        var an = ax.shift();
        var bn = bx.shift();
        var nn = an[0].localeCompare(bn[0]) || 
                 (an[1] - bn[1]) || 
                 an[2].localeCompare(bn[2]);
        if(nn) return nn;
    }
    
    return ax.length - bx.length;
}

/////////////////////////

test = [
    "img12.png",
    "img10.png",
    "img2.png",
    "img1.png",
    "img101.png",
    "img101a.png",
    "abc10.jpg",
    "abc10",
    "abc2.jpg",
    "20.jpg",
    "20",
    "abc",
    "abc2",
    "_abc",
    "_ab_c",
    "_ab__c",
    "_abc_d",
    "ab_",
    "abc_",
    "_ab_cd",
    ""
];

test.sort(naturalCompare)
document.write("<pre>" + JSON.stringify(test,0,3));

于 2015-07-24T19:15:27.183 に答える
3

先行ゼロを使用して文字列に数値を埋め込み、通常どおりに並べ替えます。

var naturalSort = function (a, b) {
    a = ('' + a).replace(/(\d+)/g, function (n) { return ('0000' + n).slice(-5) });
    b = ('' + b).replace(/(\d+)/g, function (n) { return ('0000' + n).slice(-5) });
    return a.localeCompare(b);
}

var naturalSortModern = function (a, b) {
    return ('' + a).localeCompare(('' + b), 'en', { numeric: true });
}

console.dir((["IL0 Foo", "PI0 Bar", "IL10 Baz", "IL3 Bob says hello"].sort(naturalSort)));

console.dir((["IL0 Foo", "PI0 Bar", "IL10 Baz", "IL3 Bob says hello"].sort(naturalSortModern)));

于 2017-05-08T06:12:17.173 に答える
2

次のような正規表現を実行して、文字列の非数値部分と数値部分を取得できます。

var s = "foo124bar23";
s.match(/[^\d]+|\d+/g)

戻り値:["foo", "124" , "bar" , "23"]

次に、比較関数で、2つの文字列の一部を繰り返して、それらを部分ごとに比較できます。最初の不一致部分は、全体的な比較の結果を決定します。パーツごとに、パーツが数字で始まっているかどうかを確認し、数字で始まっている場合は、比較を行う前に数値として解析します。

于 2013-03-18T14:29:56.527 に答える
1

もう1つの選択肢を追加します(なぜそうではないのか):

var ary = ["IL0 Foo", "PI0 Bar", "IL10 Hello", "IL10 Baz", "IL3 Bob says hello"];

// break out the three components in to an array
// "IL10 Bar" => ['IL', 10, 'Bar']
function getParts(i){
    i = i || '';
    var parts = i.match(/^([a-z]+)([0-9]+)(\s.*)$/i);
    if (parts){
        return [
            parts[1],
            parseInt(parts[2], 10),
            parts[3]
        ];
    }
    return []; // erroneous
}
ary.sort(function(a,b){
    // grab the parts
    var _a = getParts(a),
        _b = getParts(b);

    // trouble parsing (both fail = no shift, otherwise
    // move the troubles element to end of the array)
    if(_a.length == 0 && _b.length == 0) return 0;
    if(_a.length == 0) return -1;
    if(_b.length == 0) return 1;

    // Compare letter portion
    if (_a[0] < _b[0]) return -1;
    if (_a[0] > _b[0]) return 1;
    // letters are equal, continue...

    // compare number portion
    if (_a[1] < _b[1]) return -1;
    if (_a[1] > _b[1]) return 1;
    // numbers are equal, continue...

    // compare remaining string
    if (_a[2] < _b[2]) return -1;
    if (_a[2] > _b[2]) return 1;
    // strings are equal, continue...

    // exact match
    return 0;
});

jsfiddleの例

于 2013-03-18T14:43:00.113 に答える
0

きれいではありませんが、最初の2つの文字コードを確認してください。すべてが等しい場合は、数値を解析して比較します。

var arr = ["IL0 Foo", "IL10 Baz", "IL3 Bob says hello", "PI0 Bar"];
arr.sort(function (a1, b1) {
    var a = parseInt(a1.match(/\d+/g)[0], 10),
        b = parseInt(b1.match(/\d+/g)[0], 10),
        letterA = a1.charCodeAt(0),
        letterB = b1.charCodeAt(0),
        letterA1 = a1.charCodeAt(1),
        letterB1 = b1.charCodeAt(1);
    if (letterA > letterB) {
        return 1;
    } else if (letterB > letterA) {
        return -1;
    } else {
        if (letterA1 > letterB1) {
            return 1;
        } else if (letterB1 > letterA1) {
            return -1;
        }
        if (a < b) return -1;
        if (a > b) return 1;
        return 0;
    }
});

于 2013-03-18T14:25:05.297 に答える