entries() で配列にして絞り、fromEntries() で戻します。分割代入の残余で除く短い書き方、無い鍵を渡したとき、入れ子が共有されたままになる点までを実行して確かめます。
答え
除くだけなら、分割代入の残余がいちばん短いです。
example.js
const user = { id: 1, name: 'あ', secret: 'x' };
const { secret, ...rest } = user;
console.log(rest);
console.log(user);
出力書き換えて実行できます
{ id: 1, name: 'あ' }
{ id: 1, name: 'あ', secret: 'x' }
example.js
function pick(source, keys) {
return Object.fromEntries(
Object.entries(source).filter(([key]) => keys.includes(key)),
);
}
const user = { id: 1, name: 'あ', secret: 'x' };
console.log(pick(user, ['id', 'name']));
出力書き換えて実行できます
{ id: 1, name: 'あ' }
...rest が残り全部を受け取ります。元のオブジェクトは変わりません。
鍵を変数で渡したいなら、entries() で絞ります。
example.js
function omit(source, keys) {
return Object.fromEntries(
Object.entries(source).filter(([key]) => !keys.includes(key)),
);
}
const user = { id: 1, name: 'あ', secret: 'x' };
console.log(omit(user, ['secret']));
出力書き換えて実行できます
{ id: 1, name: 'あ' }
example.js
function pick(source, keys) {
return Object.fromEntries(
Object.entries(source).filter(([key]) => keys.includes(key)),
);
}
const user = { id: 1, name: 'あ' };
console.log(pick(user, ['nothing']));
console.log(pick(user, []));
出力書き換えて実行できます
{}
{}
除く側も同じ形
javascript
const { secret, token, password, ...rest } = user;
example.js
function omit(source, keys) {
return Object.fromEntries(
Object.entries(source).filter(([key]) => !keys.includes(key)),
);
}
const user = { id: 1, name: 'あ', passwordHash: 'xxx', token: 'yyy' };
console.log(omit(user, ['passwordHash', 'token']));
出力書き換えて実行できます
{ id: 1, name: 'あ' }
! を付けるだけです。「配列にして、絞って、戻す」形はここでも同じです。
→ オブジェクトを回す・変換する
無い鍵を渡しても落ちない
example.js
function pick(source, keys) {
return Object.fromEntries(
Object.entries(source).filter(([key]) => keys.includes(key)),
);
}
const user = { id: 1, name: 'あ', passwordHash: 'xxx', newSecret: 'zzz' };
console.log(pick(user, ['id', 'name']));
出力書き換えて実行できます
{ id: 1, name: 'あ' }
example.js
function pick(source, keys) {
return Object.fromEntries(
Object.entries(source).filter(([key]) => keys.includes(key)),
);
}
const user = { id: 1, name: 'あ', role: 'admin' };
console.log(Object.keys(pick(user, ['name', 'id'])));
出力書き換えて実行できます
[ 'id', 'name' ]
空のオブジェクトが返ります。 場合分けは要りません。
ただし「無い鍵を指定した」ことにも気づけません。
鍵の名前を打ち間違えると、静かに空になります。
使い分け
| したいこと |
書き方 |
| 決まった鍵を1〜2個除く |
分割代入の残余 |
| 鍵を配列(変数)で渡す |
entries() + filter() |
| 決まった鍵だけ残す(少数) |
const { id, name } = user して組み直す |
分割代入は書いた鍵の名前が変数になるので、
除きたい鍵が多いと、使わない変数が並びます。
example.js
function pickInOrder(source, keys) {
return Object.fromEntries(
keys.filter((key) => Object.hasOwn(source, key)).map((key) => [key, source[key]]),
);
}
const user = { id: 1, name: 'あ', role: 'admin' };
console.log(Object.keys(pickInOrder(user, ['name', 'id'])));
出力書き換えて実行できます
[ 'name', 'id' ]
3つとも使いません。この形になったら omit() に切り替えてください。
いちばんの使い道:外に出さない
example.js
function pick(source, keys) {
return Object.fromEntries(
Object.entries(source).filter(([key]) => keys.includes(key)),
);
}
const list = [
{ id: 1, name: 'あ', secret: 'x' },
{ id: 2, name: 'い', secret: 'y' },
];
console.log(list.map((user) => pick(user, ['id', 'name'])));
出力書き換えて実行できます
[ { id: 1, name: 'あ' }, { id: 2, name: 'い' } ]
javascript
const user = { id: 1, name: 'あ' };
const picked = {};
for (const key of ['id', 'nothing']) {
picked[key] = user[key];
}
console.log(picked);
console.log('nothing' in picked);
{ id: 1, nothing: undefined }
true
「除く」より「選ぶ」ほうが安全です。
omit() は、あとから鍵が増えたときにその新しい鍵が漏れます。
外へ返すデータは pick() で残すものを書くほうが事故が起きません。
example.js
function pick(source, keys) {
return Object.fromEntries(
Object.entries(source).filter(([key]) => keys.includes(key)),
);
}
const user = { id: 1, profile: { city: '東京' } };
const picked = pick(user, ['profile']);
picked.profile.city = '大阪';
console.log(user.profile.city);
出力書き換えて実行できます
大阪
newSecret が増えても漏れません。
並び順は元のまま
渡した順(name, id)ではなく、元の順(id, name)になります。
entries() が元のオブジェクトを走査しているからです。
順番も指定したいなら、鍵の側から組み立ててください。
配列に対して使う
一覧を返すときは map() と組み合わせます。
よくある間違い
手で組み立てて、無い鍵を作ってしまう
元に無い鍵が、値 undefined で増えています。
in で調べると true になるので、「あるが空」と誤解されます。
Object.hasOwn() で確かめてから入れるか、entries() で絞る形にしてください。
→ オブジェクトの基本
複製されるのは1段だけ
取り出した中身は、元と同じオブジェクトを指しています。
書き換えると元も変わります。
深く複製したいなら structuredClone() を使ってください。
→ オブジェクトを結合する
まとめ
- 除くだけなら分割代入の残余(
const { secret, ...rest } = user)
- 鍵を変数で渡すなら
entries() + filter() + fromEntries()
- 無い鍵を渡しても落ちない。打ち間違いにも気づけない
- 外へ返すなら「選ぶ」。 「除く」は鍵が増えたときに漏れる
- 複製されるのは1段だけ。入れ子は元と共有される