Object.keys と Object.values で集計する

生田 陸人
LuaGate エンジニア / 現役エンジニア
編集 LuaGate編集部

Object.keys と Object.values で集計する とは

Object.keys / values / entries でオブジェクトを配列化し、reduce で合計を求める頻出パターンを学ぶ。

Object.keys と Object.values で集計する

オブジェクトをループするとき、for...in でキーを回す書き方もありますが、現代の主流は Object.keys / values / entries で配列に変換してから mapreduce を使う書き方です。API で受け取った集計データから合計を出す、フォームの状態を配列化して検証する、といったシーンで毎日のように使う技です。

オブジェクトをループするときは 配列化してから高階関数 に流すのが最も読みやすい。

3 つの基本メソッド

オブジェクトを配列化する static メソッドは次の 3 つです。

  • Object.keys(obj) はキーの配列を返します
  • Object.values(obj) は値の配列を返します
  • Object.entries(obj)[key, value] のペア配列を返します

JavaScript

const scores = { math: 80, english: 90, science: 70 }; console.log(Object.keys(scores)); // ["math", "english", "science"] console.log(Object.values(scores)); // [80, 90, 70] console.log(Object.entries(scores)); // [["math", 80], ["english", 90], ["science", 70]]

配列化されればこっちのもの。mapfilterreduceforEach がそのまま使えます。

values + reduce で合計

オブジェクトの数値合計を求めるのは、Object.valuesreduce の黄金コンビです。

JavaScript

const scores = { math: 80, english: 90, science: 70 }; const total = Object.values(scores).reduce((acc, v) => acc + v, 0); console.log(total); // 240

初期値 0 を忘れないように。空オブジェクトのときも 0 を返してくれます。

JavaScript

const empty = {}; const total = Object.values(empty).reduce((acc, v) => acc + v, 0); console.log(total); // 0

reduce の初期値を省略すると、空配列で TypeError になる。集計系では必ず初期値を書く。

entries で key と value の両方を扱う

キーと値の両方が欲しいときは Object.entries + 分割代入が最強です。

JavaScript

for (const [key, value] of Object.entries(scores)) { console.log(`${key}: ${value}`); }

配列メソッドにも自然に渡せます。

JavaScript

const report = Object.entries(scores) .map(([k, v]) => `${k}${v}`) .join("、"); console.log(report); // "math は 80 点、english は 90 点、science は 70 点"

動きを追ってみる

diagram (will load when visible)

図のとおり、オブジェクトをまず配列化し、その後に配列メソッドで処理するという流れが基本のフローです。

Object.fromEntries で逆変換

entries の逆操作が Object.fromEntries です。[key, value] ペアの配列をオブジェクトに戻します。これを使うと「キー単位で変換 → 再度オブジェクトに」というパイプラインが書けます。

JavaScript

const scores = { math: 80, english: 90 }; const doubled = Object.fromEntries( Object.entries(scores).map(([k, v]) => [k, v * 2]), ); console.log(doubled); // { math: 160, english: 180 }

entries → map → fromEntriesオブジェクト版の map として頻出する。覚えておくとコードが一気に短くなる。

よくある間違い

第 1 に、Object.valuesreduce する際に初期値を忘れると、空オブジェクトで例外を出します。reduce((a, b) => a + b) ではなく reduce((a, b) => a + b, 0) と書きます。第 2 に、for...inプロトタイプチェーンも舐めるので、安全のために Object.hasOwn(obj, key) を使うか、最初から Object.keys で配列化する方が安全です。第 3 に、Object.keys の戻り順は 挿入順 が保証されますが、数値文字列のキーは小さい順に並ぶという仕様もあります。文字列キーだけ使うなら気にしなくて OK です。

やってみよう

数値プロパティだけを持つオブジェクト obj を受け取り、すべての値の合計を返す sumValues 関数を書きましょう。return Object.values(obj).reduce((a, b) => a + b, 0) の 1 行で完成です。空オブジェクトでも 0 を返すか、テストで確認してみてください。応用として Object.entries を使い、特定のキーだけ合計するバリエーションも面白いです。

よくある質問

Q. ブラウザと Node.js で動作は変わりますか?

A. JavaScript の構文・組み込み機能(配列・文字列・Promise 等)はどちらでも同じです。違いは DOM API(ブラウザ専用)と fs などのファイル系(Node 専用)など、実行環境固有の機能です。コアロジックは両方で動くため、テストもしやすくなります。

Q. ES6 以降の新機能は使っても大丈夫ですか?

A. 現代のブラウザ・Node.js は ES2020 以降の機能をほぼサポートしています。古い IE 等を対象にする必要が無ければ、let/const、アロー関数、分割代入、async/await を積極的に使ってください。トランスパイル(Babel/Vite)が必要なケースは年々減っています。

Q. TypeScript に進む価値はありますか?

A. 型情報があるとリファクタやエディタ補完が劇的に効くため、規模が大きくなるなら TypeScript への移行を強くおすすめします。最初は any を許容しつつ徐々に型を付ければ、JS の知識をそのまま活かせます。

次のレッスン

次は reduce でキー別にグルーピングする で、Object を学びます。

事前確認 — 進む前に次の 3 つができることを確認しましょう。

  1. keys と values の要点を自分の言葉で説明できる
  2. このレッスンの最小コード (または操作手順) を見ずに書ける
  3. 練習問題やクイズで間違えた箇所を読み直して理解した

理解度チェック (30 秒)

Q. keys と values とは何か、1 文で説明してください。

この章のポイント

A. 本文の「このレッスンで分かること」または冒頭の説明文を見直し、自分の言葉で要約できれば OK。詰まったら本レッスンの最初の H2 セクションを読み返してみましょう。

関連レッスン

要件

  1. Object.values で値の配列を取得すること
  2. Array.prototype.reduce で合計すること
  3. 初期値 0 を必ず渡すこと

入出力例

test-cases.txt

sumValues({"english":90,"math":80,"science":70})240 sumValues({"only":100})100 sumValues({})0 sumValues({"a":10,"b":0,"c":-5})5

ヒント

main.js
main.js
学習モード

メモ

Object.keys と Object.values で集計する

⌘S で保存