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

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

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

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

Q&A

解決済

4回答

3170閲覧

連想配列からカテゴリごとの小計を求める方法

Biginner_A

総合スコア7

JavaScript

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

0グッド

1クリップ

投稿2020/02/07 11:32

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ページで確認できます。

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

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

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

m.ts10806

2020/02/07 11:49

まずは自身が試したコードをご提示ください。 質問ではなく作業依頼になっています。
Biginner_A

2020/02/08 00:56

申し訳有りません 次回質問することがあれば自分のコードを貼るようにしていきます。ご指摘ありがとうございます
guest

回答4

0

こんにちは

2つのコード例を挙げます。1つ目は reduceMap を使ったコードで、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}))

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

補足

上記のコード1.および 2. ともに、結果として得られる配列の要素の順番は、"A"、 "B"、 "C" と、カテゴリーのアルファベット順になっているかと思いますが、元のデータの配列 items に含まれる要素の順番がどのようなものであっても、result としては、カテゴリーの文字列の昇順にソートされた結果を得たいのであれば、さらに sort を使って明示的にソートする必要があります。

以上、参考になれば幸いです。

投稿2020/02/07 18:27

編集2020/02/08 02:35
jun68ykt

総合スコア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
miyabi-sun

総合スコア21158

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

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

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

Technote

総合スコア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

yureighost

総合スコア2183

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

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

m.ts10806

2020/02/07 12:11

>連想配列のキーが 「値が」だと思います。
m.ts10806

2020/02/07 12:12

ただ、まだ、もしかしたらあそこは変数である可能性がなくはないので・・
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問