javascriptで連想配列からcategoryごとの小計を計算して下記期待値のような形で返却したいのですが、知見がなく手詰まりの状態です
for文を使用しながら試行錯誤しているのですがいまだにできていません
なので実現方法などを助言いただければと思います
宜しくお願い致します
◎使用するデータ
var items = [ { category: A, price: 100 }, { category: B, price: 200 }, { category: C, price: 300 }, { category: B, price: 400 }, { category: C, price: 500 }, ]
◎期待する返却地
var result= [ { category: A, subtotal: 100 }, { category: B, subtotal: 600 }, { category: C, subtotal: 800 } ]
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2020/02/08 00:56
回答4件
0
こんにちは
2つのコード例を挙げます。1つ目は reduce と Map を使ったコードで、2つ目はlodashを使ったコードです。
1. reduce と Map を使ったコード
基本的な流れは以下です。
- カテゴリーの文字列をキーとし、そのカテゴリーごとの合計額を値とするMapを、reduce を使って作る。
- 上記で得られたMapをスプレッド構文によって配列にし、
- 最後に、map によって、配列の要素を希望する形に整形します。
javascript
1var result = [... 2 items.reduce( 3 (m, item) => m.set(item.category, (m.get(item.category) || 0) + item.price) 4 ,new Map() 5 )].map(([category, subtotal]) => ({category, subtotal}))
- 動作確認用CodePen: https://codepen.io/jun68ykt/pen/JjdoGgW
2. lodash を使ったコード
配列やオブジェクトの操作で便利なライブラリlodash の、以下のメソッドを使います。
- 配列の要素を何らかの基準によってグルーピングした結果のオブジェクトを返す: _.groupBy
- 与えられたオブジェクトの各プロパティに何らかの処理をしたオブジェクトを返す: _.mapValues
上記によって得られたオブジェクトを、Object.entries()によって配列にし、先の1.のコードと同様に、map によって配列要素を望ましい形に整形します。
javascript
1var totalize = items => items.reduce((sum, item) => sum + item.price, 0) 2 3var totals = _.mapValues(_.groupBy(items, "category"), totalize) 4 5var result = Object.entries(totals).map(([category, subtotal]) => ({category, subtotal})) 6
- 動作確認用CodePen: https://codepen.io/jun68ykt/pen/OJVPMEZ
補足
上記のコード1.および 2. ともに、結果として得られる配列の要素の順番は、"A"、 "B"、 "C" と、カテゴリーのアルファベット順になっているかと思いますが、元のデータの配列 items
に含まれる要素の順番がどのようなものであっても、result
としては、カテゴリーの文字列の昇順にソートされた結果を得たいのであれば、さらに sort を使って明示的にソートする必要があります。
以上、参考になれば幸いです。
投稿2020/02/07 18:27
編集2020/02/08 02:35総合スコア9058
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
ベストアンサー
なるほど
この最終結果を作るのは中々難しいでしょうね。
JSの中級者くらいにならないと歯が立たない難問だと思います。
理由としては一撃でやろうとすると、
配列の何処にAの情報あるんだっけ?と毎回探す必要があります。
一度オブジェクトを噛ませるのが良いやり方です。
ただし、オブジェクトは配列で気軽に回せない……
そこでキー情報を配列として受け取るObject.keysを利用します。
ES2015というJavaScriptの新しい仕様で
for...ofという強力な構文が加わったのでこれをガンガン活用していきますよ。
js
1var items = [ 2 {category: "A", price: 100}, 3 {category: "B", price: 200}, 4 {category: "C", price: 300}, 5 {category: "B", price: 400}, 6 {category: "C", price: 500}, 7]; 8 9// 中間resultは配列ではなく、オブジェクトにするのがコツ 10var tmpResult = {}; 11for (const item of items) { 12 if (!tmpResult[item.category]) tmpResult[item.category] = 0; 13 tmpResult[item.category] += item.price; 14} 15 16// 中間resultを確認 17console.log(tmpResult); 18// {A: 100, B: 600, C: 800} 19 20// 中間resultから完成の結果を作る 21var result = []; 22for (const category of Object.keys(tmpResult)) { 23 result.push({ 24 category: category, 25 price: tmpResult[category], 26 }); 27} 28 29// resultを確認 30console.log(result); 31// [ 32// {category: "A", price: 100}, 33// {category: "B", price: 600}, 34// {category: "C", price: 800}, 35// ]
【おまけ】 なんとか一撃でやっつける
reduceやfor文1個で出来ないことも無いですが……という感じですね。
いくらかは分けた方が理解しやすいと思います。
js
1var items = [ 2 {category: "A", price: 100}, 3 {category: "B", price: 200}, 4 {category: "C", price: 300}, 5 {category: "B", price: 400}, 6 {category: "C", price: 500}, 7]; 8 9var result = items.reduce((arr, {category, price}) => { 10 const target = arr.find(it => it.category === category); 11 if (target) { 12 target.price += price; 13 } else { 14 arr.push({category, price}); 15 } 16 return arr; 17}, []); 18// [ 19// {category: "A", price: 100}, 20// {category: "B", price: 600}, 21// {category: "C", price: 800}, 22// ]
投稿2020/02/07 12:02
編集2020/02/07 12:06総合スコア21203
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
IEいらないなら次のように書けます。
const items = [ { category: 'A', price: 100 }, { category: 'B', price: 200 }, { category: 'C', price: 300 }, { category: 'B', price: 400 }, { category: 'C', price: 500 }, ]; console.log(Object.entries(items.reduce((acc, item) => Object.assign(acc, {[item.category]: item.price + (acc[item.category] || 0)}),{})).map(item => ({category: item[0], price: item[1]})));
↓
[ { category: 'A', price: 100 }, { category: 'B', price: 600 }, { category: 'C', price: 800 } ]
やってることはmiyabi-sunさんの最初の説明と同様です。
投稿2020/02/07 12:33
総合スコア63
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
そもそも例の連想配列のキーが文字列指定になってない時点でエラーになるんですが。
reduceを使えば簡単にまとめられます。
javascript
1var items = [ 2 { 3 category: 'A', 4 price: 100 5 }, 6 { 7 category: 'B', 8 price: 200 9 }, 10 { 11 category: 'C', 12 price: 300 13 }, 14 { 15 category: 'B', 16 price: 400 17 }, 18 { 19 category: 'C', 20 price: 500 21 }, 22]; 23 24var group = items.reduce(function (result, current) { 25 // category項目が同一か判定する関数 26 var element = result.find(function (p) { 27 return p.category === current.category 28 }); 29 // 同一であれば 30 if (element) { 31 // priceを加算 32 element.price += current.price; // sum 33 } else { 34 // 結果配列に新規追加 35 result.push({ 36 category: current.category, 37 price: current.price 38 }); 39 } 40 return result; 41 }, []); 42 43console.log(JSON.stringify(group,null,'\t'));
投稿2020/02/07 12:01
総合スコア2183
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。