オブジェクトに入っているデータを「全部まとめて処理したい」と思ったとき、配列のように for や map を直接使えず困ったことはないでしょうか。そんなときに役立つのが Object.keys・Object.values・Object.entries です。これらを使うと、オブジェクトのキー・値・キーと値のペアを配列として取り出せるため、配列のメソッドや for...of をそのまま活用できるようになります。この記事では3つのメソッドの違いと基本の使い方、反復処理や分割代入との組み合わせ、そしてつまずきやすい点までを初心者にも分かるように解説します。
目次
なぜオブジェクトを配列に変換するのか
JavaScript のオブジェクトは、キー(プロパティ名)と値をひとまとめにして持つ便利な入れ物です。ただし、配列のように map や filter、forEach といった反復メソッドを直接持っていません。そのため「全プロパティを順番に処理する」「値だけを集計する」といった操作をしようとすると、一手間かかります。
そこで使うのが Object.keys・Object.values・Object.entries です。これらはオブジェクトの中身を配列に変換してくれるので、変換したあとは配列のメソッドを自由に使えます。オブジェクトと配列の橋渡しをしてくれる存在だと考えると分かりやすいでしょう。
3つのメソッドの基本
まずは同じオブジェクトに対して3つのメソッドを使い、どんな結果が返るかを見てみます。引数には対象のオブジェクトを1つ渡すだけです。
const user = {
name: "Taro",
age: 28,
city: "Tokyo",
};
// キーだけを配列で取り出す
console.log(Object.keys(user));
// => ["name", "age", "city"]
// 値だけを配列で取り出す
console.log(Object.values(user));
// => ["Taro", 28, "Tokyo"]
// [キー, 値] のペアの配列で取り出す
console.log(Object.entries(user));
// => [["name", "Taro"], ["age", 28], ["city", "Tokyo"]]
名前のとおり、keys はキー、values は値を取り出します。entries は少し特殊で、[キー, 値] という2要素の配列を、プロパティの数だけ並べた「配列の配列」を返します。それぞれの戻り値を表で整理しておきます。
| メソッド | 戻り値 | 例(上記の user の場合) |
|---|---|---|
Object.keys(obj) | キーを並べた配列 | ["name", "age", "city"] |
Object.values(obj) | 値を並べた配列 | ["Taro", 28, "Tokyo"] |
Object.entries(obj) | [キー, 値] の配列を並べた配列 | [["name", "Taro"], ["age", 28], ...] |
いずれも元のオブジェクトは変更せず、新しい配列を返すだけです。取り出した配列をいじっても、元の user には影響しません。
for…of でキーと値を反復する
オブジェクトの全プロパティを順番に処理したいときは、Object.entries と for...of の組み合わせが定番です。さらに for...of の変数部分で分割代入を使うと、キーと値を別々の変数で受け取れて読みやすくなります。
const prices = {
apple: 120,
banana: 80,
orange: 150,
};
// [key, value] を分割代入で受け取る
for (const [name, price] of Object.entries(prices)) {
console.log(`${name} は ${price} 円です`);
}
// => apple は 120 円です
// => banana は 80 円です
// => orange は 150 円です
キーだけ、あるいは値だけを順に処理したい場合は、Object.keys や Object.values を forEach に渡すのが簡潔です。たとえば値の合計を求めるなら、次のように書けます。
let total = 0;
// 値の配列を forEach で順に足していく
Object.values(prices).forEach((price) => {
total += price;
});
console.log(total); // => 350
// reduce でも同じことができる
const sum = Object.values(prices).reduce((acc, price) => acc + price, 0);
console.log(sum); // => 350
このように、いったん配列にしてしまえば forEach や reduce といった配列のメソッドがそのまま使えるのが大きな利点です。
map で加工し fromEntries で戻す
Object.entries で配列に変換し、map で各ペアを加工したあと、Object.fromEntries でオブジェクトに戻す、という流れもよく使います。Object.fromEntries は entries と逆の働きをするメソッドで、[キー, 値] の配列からオブジェクトを組み立てます。
const prices = {
apple: 120,
banana: 80,
orange: 150,
};
// すべての価格を 1.1 倍(税込)にした新しいオブジェクトを作る
const withTax = Object.fromEntries(
Object.entries(prices).map(([name, price]) => [name, Math.round(price * 1.1)])
);
console.log(withTax);
// => { apple: 132, banana: 88, orange: 165 }
entries で配列にして、map で値を加工し、fromEntries でオブジェクトに戻す。この「往復」のパターンを覚えておくと、オブジェクトの値をまとめて書き換える処理を簡潔に書けます。条件で絞り込みたいときは map の代わりに filter を挟めば、特定のプロパティだけを残したオブジェクトも作れます。
プロパティの数を数える
オブジェクトには配列の length のような「要素数」を直接返すプロパティがありません。そこで Object.keys(obj).length を使うと、プロパティがいくつあるかを数えられます。キーの配列の長さが、そのままプロパティの数になるからです。
const settings = {
theme: "dark",
fontSize: 16,
language: "ja",
};
console.log(Object.keys(settings).length); // => 3
// 空のオブジェクトかどうかの判定にも使える
const empty = {};
if (Object.keys(empty).length === 0) {
console.log("プロパティがありません");
}
「オブジェクトが空かどうか」を確かめたいときにもこの書き方が便利です。{} === {} のような比較ではうまく判定できないため、キーの数が 0 かどうかで判断します。
プロパティが取り出される順番
3つのメソッドはプロパティを配列にしますが、その「並び順」には決まったルールがあります。基本は書いた順(追加した順)ですが、キーが整数として解釈できる文字列のときだけ例外があり、小さい数から順に並べ替えられます。
const data = {
2: "two",
10: "ten",
1: "one",
name: "sample",
};
console.log(Object.keys(data));
// => ["1", "2", "10", "name"]
// 数値キーは昇順、そのあとに文字列キーが追加順で並ぶ
このように、数値のキーは記述した順ではなく昇順に並びます。順番が重要な処理(たとえば ID をキーにしたデータを表示順どおりに扱いたい場合)では、思った並びにならないことがあるので注意してください。順番を確実に保ちたいときは、キーを文字列以外で扱える Map を使うか、配列として持つ設計に切り替えるのが安全です。
配列やネストで気をつけること
配列に使うとインデックスが文字列で返る
配列もオブジェクトの一種なので、これらのメソッドを配列に対して使うこと自体は可能です。ただし Object.keys が返すインデックスは数値ではなく文字列になる点に注意が必要です。
const fruits = ["apple", "banana", "orange"];
console.log(Object.keys(fruits)); // => ["0", "1", "2"](文字列)
console.log(Object.values(fruits)); // => ["apple", "banana", "orange"]
console.log(Object.entries(fruits));
// => [["0", "apple"], ["1", "banana"], ["2", "orange"]]
インデックスを数値として扱いたい場合は、Number() で変換するか、そもそも配列には forEach や map を直接使うほうが自然です。これらのメソッドは「配列ではないオブジェクトを反復したいとき」に使うものだと考えると、用途を整理しやすくなります。
ネストしたオブジェクトには浅くしか効かない
これらのメソッドが見るのは、オブジェクトの一番外側のプロパティだけです。値の中にさらにオブジェクトが入れ子になっていても、その中までは展開されません。
const profile = {
name: "Taro",
address: {
city: "Tokyo",
zip: "100-0001",
},
};
console.log(Object.values(profile));
// => ["Taro", { city: "Tokyo", zip: "100-0001" }]
// address の中身までは取り出されず、オブジェクトのまま返る
上の例で address の値はオブジェクトのまま配列に入ります。中の city や zip まで処理したいときは、取り出した値に対してもう一度 Object.entries を使うなど、入れ子の階層に合わせて処理を重ねる必要があります。「これらのメソッドは1階層分だけを配列にする」と覚えておきましょう。
まとめ
Object.keys・Object.values・Object.entries は、オブジェクトを配列に変換して扱いやすくするためのメソッドです。要点を振り返ります。
keysはキーの配列、valuesは値の配列、entriesは[キー, 値]の配列を返すentriesとfor...of+ 分割代入で、キーと値をまとめて反復できるentries→map→fromEntriesでオブジェクトの値をまとめて書き換えられるObject.keys(obj).lengthでプロパティの数を数えられる- 数値キーは昇順に並ぶ、配列ではインデックスが文字列になる、ネストには浅くしか効かない点に注意
オブジェクトの中身を「順番に処理したい」「値を集計したい」「形を変えたい」と感じたら、まずこの3つのメソッドで配列にできないかを考えてみてください。配列のメソッドと組み合わせることで、多くの処理がシンプルに書けるようになります。