자바스크립트 자료구조와 자료형 파트의 맵과 셋에 대해 공부하겠습니다.
맵과 셋
지금까지 아래와 같은 복잡한 자료구조를 알아보았다.
객체 - 키가 있는 컬렉션을 저장함
배열 - 순서가 있는 컬렉션을 저장함
하지만 현실 세계를 반영하기엔 이 두 자료구조 만으론 부족하여 맵(Map)과 셋(Set)이 등장하게 되었다.
맵
맵(Map)은 키가 있는 데이터를 저장한다는 점에서 객체와 유사하다. 맵은 키에 다양한 자료형을 허용한다는 점에서 차이가 있다.
맵에는 다음과 같은 주요 메서드와 프로퍼티가 있다.
new Map() - 맵을 만든다.
map.set(key, value) - key를 이용해 value를 저장한다.
map.get(key) - key에 해당하는 값을 반환한다. key가 존재하지 않으면 undefined를 반환한다.
map.has(key) - key가 존재하면 true, 존재하지 않으면 false를 반환한다.
map.delete(key) - key에 해당하는 값을 삭제한다.
map.clear() - 맵 안의 모든 요소를 제거한다.
map.size - 요소의 개수를 반환한다.

맵은 객체와 달리 키를 문자형으로 변환하지 않는다. 키엔 자료형 제약이 없다.
TMI - map[key]는 Map을 쓰는 바른 방법이 아니다.
map.[key] = 2로 값을 설정하는 것 같이 map.[key]를 사용할 수 있긴하다. 하지만 이 방법은 map을 일반 객체처럼 취급하게 된다. 따라서 여러 제약이 생기게 된다.
map을 사용할 땐 map 전용 메서드 set, get 등을 사용해야만 한다.
맵은 키로 객체를 허용한다.

객체를 키로 사용할 수 있다는 점은 맵의 가장 중요한 기능 중 하나이다. 객체에는 문자열 키를 사용할 수 있다. 하지만 객체 키는 사용할 수 없다.
객체형 키를 객체에 써보자.

visitsCountObj는 객체이기 때문에 모든 키를 문자형으로 변환시킨다. 이 과정에서 Lee는 문자형으로 변환되어 "[object Object]"가 된다.
TMI - 맵이 키를 비교하는 방식
맵은 SameValueZero라 불리는 알고리즘을 사용해 값의 등가 여부를 확인한다.
이 알고리즘은 일치 연산자 ===와 거의 유사하지만, NaN과 NaN을 같다고 취급하는 것에서 일치 연산자와 차이가 있다. 따라서 맵에선 NaN도 키로 쓸 수 있다.
이 알고리즘은 수정하거나 커스터마이징 하는 것이 불가능하다.
TMI - 체이닝
map.set을 호출할 때마다 맵 자신이 반환된다. 이를 이용하면 map.set을 '체이닝(chaining)'할 수 있다.

맵의 요소에 반복 작업하기
다음 세 가지 메서드를 사용해 맵의 각 요소에 반복 작업을 할 수 있다.
map.keys() - 각 요소의 키를 모은 반복 가능한(iterable, 이터러블) 객체를 반환한다.
map.values() - 각 요소의 값을 모은 이터러블 객체를 반환한다.
map.entries() - 요소의 [키, 값]을 한 쌍으로 하는 이터러블 객체를 반환한다. 이 이터러블 객체는 for..of 반복문의 기초로 쓰인다.

TMI - 맵은 삽입 순서를 기억한다.
맵은 값이 삽입된 순서대로 순회를 실시한다. 객체가 프로퍼티 순서를 기억하지 못하는 것과는 다르다.
여기에 더하여 맵은 배열과 유사하게 내장 메서드 forEach도 지원한다.

Object.entries: 객체를 맵으로 바꾸기
각 요소가 키-값 쌍인 배열이나 이터러블 객체를 초기화 용도로 맵에 전달해 새로운 맵을 만들 수 있다.

평범한 객체를 가지고 맵을 만들고 싶다면 내장 메서드 Object.entries(obj)를 활용해야한다.
이 메서드는 객체의 키-값 쌍을 요소([key, value])로 가지는 배열을 반환한다.

