オブジェクトのキー・値・組を配列で取り出します。継承したものや列挙できないものは入りません。数字に見えるキーが先に来る理由、配列に戻す方法まで実行して確かめます。
オブジェクトの中身を配列として取り出します。
オブジェクトには map() も filter() も無いので、まず配列にします。
example.js
const scores = { math: 80, english: 95 };
console.log(Object.keys(scores));
console.log(Object.values(scores));
console.log(Object.entries(scores));
出力書き換えて実行できます
[ 'math', 'english' ]
[ 80, 95 ]
[ [ 'math', 80 ], [ 'english', 95 ] ]
example.js
const scores = { math: 80, english: 95, science: 70 };
console.log(Object.keys(scores).length);
console.log(Object.values(scores).reduce((sum, n) => sum + n, 0));
for (const [name, score] of Object.entries(scores)) {
console.log(`${name}: ${score}`);
}
出力書き換えて実行できます
3
245
math: 80
english: 95
science: 70
上のコードは書き換えて実行できます。項目を1つ足してみてください。
使い分け
|
返るもの |
使う場面 |
Object.keys() |
キーの配列 |
名前だけ要る。件数を数える |
Object.values() |
値の配列 |
合計・最大など、値だけ要る |
Object.entries() |
[キー, 値] の配列 |
両方要る(いちばん多い) |
example.js
const parent = { p: 1 };
const child = Object.create(parent);
child.c = 2;
console.log(Object.keys(child));
console.log('p' in child);
console.log(child.p);
出力書き換えて実行できます
[ 'c' ]
true
1
example.js
const obj = Object.defineProperty({ a: 1 }, 'b', { value: 2, enumerable: false });
console.log(Object.keys(obj));
console.log(obj.b);
console.log(Object.getOwnPropertyNames(obj));
出力書き換えて実行できます
[ 'a' ]
2
[ 'a', 'b' ]
entries() は [キー, 値] の配列を返すので、分割代入で受け取ると読めます。
入らないもの
Object.keys() が返すのは、そのオブジェクト自身が持つ、
列挙できる、文字列のキーだけです。3つとも条件です。
受け継いだものは入らない
example.js
const s = Symbol('id');
const obj = { a: 1, [s]: 2 };
console.log(Object.keys(obj));
console.log(Object.getOwnPropertySymbols(obj).length);
console.log(obj[s]);
出力書き換えて実行できます
[ 'a' ]
1
2
example.js
const obj = { b: 1, 2: 2, a: 3, 1: 4 };
console.log(Object.keys(obj));
出力書き換えて実行できます
[ '1', '2', 'b', 'a' ]
child.p は読めるのに、Object.keys() には入りません。
「読めるかどうか」と「自身が持っているか」は別です。
for...in は受け継いだものも回します。だから配列にも使ってはいけません。
列挙できないものは入らない
example.js
const m = new Map([['b', 1], [2, 2], ['a', 3]]);
console.log([...m.keys()]);
出力書き換えて実行できます
[ 'b', 2, 'a' ]
example.js
console.log(Object.keys([10, 20]));
console.log(Object.keys('abc'));
console.log([...[10, 20].keys()]);
console.log([...[10, 20].entries()]);
出力書き換えて実行できます
[ '0', '1' ]
[ '0', '1', '2' ]
[ 0, 1 ]
[ [ 0, 10 ], [ 1, 20 ] ]
隠したいときは enumerable: false、全部見たいときは getOwnPropertyNames() です。
Symbol のキーは入らない
example.js
console.log(Object.fromEntries([['a', 1], ['b', 2]]));
console.log(Object.fromEntries(new Map([['a', 1]])));
出力書き換えて実行できます
{ a: 1, b: 2 }
{ a: 1 }
example.js
const scores = { math: 80, english: 95, science: 70 };
const passed = Object.fromEntries(
Object.entries(scores).filter(([, score]) => score >= 80),
);
const doubled = Object.fromEntries(
Object.entries(scores).map(([name, score]) => [name, score * 2]),
);
console.log(passed);
console.log(doubled);
出力書き換えて実行できます
{ math: 80, english: 95 }
{ math: 160, english: 190, science: 140 }
Symbol は「うっかり列挙されない」ための道具でもあります。
順序
数字に見えるキーが先に、小さい順で並びます。そのあとが書いた順です。
javascript
console.log(Object.keys(null));
TypeError: Cannot convert undefined or null to object
example.js
const data = null;
console.log(Object.keys(data ?? {}));
console.log(Object.keys(data ?? {}).length === 0);
出力書き換えて実行できます
[]
true
b より先に 1 と 2 が来ています。書いた順にはなりません。
順序に依存する処理を書かないでください。 順番が要るなら Map か配列を使います。
Map は入れた順を保ちます。キーに数値も使えます。
配列や文字列に使うと
添字が文字列で返ります。 配列に使う理由はほぼありません。
配列の添字が欲しいなら array.keys()(数値で返る)か entries() を使ってください。
配列からオブジェクトに戻す
Object.fromEntries() が entries() の逆です。
**「配列にして、加工して、戻す」**が定型です。
オブジェクトに filter() や map() は無いので、この形で代わりにします。
分割代入で先頭を空けておくのは「1つ目は使わない」という書き方です。読み飛ばす意図が伝わります。
null や undefined を渡すと落ちる
通信の結果をそのまま渡すと、ここで落ちます。
?? {} を挟むだけで防げます。
結合と複製の話は
オブジェクトを結合する にまとめてあります。
鍵に文字列以外を使いたくなったら、オブジェクトではなく Map の出番です。数値の 1 と文字列の '1' を別の鍵として持てます。
→ Map と Set