[JS #5] ES6 Map(), Set()

얼마 전부터 회사 업무를 진행할 때 본격적으로, 그리고 의식적으로 ES6 에 도입된 문법을 적용하고 있는데, 그중에서 가장 자주 활용하는 자료구조, Map 과 Set 에 대해 이야기해보려고 합니다. 이 글의 모티브는 상당부분 Mozilla 웹기술블로그에 기반합니다. 사내세미나에서 발표한 내용을 글로 정리했습니다.

Map

  • Map() 은 자바스크립트의 key-value 페어(pair) 로 이루어진 컬렉션
  • key 를 사용해서 value 를 get, set 할 수 있음
  • key 들은 중복될 수 없음: 하나의 key 에는 하나의 value 만
  • key 로 사용할 수 있는 데이터형: string, symbol(ES6), object, function >> number 는 사용할 수 없음에 주의!
// 새로운 map 을 만들고 map 에 key, value 엔트리를 추가
let me = new Map();
me.set('name', 'kevin');
me.set('age', 28);
console.log(me.get('age'); // 28
// 대괄호를 사용해서 map 을 선언하는 방법
const roomTypeMap = new Map(
[
["01", "원룸(오픈형)"],
["02", "원룸(분리형)"],
["03", "원룸(복층형)"],
["04", "투룸"],
["05", "쓰리룸"]
]
);
// 새로운 map 을 만들고 그 데이터를 기존의 [key, value] 페어컬렉션으로 채움
let you = new Map().set('name', 'paul').set('age', 34);
console.log(you.get('name')); // 'paul'
// has(): 주어진 key 가 존재하는지 확인
console.log(me.has('name')); // true
// size: map 에 담겨진 엔트리의 개수를 조회
console.log(you.size); // 2
// delete(): 엔트리를 삭제
me.delete('age');
console.log(me.has('age')); // false
// clear(): 모든 엔트리를 삭제
you.clear();
console.log(you.size); // 0

<참고 1> Map 과 Object 비교

  • Object 의 key 는 string 과 symbol(ES6) 만 가능하지만, map 은 어떤 값도 가능
  • Object 에서는 크기를 추적해서 알 수 있지만, map 은 손쉽게 얻을 수 있음(size)

Set

  • Set() 은 value 들로 이루어진 컬렉션(“집합”이라는 표현이 적절)
  • Array 와는 다르게 Set 은 같은 value 를 2번 포함할 수 없음
  • 따라서 Set 에 이미 존재하는 값을 추가하려고 하면 아무 일도 없음
// 비어있는 새로운 set 을 만듬
let setA = new Set();
// 새로운 set 을 만들고 인자로 전달된 iterable 로 인자를 채움
let setB = new Set().add('a').add('b');
setB.add('c');
console.log(setB.size); // 3
// has(): 주어진 값이 set 안에 존재할 경우, true 를 반환
// indexOf() 보다 빠름. 단, index 가 없음
console.log(setB.has('b')); // true
// set 에서 주어진 값을 제거
setB.delete('b');
console.log(setB.has('b')); // false
// set 안의 모든 데이터를 제거
setB.clear();
console.log(setB.size); // 0
  • <TODO> has() 는 indexOf() 보다 빠르다. 다만, index 이 존재하지 않기때문에 index 로 value 로 접근할 수 없다.

<참고 2> Spread 연산자

  • 이터러블 오브젝트(iterable object)의 엘리먼트를 하나씩 분리하여 전개
// string == iterable object
console.log([...'music']); // ['m', 'u', 's', 'i', 'c']

<참고 3> for 문들

  • for 문
let sampleArr = [1, 2, 3, 4, 5];
for (let i = 0, length = sampleArr.length; i < length; i++) {
console.log(sampleArr[i]);
}
  • forEach: ES5 자바스크립트 배열 메서드
let sampleArr = [1, 2, 3, 4, 5];
sampleArr.forEach(v => console.log(v));
  • for-in: Object 를 순회하기 위한
let sampleObj = {
a: 1,
b: 'hello',
c: [1, 2]
}
for (let key in sampleObj) {
console.log(key);
console.log(sampleObj[key]);
}
  • for-of: 배열의 요소들, 즉 data 를 순회하기 위한(string 도 가능)
let sampleArr = [1, 2, 3, 4, 5];
let (for value of sampleArr) {
console.log(value);
}

  • 일반 객체(Object)는 iterable 하지 않다!
  • for-of 나 …(spread 연산자)를 사용할 수 없다!
  • for-in 으로나 순회할 수 있다.

Map 의 iterable object

  • map.keys(), map.values()
  • map 안의 key 혹은 value 들을 순회할 수 있는 iterable object 를 반환
let me = new Map().set('a', 1).set('b', 2);
console.log([...me.keys()]); // ['a', 'b']
console.log([...me.values()]); // [1, 2]
  • map.entries(), map.next()
  • map 안의 모든 엔트리들을 순회할 수 있는 iterable object 를 반환
let you = new Map().set('Seoul', 28).set('Tokyo', 26);
let iterObj = you.entries();
console.log(iterObj.next()); // {value: ['Seoul', 28], done: false}
console.log(iterObj.next()); // {value: ['Tokyo', 26], done: false}
console.log(iterObj.next()); // {value: undefined, done: true}
  • for-of, map.forEach();
  • forEach 의 경우, 인자 순서가 이상한데(key, value 순서가 반대) Array.prototype.forEach() 구문과 통일성을 유지하기 위함(value, index, array 순서인 것)
let we = new Map().set('car', 30).set('bus', 45);// for-of 로 map 순회하기
for (let [key, value] of we) {
console.log(key + '^' + value);
}
// 차례대로 'car^30', 'bus^45' 출력
// forEach 로 map 순회하기
we.forEach((value, key, map) => {
console.log(key + '$' + value);
});
// 차례대로 'car$30', 'bus$45' 출력
  • 자바스크립트 배열 메서드에 존재하는 map, filter 메서드는 Map 에 존재하지 않는다. 하지만 아래와 같은 방식으로 우회해서 사용이 가능하다.
let me = new Map().set('a', 1).set('b', 2);// value 가 1 이상인 엔트리만 filtering 하기
let map1 = new Map(
[...me]
.filter(([k, v]) => v > 1)
);
console.log([...map1.entries()]) // [['b', 2]]
// key 뒤에 'super' 문자열을 붙이고, value 에 1을 더하기
let map2 = new Map(
[...me]
.map(([k, v]) => [k + "super", v + 1])
);
console.log([...map2.entries()]) // [['asuper, 2], [bsuper, 3]]

Set 의 iterable object

  • set.values();
  • 기본적으로 Set 의 prototype 메서드로 keys() 는 존재하지 않고, values() 만 존재하지만, MDN 의 설명에 따르면, map 오브젝트와 동일하게 동작하기 때문에 Set.prototype.keys() 는 Set.prototype.values() 와 같은 결과
let setA = new Set();
setA.add('a');
setA.add('b');
setA.add('a');
console.log([...setA.keys()]); // ['a', 'b']
console.log([...setA.values()]); // ['a', 'b']
  • set.entries();
let setB = new Set();
setB.add('Korea');
setB.add('Japan');
setB.add('China');
let entries = setB.entries();console.log(entries.next());
// {value: ['Korea', 'Korea'], done: false}
console.log(entries.next());
// {value: ['Japan', 'Japan'], done: false}
console.log(entries.next());
// {value: ['China', 'China'], done: false}
console.log(entries.next());
// {value: undefined, done: true}
  • for-of, set.forEach();
let setC = new Set();
setC.add('Korea');
setC.add('Japan');
setC.add('China');
for (let key of setC) {
console.log(key);
}
// 차례대로 'Korea', 'Japan', 'China' 출력
setC.forEach((v, k) => {
console.log(v);
})
// 차례대로 'Korea', 'Japan', 'China' 출력

Set: 집합연산

스위프트 집합연산 — 링크
  • union(합집합), intersection(교집합), difference(차집합)
let setA = new Set([1, 2, 3, 4, 5]);
let setB = new Set([4, 5, 6, 7, 8]);
// 합집합
let unionSet = new Set([...setA, ...setB])
for (let value of unionSet) {
console.log(value);
}
// 차례대로 1, 2, 3, 4, 5, 6, 7, 8 출력
// 교집합
let intersectionSet = new Set(
[...setA].filter(v => setB.has(v))
);
for (let value of intersectionSet) {
console.log(value);
}
// 차례대로 4, 5 출력
// 차집합
let differenceSet = new Set(
[...setA].filter(v => !setB.has(v))
);
for (let value of differenceSet) {
console.log(value);
}
// 차례대로 1, 2, 3 출력
  • symmetricDifference
// Symmetric Difference
var set1 = new Set([1, 2, 3, 4, 5]);
var set2 = new Set([3, 4, 5, 6, 7]);
var symmetricDifferenceSet = new Set(
[...[...set1].filter(x => !set2.has(x)), ...[...set2].filter(x => !set1.has(x))]
)
for (let value of symmetricDifferenceSet) {
console.log(value);
}
// 차례대로 1, 2, 6, 7 출력

지금까지 Map 과 Set 에 대해 자세히 알아보았습니다.

단순히 key 와 value 를 set 하거나 value 를 set 하는 것뿐만 아니라, iterable object 의 특성을 살려서 map 과 set 을 순회하는 것을 알아보았습니다. 더 나아가서는 map 과 set 에서는 지원하지 않는 배열 메서드(Array.prototype) 인 map, filter 를 적용해보고, 집합연산까지도 진행해보았습니다.

억지로 배열의 형태로, 기본 객체형태로 코딩하기 보다는 적재적소의 자료구조의 특성에 맞게 코딩하는 습관을 기르면 좋겠습니다.

다음에는 윗글에서도 잠깐 나왔지만, 왜 배열의 indexOf 메서드보다 Set 의 has 가 더 “빠른지" 알아보겠습니다^^ 읽어주셔서 감사합니다.

)

Seokyou (Kevin) Hong

Written by

Software Engineer who tries to be better than yesterday

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade