🐡

一日一処: JavaScriptで変換やフィルタリングを容易に

2024/01/27に公開

変換やフィルタリングの必要性

JavaScriptだけではないが、多くのプログラムでは、外部のデータを読み込むことがある。JavaScriptでもCSVやJSONを用いる事がある。特にこれらのデータは、文字列として読み込まれる事が多いため、変換やフィルタリングを必要とする場合がある。

配列のデータを変換してみる

読み込みの過程については、今回のテーマではないため、割愛するとして、まずは、文字列として読み込まれた情報をJavaScriptのプリミティブ型として、変換するための様子を見よう。

const data = ['1', '2', '3']
const numbers = data.map(Number)

mapを用いれば、簡単だ。同様に、真偽値の変換できる…

const data = ['true', 'false', 'true']
const booleans = data.map(Boolean)
// -> [true, true, true]

変換できる様に見えるが、一文字以上の文字列の場合は、真と判断されるため、正確に変換ができない。ただし、元データがこのような状態だと、可能だ。

const data = [1, 0, 1]
const booleans = data.map(Boolean)
// -> [true, false, true]

元データが結果的なデータと一致しないため、少々困ってしまうが、変換するために、元データを調整することも考えよう。文字列のみの場合、変換を重ねて、このようにもできるだろう。

const data = ['1', '0', '1']
const booleans = data.map(Number).map(Boolean)
// -> [true, false, true]

そして、フィルタリングもこの流れに合わせて、メソッドチェーンを重ねれば、結果が得られるだろう。

const data = ['a', '1', 'c']
const booleans = data.map(Number).filter(Boolean)
// -> [1]

日付も同様に扱えそうに思えるが、若干調整が必要だ。

const data = ['2020/1/1', '2024/1/1']
const dates = data.map((s) => new Date(s))
console.log(dates)
// -> [1]

非常に基礎的だが、咄嗟にこのようなコードが思いつかないエンジニアも多くいるため、知らなかった人は覚えてほしい。

オブジェクトのデータを変換してみる

オブジェクトの例も書いておこう。

const data = { age: '20', score: '99' }
const reducer = (o, k) => ({ ...o, [k]: Number(data[k]) })
const converted = Object.keys(data).reduce(reducer, {})
// -> { age: 20, score: 99 }

このようにすると、キーに合わせて変換することもできる。

const data = { age: '20', active: 0 }
const rule = { age: Number, active: Boolean }
const reducer = (o, k) => ({ ...o, [k]: rule[k](data[k]) })
const converted = Object.keys(data).reduce(reducer, {})
// -> { age: 20, active: false }

ruleを中心に処理を行う使い方を考えれば、このような記述でもいいだろう。

const data = { age: '20', active: 0 }
const rule = { age: Number, active: Boolean }
const reducer = (o, [k, f]) => ({ ...o, [k]: f(data[k])})
const converted = Object.entries(rule).reduce(reducer, {})
// -> { age: 20, active: false }

配列の場合と同様に、すべてが文字列だった場合、真偽値はこのように処理を重ねる。

const data = { age: '20', active: '0' }
const rules = [
  { age: Number, active: Number },
  { age: i => i, active: Boolean },
]
const reducer = (o, [k, f]) => ({ ...o, [k]: f(o[k]) })
const converted = rules.reduce(
  (o, r) => Object.fromEntries(Object.entries(r).map(([k, f]) => [k, f(o[k])])),
  data
)
// -> { age: 20, active: false }

これまでのruleを配列にしrulesという変数名に変更した後、rulesに対して、reduceを実行して、rules配列の0番目から順に処理を実行する。明確にルールが設定できるので、わかりやすいかもしれないが、後の処理が少々複雑に見えるかもしれない。
ただ、Object.keysを用いなくてもentriesfromEntriesメソッドのようにオブジェクトを変換できる術を知っておくと便利だろう。

Discussion