JavaScript 重複を除去する(投稿用)
JavaScriptの重複をSet Object, Map Objectを用いて取り除く方法について解説します。
code:javascript
const members = [
{ name: 'Alice', group: 'A班' },
{ name: 'Bob', group: 'B班' },
{ name: 'Carol', group: 'A班' },
{ name: 'Dave', group: 'C班' },
{ name: 'Eve', group: 'D班' },
]
例えば、上記のmembersをグループ毎に分けたい時、下記の手順を取るかと思います。
code:javascript
// 重複のないグループ名の配列に変換する
const _groups = distinctGroups(members)
// => group: 'A班' ), { group: 'B班' }, { group: 'C班' }, { group: 'D班' }
// _groupsの各要素について、同一のグループ名を持つ者をmembersの中から抽出してまとめる
const groups = assignMember(_groups, members)
/* =>
[
{ group: 'A班', members: 'Alice', 'Carol' },
{ group: 'B班', members: 'Bob' },
{ group: 'C班', members: 'Dave' },
{ group: 'D班', members: 'Eve' }
]
*/
一旦重複のないグループの配列を作るわけですが、上記のgroupの重複除去にはSet Objectが利用できます。
Set オブジェクトは値のコレクションです。挿入順に要素を反復することができます。Set に重複する値は格納出来ません。Set 内の値はコレクション内で一意となります。
引用: Set - JavaScript | MDN
Set Objectには要素を次々追加することができますが、既に存在する要素を追加した場合は無視されます。
code:javascript
let mySet = new Set()
mySet.add(1) // Set 1
mySet.add(5) // Set 1, 5
mySet.add(5) // Set 1, 5
引用: Set - JavaScript | MDN
また、コンストラクタに引数を渡すことにより、渡した要素を加えたSet Objectが生成できます。
code:javascript
const set = new Set(1, 1, 1, 2, 3, 4, 1, 3, 5, 3)
console.log(set)
// => Set(5) { 1, 2, 3, 4, 5 }
Set Objectはそのままでは配列としては使えず、一旦values()メソッドでイテレーターオブジェクトを返却してから、それを配列に変換する必要があります。
Array.from()やスプレッド構文が使えます。
code:javascript
const arr = ...set.values()
const arr2 = Array.from(set.values())
// => どちらも1, 2, 3, 4, 5
Set Objectを使うと、下記のように重複を除去するdistinctGroups()を書くことができます。
code:javascript
const distinctGroups = members => {
// グループ名のみ抽出
const groups = members.map(e => e.group)
// Set Objectを生成して重複を除去
const set = new Set(groups)
// グループ名のstring配列から、{ group: string } のオブジェクトの配列にmap
return ...set.values().map(e => ({ group: e }))
}
console.log(distinctGroups(members))
// => { group: 'A班' }, { group: 'B班' }, { group: 'C班' }, { group: 'D班' }
groupがただのstringのグループ名であれば上記の方法で問題ないのですが、Objectだった場合、Set Objectを用いた重複除去は意図した通りに動作しません。
code:javascript
const members = [
{ name: 'Alice', group: { id: '001', name: 'A班' } },
{ name: 'Bob', group: { id: '002', name: 'B班' } },
{ name: 'Carol', group: { id: '001', name: 'A班' } },
{ name: 'Dave', group: { id: '003', name: 'C班' } },
{ name: 'Eve', group: { id: '004', name: 'D班' } },
]
この場合、Set Objectを用いて重複除去を行おうとした場合下記の結果となります。
code:javascript
const set = new Set(members.map(e => e.group))
console.log(set)
// 出力内容
Set(5) {
{ id: '001', name: 'A班' },
{ id: '002', name: 'B班' },
{ id: '001', name: 'A班' },
{ id: '003', name: 'C班' },
{ id: '004', name: 'D班' }
}
なぜかと言うと、JavaScriptのObjectの等価性の問題です。
Object同士が等価かどうかは、「Objectの内容が同一かどうか」ではなく、「同一のインスタンスか」で確認しているので、「内容が同じでも別のインスタンスのObject同士」は異なるオブジェクトと判断されます。
内容は全く見ずに「全部別のインスタンスだからそれぞれ異なる内容や!」って具合でまるごとSet Objectに含まれてしまっている感じです。
code:javascript
const group = members0.group
const group2 = { id: '001', name: 'A班' }
const group3 = { id: '001', name: 'A班' }
console.log(group === group2)
// => false
console.log(group === group3)
// => false
console.log(group2 === group3)
// => false
この場合は、Map Objectを利用することで意図した通りに重複を除去できます。
Map Objectはkeyとvalueのペアを格納できるObjectで、keyに関してSet Objectのように重複が排除されます。
Set ObjectのKey value pairのような形で使用できます。
code:javascript
// コンストラクタにはkey, valueの配列を渡せる
const map = new Map([
'1', {id: 1, name: 'user1'},
'2', {id: 2, name: 'user2'},
'2', {id: 3, name: 'user3'},
])
console.log(map)
// 出力内容
Map(2) {
'1' => { id: 1, name: 'user1' },
'2' => { id: 3, name: 'user3' }
}
// map.values()は、Map Objectに格納されたvalueのみのイテレーターオブジェクトを返却する。
// Set Objectのvalues()と同様配列に変換するにはArray.from()やスプレッド構文が使える。
console.log(...map.values())
// => { id: 1, name: 'user1' }, { id: 3, name: 'user3' }
Map Objectを用いてdistinctGroups()を書き直すと下記のようになります。
code:javascript
const distinctGroups = members => {
// グループオブジェクトのみ抽出
const groups = members.map(e => e.group)
// key valueペアを生成 keyはgroup.id, valueはgroupオブジェクトそのままとする
const kvp = groups.map(group => (group.id, group))
// Map Objectを生成して重複を除去
const map = new Map(kvp)
// 配列に変換して返却
return ...map.values()
}
console.log(distinctGroups(members))
// => { id: '001', name: 'A班' }, { id: '002', name: 'B班' }, { id: '003', name: 'C班' }, { id: '004', name: 'D班' }
以上、Set Object, Map Objectを用いた重複除去についてでした。
参考:
Set - JavaScript | MDN
Map - JavaScript | MDN