LoginSignup
0

More than 3 years have passed since last update.

Array<Object> な値を reduceしてObjectにする

Last updated at Posted at 2019-07-09

はじめに

例えば、以下のようなSQLでDBからレコードを取得してきた場合


SELECT
  a,
  SUM(CASE WHEN case = 'b'  THEN 1 ELSE 0 END) AS b,
  SUM(CASE WHEN case = 'c'  THEN 1 ELSE 0 END) AS c
FROM table
GROUP BY a
;

戻り値はこんな感じで取得できたりする。


src = [{ a: 1, b: 2, c: 3 }, { a: 10, b: 20, c: 30 }]

group by a ごとの集計(SQL結果そのまま)と
group by しない合計を出したい場合を考えた時に、
単純にSQLを2発投げることもできるが、JS側で足し算すればいいじゃん?とも思った。
(ただし、足し算するレコードが多い場合は、SQLでやった方が早い気もする。
 今回は100〜200レコードしか取得されない前提。)

JavaScriptのArrayには強力なメソッドがいくつもあり、
その中の一つ、reduceがかなりいい仕事をしてくれるのだが

[1,2,3].reduce((p,c)=>{
  return p + c 
})
// 6

Object同士の足し算だとポンコツな結果が返ってくる(いや、ポンコツなのは仕様を理解できていない自分なのだと思うが)

[{a:1},{a:2},{a:3}].reduce((p,c)=>{
  return p + c 
})
// {a:6} を期待するが、実際はこれ
// [object Object][object Object][object Object]

どうやら、Objectの場合は、ちょっと工夫が必要らしい

[{a:1},{a:2},{a:3}].reduce((p,c)=>{
  return {a:p.a + c.a}
})
// {a:6}

結論

※追記 コメントでこのような書き方もあると教えていただきました!
だいぶスマートですね!


src = [{ a: 1, b: 2, c: 3 }, { a: 10, b: 20, c: 30 }]
keys = ["a","b","c"]
keys.map(k=>(
    {[k]:  src.map(e=>e[k]).reduce((p,c)=>(p+c))}
)).reduce((p,c)=>({...p,...c}))

で、完成したのがこれ。

/**
 * Array[Object]型の全要素を足し上げし、
 * Object型で返す。
 * @param {Array<Object>} src
 * @return Object
 */
reduceObjectArray(src) { // src = [{ a: 1, b: 2, c: 3 }, { a: 10, b: 20, c: 30 }]
  let result = {}
  Object.keys(src[0]).forEach((name) => {
    result[name] = src.reduce((p, c) => {
      return p + c[name]
    }, 0)
  })
  return result // result = { a: 11, b: 22, c: 33 }
};

関数名があれだが

解説

Object.keys(src[0]).forEach((name) => {

で、要素名(今回はaとbとc)分繰り返し、

    result[name] = src.reduce((p, c) => {
      return p + c[name]
    }, 0)

で、resultという連想配列(let result = {}で定義)に
1要素ずつreduceした結果を入れていく。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0