JavaScript(node.js)で数字込みのファイル名をsortする

sort関連のサンプルコードです。覚書です。

目次

JavaScript(node.js)で数字込みのファイル名をsortする

const sorted = filenames.sort((a, b) => {
  const a1 = parseInt(a.replace(/[^0-9]/g, ''), 10);
  const b1 = parseInt(b.replace(/[^0-9]/g, ''), 10);

  const a2 = isNaN(a1) ? 0 : a1;
  const b2 = isNaN(b1) ? 0 : b1;

  if (a2 !== b2) {
    return a2 - b2;
  }

  return a.localeCompare(b);
});

Node.jsでファイル名に数字が含まれる場合のソートを行う際には、Array.prototype.sort()メソッドを使用しますが、デフォルトのソート機能では文字列として扱われるため、数値の大小関係に基づいたソートが行われません。そのため、数値を含むファイル名を正しくソートするには、比較関数をカスタマイズする必要があります。

数値としてソートすると、以下の順番になります。

  1. a-001.md (1)
  2. a-02.md (2)
  3. 3-a.md (3)
  4. a-11.md (11)
  5. 3-1.md (31)
  6. 7-1.md (71)
  7. 3-1-1.md (311)
  8. apple.md (数字なし、文字列比較)
  9. banana.md (数字なし、文字列比較)
  1. ファイル名の配列をソートするjavascriptconst sorted = filenames.sort((a, b) => { ここでfilenamesはソートしたいファイル名の配列です。sortメソッドは配列の要素をソートするために使用され、このメソッドには比較関数が渡されます。比較関数は2つの引数abを取り、これらを比較してどちらが先に来るべきかを決定します。
  2. ファイル名から数字のみを抽出して数値に変換するjavascriptconst a1 = parseInt(a.replace(/[^0-9]/g, ''), 10); const b1 = parseInt(b.replace(/[^0-9]/g, ''), 10); ここでは、各ファイル名から数字以外の文字を全て削除(replace(/[^0-9]/g, '')で実現)し、残った文字列を10進数の整数に変換します(parseInt関数を使用)。/[^0-9]/gは正規表現で、数字以外のすべての文字にマッチし、それらを空文字に置換しています。
  3. 数値変換後のNaNを0に置き換えるjavascriptconst a2 = isNaN(a1) ? 0 : a1; const b2 = isNaN(b1) ? 0 : b1; parseIntが数字以外の文字列を変換しようとした場合、結果はNaN(Not a Number)になります。isNaN関数を使ってこれをチェックし、NaNであれば0に置き換えます。これにより、数字が含まれていないファイル名は数値として0として扱われます。
  4. 数値でソートするjavascriptif (a2 !== b2) { return a2 - b2; } 抽出した数値が異なる場合は、その差(a2 - b2)によってソート順を決定します。この結果が負の値ならaが先、正の値ならbが先に来ます。
  5. 数値が同じ場合は文字列として比較するjavascriptreturn a.localeCompare(b); 数値が同じか、両方とも数字が含まれていない場合は、localeCompareメソッドを使用して文字列として自然な順序でソートします。これにより、例えばアルファベット順にソートされます。

node.jsの非同期処理で順番が変わる

sortとは別に非同期処理を行うと順番が入れ替わってしまう可能性があるっぽい。

その場合は、再帰関数processFilesを導入することで、非同期処理を同期的に行います。

Object.fromEntries(Object.entries(obj).sort())) 追加

ランキングはスコアだけではないでしょうから、考え方としてはオブジェクトのソートでしょう。

objects.keyをソートする場合、この方法だと一行でいけますね。

Object.fromEntries(Object.entries({type2: 10, type3:1, type1:5}).sort())

Object.entriesでオブジェクトを配列化し、ソートした上でまたオブジェクトに変換する感じ。

配列のソート(ランキング化)

配列をソートしようしたら次のエラーがでました。

sort is not a function

配列に統一感がなかったため、配列を統一してランキング化しました。

Obj {"type1":11,"type2":9,"type3":10}

scoreのobjでランキング化したい場合。

const array = Object.keys(obj).map((key) => {
  return { score: obj[key] }
})
array.sort((a, b) => {
  // return a.score - b.score // 昇順
  return b.score - a.score // 降順
})
console.log('ソート済', JSON.stringify(array))


純粋な配列でランキング化したい場合。

const array = Object.keys(obj).map((key) => {
  return obj[key]
})
array.sort((a, b) => {
  return b - a // 降順
})
console.log('ソート済', JSON.stringify(array))

JavaScriptは配列のソートする際、同値の場合はブラウザにより挙動が違うようなので、その対策も考慮する必要があります。return 0;する(読んだだけで詳しくは未検証)

objのソート(別バージョン)

このobjをそのまま使いたい場合は次のようになります。

Obj {"type1":11,"type2":9,"type3":10}
const entries = Object.entries(obj)
const sorted = entries.sort((a, b) => b[1] - a[1]) // 降順

sortedObj = Object.fromEntries(sorted)
keys = Object.keys(sortedObj)
values = Object.values(sortedObj)

nodeのバージョンに制限がある場合、Object.fromEntriesは使えません。他の命令で代替が必要になったケースがありました。

const entries = Object.entries(obj)
const sorted = entries.sort((a, b) => b[1] - a[1]) // 降順

const sortedObj = Object.assign(
  ...sorted.map(([key, value]) => ({ [key]: value }))
)

objectをn回文繰り返す

ソートしたオブジェクトからトップ3だけ抽出する場合などに便利です。

      Array.from({ length: 3 }, (_, i) => {
        console.log('3回数ぶん、繰り返す', _, i)
        const best3 = Object.keys(obj)[i]

        console.log('best3', best3)
      })


length: 3で3回分繰り返します。
_はすべてundefined。
iは0, 1, 2です。

{ length: 3 }だけの指定や_を省くと、すべてundefinedになります。

Rangeを使うやり方など、いろいろなやり方があります。

https://stackoverflow.com/questions/3746725/how-to-create-an-array-containing-1-n

https://stackoverflow.com/questions/10993824/do-something-n

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

コメント

コメントする

目次