実践ノート / コード解説
入出力の条件からコードを読む
何が変わり、何が共有され、どの入力で前提が崩れるかを読むために。
処理の説明より先に条件を書く
uniqueById なら「ID を持つ null でないオブジェクトの配列を受け取り、異なるキーごとに最後のオブジェクトを返す」と書く方が、単なる重複除去より正確です。同一性と重複時の方針が明らかになります。最初のオブジェクトを残す要件なら、重複がなくても実装は要件と一致しません。
行だけでなく値を追う
map はキーと値の組を作り、Map コンストラクターがそれを読み込みます。同じキーの後の値が保存値を置き換えますが、列挙順は最初の挿入位置に従います。最後のスプレッドは新しい配列を作ります。重複を含む 3 項目を追うと、値の置き換えと順序の違いが見えます。
見えない共有を探す
新しい配列でも、オブジェクトまで新しいとは限りません。出力側のオブジェクト変更が入力側にも影響します。浅い集合か独立したコピーか、利用側の要件を確認します。ID の欠落は undefined キーで衝突し、1 と "1" は別のキーです。これらは要件と照合すべき挙動であり、自動的に不具合とは言えません。
説明を確認項目にする
空の入力、1 項目、重複 ID、ID の欠落、不正な項目を試します。実行前に予想結果を書きます。並べ替えや深いコピーを行うという説明があれば、その操作を探してください。このコードにはありません。説明は見えるコードに照らして確かめられる必要があります。
具体例で考える
コード解説
作成済みの例:この固定結果はサンプル原文にのみ対応し、AI のリアルタイム回答ではありません。
原文
function uniqueById(items) {
return [...new Map(items.map(item => [item.id, item])).values()];
}解説付きの結果
目的
異なる id ごとに 1 つのオブジェクトを返します。
仕組み
items.map が [id, オブジェクト] の組を作ります。Map は同じキーの値を後の値で置き換えますが、キーの最初の位置は変わりません。スプレッド構文は新しい配列を作ります。
重要な点
[{id:1,v:"a"},{id:2,v:"b"},{id:1,v:"c"}] の予想結果は [{id:1,v:"c"},{id:2,v:"b"}] です。実行記録ではなく、コードから導いた例です。
制約
オブジェクトの参照は元の配列と共有されます。id のない項目は undefined キーにまとめられ、null の項目は例外になります。数値 1 と文字列 "1" は別のキーです。