Map & Set

김수정·2020년 4월 21일
0
post-custom-banner

객체 - 문자열, 심볼 키가 있는 컬렉션을 저장함
배열 - 순서가 있는 컬렉션을 저장함
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 – 요소의 개수를 반환합니다.

map[key] = value 사용 금지.

map 반복 작업

맵은 객체와 달리 값이 삽입된 순서대로 순회를 실시합니다. 즉, 프로퍼티 순서를 기억한다는 것이죠.
map.keys() – 각 요소의 키를 모은 반복 가능한(iterable, 이터러블) 객체를 반환합니다.
map.values() – 각 요소의 값을 모은 이터러블 객체를 반환합니다.
map.entries() – 요소의 [키, 값]을 한 쌍으로 하는 이터러블 객체를 반환합니다. 이 이터러블 객체는 for..of반복문의 기초로 쓰입니다.
map.forEach((value, key, map) => {}) - 배열처럼 forEach 내장 메서드를 사용할 수 있습니다.

let recipeMap = new Map([
  ['cucumber', 500],
  ['tomatoes', 350],
  ['onion',    50]
]);

// 키(vegetable)를 대상으로 순회합니다.
for (let vegetable of recipeMap.keys()) {
  alert(vegetable); // cucumber, tomatoes, onion
}

// 값(amount)을 대상으로 순회합니다.
for (let amount of recipeMap.values()) {
  alert(amount); // 500, 350, 50
}

// [키, 값] 쌍을 대상으로 순회합니다.
for (let entry of recipeMap) { // recipeMap.entries()와 동일합니다.
  alert(entry); // cucumber,500 ...
}

recipeMap.forEach( (value, key, map) => {
  alert(`${key}: ${value}`); // cucumber: 500 ...
});

Object -> Map

맵과 동일한 모양을 만들어주는 메서드 Object.entries(obj)를 활용합니다.

let obj = {
  name: "John",
  age: 30
};

let map = new Map(Object.entries(obj));
alert( map.get('name') ); // John

Map -> Object

Object.fromEntries(map)는 각 요소가 [키, 값]쌍인 배열을 객체로 바꿔줍니다.
우리가 데이터를 맵에 저장했는데 서드파티 라이브러리에서 자료를 객체로 넘겨받길 원할 때 유용합니다.

let prices = Object.fromEntries([
  ['banana', 1],
  ['orange', 2],
  ['meat', 4]
]);

// now prices = { banana: 1, orange: 2, meat: 4 }

alert(prices.orange); // 2

Set

셋의 모습은 배열처럼 생겼습니다. 그러나 배열보다 유일무이함을 찾는데 최적화되어 있으므로 중복이 발생하지 않아야 할 때 사용하면 유용합니다.
new Set(iterable) – 셋을 만듭니다. 이터러블 객체를 전달받으면(대개 배열을 전달받음) 그 안의 값을 복사해 셋에 넣어줍니다.
set.add(value) – 값을 추가하고 셋 자신을 반환합니다.
set.delete(value) – 값을 제거합니다. 호출 시점에 셋 내에 값이 있어서 제거에 성공하면 true, 아니면 false를 반환합니다.
set.has(value) – 셋 내에 값이 존재하면 true, 아니면 false를 반환합니다.
set.clear() – 셋을 비웁니다.
set.size – 셋에 몇 개의 값이 있는지 세줍니다.

반복작업

셋과 맵의 호환이 쉽게 하기 위해 고안된 방법들입니다.
set.keys() – 셋 내의 모든 값을 포함하는 이터러블 객체를 반환합니다.
set.values() – set.keys와 동일한 작업을 합니다. 맵과의 호환성을 위해 만들어진 메서드입니다.
set.entries() – 셋 내의 각 값을 이용해 만든 [value, value] 배열을 포함하는 이터러블 객체를 반환합니다. 맵과의 호환성을 위해 만들어졌습니다.

