JavaScript: 数列を圧縮する
単調増加する整数列を圧縮した文字列表現に変換したい。
つまり、[1, 2, 3, 5, 7, 8, 9] を "1-3,5,7-9" に変換したい。
JavaScript で書いてみたけど存外に長くなってしまった。
const { parseArgs } = require('node:util')
const numCompress = (ary) => {
diff = diffSeq(ary)
const result = []
let buf = [ary.shift(), null]
for (let i=0; i<diff.length; i++) {
if (diff[i] == 1) {
buf[1] = ary[i]
} else {
result.push(compress(buf))
buf = [ary[i], null]
}
}
result.push(compress(buf))
return result.join(',')
}
const slices = (ary, n) => {
const l = ary.length - n
const result = []
for (let i=0; i<=l; i++) {
result.push(ary.slice(i, i+n))
}
return result
}
const diffSeq = (ary) => slices(ary, 2).map(([a, b]) => b - a)
const compress = (buf) => {
if (buf[1] == null) {
return String(buf[0])
} else {
return buf.join('-')
}
}
// main routine
const { positionals } = parseArgs({
allowPositionals: true,
})
ary = positionals.map(Number)
result = numCompress(ary)
console.log(result)
考え方としては、先に階差数列を作っておき、それを参照しながらバッファを操作する。
バッファは2つの値(初めと終わりに相当)を保持でき、階差が 1 なら終わりの値を更新、1 より大きいなら圧縮した文字列を result 変数に追加してバッファを初期化する。最後に result を ',' でつないで完了だ。
ちゃんと期待通りに動作する。
$ node numCompress.js 1 2 3 5 7 8 9
1-3,5,7-9
だけど、階差数列を作るのってまどろっこしくないか?
そんなことしなくても for ループの繰り返しの中でやればいいんじゃないか?
と思い直して書いたのがこれ。
const { parseArgs } = require('node:util')
const numCompress = (ary) => {
const result = []
let buf = [ary[0], null]
for (let i=1; i<ary.length; i++) {
if (buf[1] === null) {
if (ary[i] - buf[0] == 1) {
buf[1] = ary[i]
} else {
result.push(compress(buf))
buf = [ary[i], null]
}
} else if (ary[i] - buf[1] == 1) {
buf[1] = ary[i]
} else {
result.push(compress(buf))
buf = [ary[i], null]
}
}
result.push(compress(buf))
return result.join(',')
}
const compress = (buf) => {
if (buf[1] == null) {
return String(buf[0])
} else {
return buf.join('-')
}
}
// main routine
const { positionals } = parseArgs({
allowPositionals: true,
})
ary = positionals.map(Number)
result = numCompress(ary)
console.log(result)
これも期待通りに動作する。
$ node numCompress2.js 1 2 3 5 7 8 9
1-3,5,7-9
が、短くはなったけど、条件分岐が複雑になってわかりにくくなった。一つ目のほうがよさそう。