Object.entries를 사용해 객체 obj를 배열 [["name", "Lee"], ["age", 30]]로 바꾸고, 이 배열을 이용해 새로운 맵을 만들어보았다.
Object.fromEntries: 맵을 객체로 바꾸기
방금까진 Object.entries(obj)를 사용해 평법한 객체를 맵으로 바꾸는 방법에 대해 알아보았다.
이젠 이 반대인 맵을 객체로 바꾸는 방법에 대해 알아보자.
Object.fromEntries를 사용하면 가능하다. 이 메서드는 각 요소가 [키, 값] 쌍인 배열을 객체로 바꿔준다.

Object.fromEntries를 사용해 맵을 객체로 바꿔보자.
자료가 맵에 저장되어있는데, 서드파티 코드에서 자료를 객체형태로 넘겨받길 원할 때 이 방법을 사용할 수 있다.

map.entries()를 호출하면 맵의 [키, 값]을 요소로 가지는 이터러블을 반환한다.
Object.fromEntries를 사용하기 위해 딱 맞는 형태이다.
(*)로 표시한 줄을 좀 더 짧게 줄이는 것도 가능하다.

Object.fromEntries는 인수로 이터러블 객체를 받기 때문에 짧게 줄인 코드도 이전 코드와 동일하게 동작한다. 꼭 배열을 전달해줄 필요는 없다. 그리고 map에서의 일반적인 반복은 map.entries()를 사용했을 때와 같은 키-값 쌍을 반환한다. 따라서 map과 동일한 키-값을 가진 일반 객체를 얻게 된다.
셋
셋(Set)은 중복을 허용하지 않는 값을 모아놓은 특별한 컬렉션이다. 셋에 키가 없는 값이 저장된다.
주요 메서드는 다음과 같다.
new Set(iterable) - 셋을 만든다. 이터러블 객체를 전달받으면(대개 배열을 전달받음) 그 안의 값을 복사해 셋에 넣어준다.
set.add(value) - 값을 추가하고, 셋 자신을 반환한다.
set.delete(value) - 값을 제거한다. 호출 시점에 셋 내에 값이 있어서 제거에 성공하면 true, 아니면 false를 반환한다.
set.has(value) - 셋 내에 값이 존재하면 true, 아니면 false를 반환한다.
set.clear() - 셋을 비운다.
set.size - 셋에 몇 개의 값이 있는지 세준다.
셋 내에 동일한 값(value)이 있다면 set.add(value)을 아무리 많이 호출하더라도 아무런 반응이 없을 것이다.
셋 내의 값에 중복이 없는 이유가 이 때문이다.
방문자 방명록을 만든다고 가정해보자. 한 방문자가 여러 번 방문해도 방문자를 중복해서 기록하지 않겠다고 결정 내린 상황이다. 즉, 한 방문자는 '단 한 번만 기록' 되어야 한다.
이때 적합한 자료구조가 바로 셋이다.

셋 대신 배열을 사용하여 방문자 정보를 저장한 후, 중복 값 여부는 배열 메서드인 arr.find를 이용해 확인할 수도 있다. 하지만 arr.find는 배열 내 요소 전체를 뒤져 중복 값을 찾기 때문에, 셋보다 성능 면에서 떨어진다. 반면, 셋은 값의 유일무이함을 확인하는 데 최적화되어 있다.
셋의 값에 반복 작업하기
for..of나 forEach를 사용하면 셋의 값을 대상으로 반복 작업을 수행할 수 있다.

forEach에 쓰인 콜백 함수는 세 개의 인수를 받는데, 첫 번째는 값, 두 번째도 같은 값인 valueAgain을 받고 있다. 세 번째는 목표하는 객체(셋)이고, 동일한 값이 인수에 두 번 등장하고 있다.
이렇게 구현된 이유는 맵과의 호환성 때문이다. 맵의 forEach에 쓰인 콜백이 세 개의 인수를 받을 때를 위해서다. 맵을 셋으로 혹은 셋을 맵으로 교체하기 쉽다.
셋에도 맵과 마찬가지로 반복 작업을 위한 메서드가 있다.
set.keys() - 셋 내의 모든 값을 포함하는 이터러블 객체를 반환한다.
set.values() - set.keys와 동일한 작업을 한다. 맵과의 호환성을 위해 만들어진 메서드이다.
set.entries() - 셋 내의 각 값을 이용해 만든 [value, value] 배열을 포함하는 이터러블 객체를 반환한다. 맵과의 호환성을 위해 만들어졌다.