質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

解決済

2回答

9969閲覧

map,reduce,filterで集計処理を簡潔に書きたい

stakezaki

総合スコア46

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

0グッド

2クリップ

投稿2016/12/27 02:54

###前提・実現したいこと
JavaScriptで以下のようなmap,reduce,filterを使って集計するコードを書いています。
name単位でvalueを合算するものです。
もっと簡潔にエレガントに書けないものでしょうか?

自分ならこう書くといった例を示していただけると幸いです。

###ソースコード

JavaScript

1var src = [{'name':'abc','value':100},{'name':'def','value':50},{'name':'abc','value':123},{'name':'hij','value':789},{'name':'def','value':001}]; 2 3var sorted = src.sort(function(a, b) { 4 if (a.name > b.name){ 5 return 1; 6 }else{ 7 return -1; 8 } 9}); 10 11var result = sorted.map(function(entry){ return entry.name; }) // 同じnameの重複をなくす 12 .filter(function (x, i, self) { 13 return self.indexOf(x) === i; 14 }) 15 .map(function(name) { // 重複を排除した配列で繰り返し 16 return sorted.reduce(function(prev,current) { // ソートした配列(重複を含む)を元に集計 17 if (current.name===name){ 18 if (prev.name===current.name) { // 前回のものと今回のものが同じnameであれば合算 19 var entry = {}; // 元の配列を壊さないように新しくentryを生成する 20 entry.name = name; 21 entry.value = current.value + prev.value; 22 return entry; 23 } 24 return current; 25 26 } 27 return prev; 28 },{}) 29 }); 30 31 32console.log(result); 33 34[Object { name="abc", value=223}, Object { name="def", value=51}, Object { name="hij", value=789}]

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

think49

2016/12/27 14:14

name は String 型である事が確定しているのでしょうか。Object 型が来るケースは想定されますか。
stakezaki

2016/12/27 14:34

String型確定です
think49

2016/12/27 14:47

であれば、オブジェクト初期化子を利用できますね。回答にてコードを書きました。
guest

回答2

0

js

1var src = [{'name':'def','value':50},{'name':'abc','value':100},{'name':'abc','value':123},{'name':'hij','value':789},{'name':'def','value':001}]; 2var sum = {}; 3src.forEach(o => sum[o.name] = (sum[o.name] || 0) + o.value); 4var res = Object.keys(sum).sort((a, b) => a > b ? 1 : -1).map(name => ({name, value: sum[name]})); 5console.log(res);

こんな感じとか。

投稿2016/12/27 14:32

turbgraphics200

総合スコア4267

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

stakezaki

2016/12/27 14:44

エレガントですね〜
guest

0

ベストアンサー

Array.prototype.reduce 単体

要件を達成する方法としてはおそらく最速。
(Array.prototype.reduce を使わなければもっと速いですが、reduce を使う条件なのでそこは置いておきます。)

JavaScript

1'use strict'; 2var src = [{'name':'abc','value':100},{'name':'def','value':50},{'name':'abc','value':123},{'name':'hij','value':789},{'name':'def','value':1}]; 3 4var results = src.reduce(function (results, current) { 5 for (var i = 0, l = results.length, name = current.name, object; i < l; ++i) { 6 object = results[i]; 7 8 if (object.name === name) { 9 object.value += current.value; 10 return results; 11 } 12 } 13 14 results.push(current); 15 16 return results; 17}, []); 18 19console.log(JSON.stringify(results)); // [{"name":"abc","value":223},{"name":"def","value":51},{"name":"hij","value":789}]

オブジェクト初期化子

このコードは nameString 型であることが確定している必要があります。

JavaScript

1'use strict'; 2var src = [{'name':'abc','value':100},{'name':'def','value':50},{'name':'abc','value':123},{'name':'hij','value':789},{'name':'def','value':1}]; 3 4var object = src.reduce(function (object, current) { 5 var name = current.name; 6 7 if (object.hasOwnProperty(name)) { 8 object[name] += current.value; 9 } else { 10 object[name] = current.value; 11 } 12 13 return object; 14}, {}); 15 16var results = Object.keys(object).map(function (key) { 17 return {name: key, value: object[key]}; 18}); 19 20console.log(JSON.stringify(results)); // [{"name":"abc","value":223},{"name":"def","value":51},{"name":"hij","value":789}]

オブジェクト初期化子 + Object.entries (ES8)

先行仕様(ES8/ES2017)ですが、Object.entries を利用するとオブジェクト初期化子のコードを更にエレガントに書けます。
Object.entries は Polyfill コードがあるので、現行でも使おうと思えば使えます。

JavaScript

1'use strict'; 2var src = [{'name':'abc','value':100},{'name':'def','value':50},{'name':'abc','value':123},{'name':'hij','value':789},{'name':'def','value':1}]; 3 4var results = Object.entries(src.reduce((object, current) => { 5 var name = current.name; 6 7 if (object.hasOwnProperty(name)) { 8 object[name] += current.value; 9 } else { 10 object[name] = current.value; 11 } 12 13 return object; 14}, {})).map(entry=>{ 15 return {name: entry[0], value: entry[1]}; 16}); 17 18console.log(JSON.stringify(results)); // [[{"name":"abc","value":223},{"name":"def","value":51},{"name":"hij","value":789}]

new Map (ES6)

オブジェクト初期化子で「nameString 型でなければならない」欠点を new Map で克服しており、可搬性が最も高いと思われます。
構造を変えていいのなら、Map.prototype.entries の出力値で十分かもしれません。

JavaScript

1'use strict'; 2var src = [{'name':'abc','value':100},{'name':'def','value':50},{'name':'abc','value':123},{'name':'hij','value':789},{'name':'def','value':1}], 3 map = new Map, 4 results = []; 5 6src.reduce(function (map, current) { 7 var name = current.name; 8 9 map.set(name, map.has(name) ? map.get(name) + current.value : current.value); 10 11 return map; 12}, map).forEach(function (value, key, map) { 13 this.push({name: key, value: value}); 14}, results); 15 16console.log(JSON.stringify(results)); // [[{"name":"abc","value":223},{"name":"def","value":51},{"name":"hij","value":789}] 17console.log(JSON.stringify([...map.entries()])); // [["abc",223],["def",51],["hij",789]]

更新履歴

  • 2016/12/28 00:08 new Mapコードを Object.keys -> Map#forEach
  • 2016/12/28 00:32 new Mapコードを三項演算子を使ったコードに修正
  • 2016/12/28 01:38 オブジェクト初期化子 + Object.entriesのコード追記

Re: stakezaki さん

投稿2016/12/27 14:45

編集2016/12/27 16:38
think49

総合スコア18162

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

stakezaki

2016/12/27 15:00

オブジェクト初期化子の例が私が一番イメージしていたものに近いです。 また、ES6の例も示してくださり、大変勉強になります。 reduce縛りのような質問になっていましたが、実は短くて高速でエレガントであれば何でもよかったのです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問