let set = new Set(["oranges", "apples", "bananas"]);

for (let value of set) alert(value);

// forEach를 사용해도 동일하게 동작합니다.
set.forEach((value, valueAgain, set) => { // 첫 번째와 두 번째 인수는 같은 값입니다.
  alert(value);
});

WeakMap

참조값이 키나 값이 될 경우, 그 것을 참조하는 것이 더 이상 없어도 가비지 컬렉션의 대상이 되지 않는 다는 점을 해결하기 위해서, 위크맵과 위크셋이 생겼습니다. 따라서 위크- 들은 객체\만을 다루며, 가비지 컬렉션의 대상이 됩니다.

위크맵은 맵과 달리 반복 작업과 keys(), values(), entries() 메서드를 지원하지 않습니다.
아래의 단출한 메서드만 지원하며, 가비지컬렉션에 의해 언제 값이 사라질 지 모르기 때문에 그렇습니다.
weakMap.get(key)
weakMap.set(key, value)
weakMap.delete(key)
weakMap.has(key)

이런 제약이 심한 WeakMap을 사용하는 경우들을 살펴봅시다.

추가 데이터

부차적인 데이터를 저장할 때 유용합니다.
해당 객체에 부가적인 데이터들을 weakMap에 저장해놓고 해당 객체가 사라지면 관련 정보도 자동으로 삭제 대상이 됩니다.

// 📁 visitsCount.js
let visitsCountMap = new WeakMap(); // 위크맵에 사용자의 방문 횟수를 저장함

// 사용자가 방문하면 방문 횟수를 늘려줍니다.
function countUser(user) {
  let count = visitsCountMap.get(user) || 0;
  visitsCountMap.set(user, count + 1);
}

// 📁 main.js
let john = { name: "John" };

countUser(john); // John의 방문 횟수를 증가시킵니다.

// John의 방문 횟수를 셀 필요가 없어지면 아래와 같이 john을 null로 덮어씁니다.
john = null;

캐싱

캐시도 관련 정보들이 필요 없어지면 불필요한 정보겠지요?

// 📁 cache.js
let cache = new WeakMap();

// 연산을 수행하고 그 결과를 위크맵에 저장합니다.
function process(obj) {
  if (!cache.has(obj)) {
    let result = /* 연산 수행 */ obj;

    cache.set(obj, result);
  }

  return cache.get(obj);
}

// 📁 main.js
let obj = {/* ... 객체 ... */};

let result1 = process(obj);
let result2 = process(obj);

// 객체가 쓸모없어지면 아래와 같이 null로 덮어씁니다.
obj = null;

// 이 예시에선 맵을 사용한 예시처럼 cache.size를 사용할 수 없습니다.
// 하지만 obj가 가비지 컬렉션의 대상이 되므로, 캐싱된 데이터 역시 메모리에서 삭제될 겁니다.
// 삭제가 진행되면 cache엔 그 어떤 요소도 남아있지 않을겁니다.

WeakSet

위크맵과 대체적으로 다 유사합니다. 객체만 저장 가능합니다.

let visitedSet = new WeakSet();

let john = { name: "John" };
let pete = { name: "Pete" };
let mary = { name: "Mary" };

visitedSet.add(john); // John이 사이트를 방문합니다.
visitedSet.add(pete); // 이어서 Pete가 사이트를 방문합니다.
visitedSet.add(john); // 이어서 John이 다시 사이트를 방문합니다.

// visitedSet엔 두 명의 사용자가 저장될 겁니다.

// John의 방문 여부를 확인해보겠습니다.
alert(visitedSet.has(john)); // true

// Mary의 방문 여부를 확인해보겠습니다.
alert(visitedSet.has(mary)); // false

john = null;

// visitedSet에서 john을 나타내는 객체가 자동으로 삭제됩니다.
profile
정리하는 개발자
post-custom-banner

0개의 댓글