やる事
文字列と数字が混在した配列でも、それっぽいソートをしたい。
Test1
Test10
Test11
Test2
Test3
...
という「なんだかな~」っていう並びを
Test1
Test2
Test3
...
Test9
Test10
Test11
にするやつです。
他にいいやつあるかな~と思ったけど、~~この位だと作った方が早いし、~~多言語に移植する予定もあったので、考察の為
とりあえず、適当に列挙したこれが
- '25'
- 12.234.111.233
- 12.234.111.28
- 12.234.27.255
- 184
- a9-b
- a100
- a9-a
- tenuno01
- test
- test00030-1
- test00123-156-3
- test123-4
- wawa1
これになります。
- 12.234.27.255
- 12.234.111.28
- 12.234.111.233
- '25'
- 184
- a9-a
- a9-b
- a100
- tenuno01
- test
- test00030-1
- test123-4
- test00123-156-3
- wawa1
気に入らなければ話はここで終わりDA(改善点聞かせてください><)
考察
- 例えば
Test1-10
といった値は 「110」という捉え方ではなく、「1」 グループの中の 「10」 という捉え方とする - 文字列の長さ優先ソートではなく、中途半端にゼロ埋めされたデータにも対応したい(
1
と01
とか) - 従って既存のゼロ埋めは無視(
ABC-00123
とABC-05
だと05が前に来る等) - 数の接続詞(- / : . (n) 等)の重みは考慮しない(考慮するときりがない・・)
- つまり・・数字は「数字の塊」、文字は「文字の塊」と見なしてソートする
- マイナス無視(ハイフンとみなす為)
コード
プロトタイプで失礼。バグや、他に良いものがあれば教えて下さい。
追記: 更に良いものを教えていただきましたので、この例はあくまで経緯としてご覧ください。
// テスト
var array_test = ["25", "12.234.111.233", "12.234.111.28", "12.234.27.255", 184, "a9-b", "a100", "a9-a", "tenuno01", "test", "test00030-1", "test00123-156-3", "test123-4", "wawa1"];
console.log(array_test.sort(sort_num_block));
// ["12.234.27.255", "12.234.111.28", "12.234.111.233", "25", 184, "a9-a", "a9-b", "a100", "tenuno01", "test", "test00030-1", "test123-4", "test00123-156-3", "wawa1"]
/**
* 数値文字列グループ化ソート
*
* - ゼロ埋め無視(ゼロ埋めの桁数が合ってなくても無視)
* - マイナス無視(ハイフンとみなす)
*/
function sort_num_block(a, b) {
// 数字のかたまりと、文字のかたまりをグループ化
ma = String(a).match(/([0-9]+|[^0-9]+)/g);
mb = String(b).match(/([0-9]+|[^0-9]+)/g);
if (!ma && !mb) return 0;
if (!ma && mb) return -1;
if (!mb && ma) return 1;
for (var i = 0; i < ma.length; i++) {
if (!mb[i]) return 1;
if (!isNaN(ma[i]) && !isNaN(mb[i])) {
// 両方数値化できれば数値で比較
if (parseInt(ma[i]) != parseInt(mb[i])) {
return parseInt(ma[i]) - parseInt(mb[i]);
}
} else {
// それ以外は文字列で比較
if (ma[i] > mb[i]) {
return 1;
} else if (ma[i] < mb[i]) {
return -1;
}
}
}
if (ma.length < b.length) return -1;
return 0;
}
追記:更に良いコード
@vf8974 さんありがとうございます!
私の書いた先程の記載だと以下の問題が生じます。
- どうしても数値比較なので、桁数があふれるとオーバーフローを起こす。
sort_num_block
の関数を以下の様に書き換えることにより改善できます。
function sort_num_block(a, b) {
const sa = String(a).replace(/(\d+)/g, m => m.padStart(30, '0'));
const sb = String(b).replace(/(\d+)/g, m => m.padStart(30, '0'));
return sa < sb ? -1 : sa > sb ? 1 : 0;
}
数字の個所を想定範囲以上にゼロ埋めして文字列としてソートする
分かりやすい例として(コード上は30桁を想定していますが、例として10桁埋めとします。)
Test12-3-456
Test1-234-56
を
Test0000000012-0000000003-0000000456
Test0000000001-0000000234-0000000056
とすると、文字列でソートしても問題なくソートできるという仕組みです。