Ramda で入れ子のオブジェクトをフラットにする
Ramda を使って入れ子になったオブジェクトをフラットにする処理を考えてみました。
ソースは http://github.com/fits/try_samples/tree/master/blog/20200216/
サンプル1
まずは、以下のように入れ子になったオブジェクトを使って
{ item: { name: 'item-1', details: { color: 'white', size: 'L' } }, num: 5 }
下記のように変換してみます。
{ name: 'item-1', color: 'white', size: 'L', num: 5 }
方法はいろいろありそうですが、ここではオブジェクトの値部分を単一要素のオブジェクト {<項目名>: <値>}
へ変換してから R.mergeAll
でマージするようにしてみました。
sample1.js
const R = require('ramda') const data = { item: { name: 'item-1', details: { color: 'white', size: 'L' } }, num: 5 } // Object の判定 const isObject = R.pipe(R.type, R.equals('Object')) // 値部分を単一要素のオブジェクトへ変換 const valuesToObjOf = R.pipe( R.mapObjIndexed( R.ifElse( isObject, v => valuesToObjOf(v), R.flip(R.objOf) ) ), R.values, R.flatten ) const flatObj = R.pipe( valuesToObjOf, R.mergeAll ) console.log( valuesToObjOf(data) ) console.log('------') console.log( flatObj(data) )
値部分がオブジェクトの場合は再帰的に処理するようにしていますが、その判定に R.is(Object)
を使うと Array
等も true
になってしまい都合が悪いので、R.type
の結果が Object
か否かで判定するようにしています。
実行結果
> node sample1.js [ { name: 'item-1' }, { color: 'white' }, { size: 'L' }, { num: 5 } ] ------ { name: 'item-1', color: 'white', size: 'L', num: 5 }
サンプル2
上記の処理だと項目名が重複した際に不都合が生じるため、項目名を連結するように valuesToObjOf
を改良してみました。
sample2.js
・・・ const valuesToObjOf = (obj, prefix = '') => R.pipe( R.mapObjIndexed( R.ifElse( isObject, (v, k) => valuesToObjOf(v, `${prefix}${k}_`), (v, k) => R.objOf(`${prefix}${k}`, v) ) ), R.values, R.flatten )(obj) const flatObj = R.pipe( valuesToObjOf, R.mergeAll ) console.log( flatObj(data) )
実行結果
> node sample2.js { item_name: 'item-1', item_details_color: 'white', item_details_size: 'L', num: 5 }