1
/
5

【社内TechBlog vol.5】[JavaScript] 重複を除去する

JavaScriptの重複をSet Object, Map Objectを用いて取り除く方法について解説します。

 const members = [
     { name: 'Alice', group: 'A班' },
     { name: 'Bob', group: 'B班' },
     { name: 'Carol', group: 'A班' },
     { name: 'Dave', group: 'C班' },
     { name: 'Eve', group: 'D班' },
 ]

例えば、上記のmembersをグループ毎に分けたい時、下記の手順を取るかと思います。

 // 重複のないグループ名の配列に変換する
 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には要素を次々追加することができますが、既に存在する要素を追加した場合は無視されます。

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が生成できます。

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()やスプレッド構文が使えます。

const arr = [...set.values()]
const arr2 = Array.from(set.values())
// => どちらも[1, 2, 3, 4, 5]

Set Objectを使うと、下記のように重複を除去するdistinctGroups()を書くことができます。

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を用いた重複除去は意図した通りに動作しません。

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を用いて重複除去を行おうとした場合下記の結果となります。

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に含まれてしまっている感じです。

const group = members[0].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のような形で使用できます。

// コンストラクタには[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()を書き直すと下記のようになります。

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

株式会社Freedoming's job postings

Weekly ranking

Show other rankings
Like 刀川正嗣's Story
Let 刀川正嗣's company know you're interested in their content