40일차 - 맵과 셋

이상민·2024년 10월 24일

TIL

목록 보기
42/50

맵(map)

맵(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 – 요소의 개수를 반환합니다.
let map = new Map();

map.set('1', 'str1');   // 문자형 키
map.set(1, 'num1');     // 숫자형 키
map.set(true, 'bool1'); // 불린형 키

// 객체는 키를 문자형으로 변환한다.
// 맵은 키의 타입을 변환시키지 않고 그대로 유지합니다. 따라서 아래의 코드는 출력되는 값이 다릅니다.
alert( map.get(1)   ); // 'num1'
alert( map.get('1') ); // 'str1'

alert( map.size ); // 3

맵은 키로 객체를 허용한다.

let john = { name: "John" };

// 고객의 가게 방문 횟수를 세본다고 가정해 봅시다.
let visitsCountMap = new Map();

// john을 맵의 키로 사용하겠습니다.
visitsCountMap.set(john, 123);

alert( visitsCountMap.get(john) ); // 123

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

객체형 키를 객체에 써보자.

let john = { name: "John" };

let visitsCountObj = {}; // 객체를 하나 만듭니다.

visitsCountObj[john] = 123; // 객체(john)를 키로 해서 객체에 값(123)을 저장해봅시다.

// 원하는 값(123)을 얻으려면 아래와 같이 키가 들어갈 자리에 `object Object`를 써줘야합니다.
alert( visitsCountObj["[object Object]"] ); // 123

맵의 요소에 반복 작업하기

다음 세 가지 메서드를 사용해 맵의 각 요소에 반복 작업을 할 수 있습니다.

  • map.keys() – 각 요소의 키를 모은 반복 가능한(iterable, 이터러블) 객체를 반환.

  • map.values() – 각 요소의 값을 모은 이터러블 객체를 반환.

  • map.entries() – 요소의 [키, 값]을 한 쌍으로 하는 이터러블 객체를 반환한다. 이 이터러블 객체는 for..of반복문의 기초로 쓰인다.

    예시)

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 ...
}

여기에 더하여 맵은 배열과 유사하게 내장 메서드 forEach도 지원한다.

// 각 (키, 값) 쌍을 대상으로 함수를 실행
recipeMap.forEach( (value, key, map) => {
  alert(`${key}: ${value}`); // cucumber: 500 ...
});

Object.entries: 객체를 맵으로 바꾸기

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

// 각 요소가 [키, 값] 쌍인 배열
let map = new Map([
  ['1',  'str1'],
  [1,    'num1'],
  [true, 'bool1']
]);

alert( map.get('1') ); // str1

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

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

let map = new Map(Object.entries(obj));

alert( map.get('name') ); // John

Object.fromEntries: 맵을 객체로 바꾸기

이번에는 맵을 객체로 바꾸는 방법에 대해 알아보자. Object.fromEntries를 사용하여 각 요소가 [키, 값] 쌍인 배열을 객체로 바꿔보자.

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

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

alert(prices.orange); // 2
let map = new Map();
map.set('banana', 1);
map.set('orange', 2);
map.set('meat', 4);
// map.entries()를 호출하면 맵의 [키, 값]을 요소로 가지는 이터러블을 반환하게 된다.
let obj = Object.fromEntries(map.entries()); // 맵을 일반 객체로 변환 (*)

// 맵이 객체가 되었습니다!
// obj = { banana: 1, orange: 2, meat: 4 }

alert(obj.orange); // 2

셋(set)

셋(Set)은 중복을 허용하지 않는 값을 모아놓은 특별한 컬렉션으로, 셋에 키가 없는 값이 저장된다.

주요 메서드는 다음과 같다.

  • new Set(iterable) – 셋을 만듭니다. 이터러블 객체를 전달받으면(대개 배열을 전달받음) 그 안의 값을 복사해 셋에 넣어줍니다.
  • set.add(value) – 값을 추가하고 셋 자신을 반환합니다.
  • set.delete(value) – 값을 제거합니다. 호출 시점에 셋 내에 값이 있어서 제거에 성공하면 true, 아니면 false를 반환합니다.
  • set.has(value) – 셋 내에 값이 존재하면 true, 아니면 false를 반환합니다.
  • set.clear() – 셋을 비웁니다.
  • set.size – 셋에 몇 개의 값이 있는지 세줍니다.

방문자 방명록을 만든다고 가정해 보자. 한 방문자가 여러 번 방문해도 방문자를 중복해서 기록하지 않겠다고 결정 내린 상황이다. 즉, 한 방문자는 '단 한 번만 기록’되어야 한다.

이때 적합한 자료구조가 바로 셋(set)이다.

let set = new Set();

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

// 어떤 고객(john, mary)은 여러 번 방문할 수 있습니다.
set.add(john);
set.add(pete);
set.add(mary);
set.add(john);
set.add(mary);

// 셋에는 유일무이한 값만 저장됩니다.
alert( set.size ); // 3

for (let user of set) {
  alert(user.name); // // John, Pete, Mary 순으로 출력됩니다.
}

셋(set) 대신 배열을 사용하여 방문자 정보를 저장한 후, 중복 값 여부는 배열 메서드인 arr.find를 이용해 확인할 수도 있지만 arr.find는 배열 내 요소 전체를 뒤져 중복 값을 찾기 때문에, 셋보다 성능 면에서 떨어진다.

셋의 값에 반복 작업하기

셋에도 맵과 마찬가지로 반복 작업을 위한 메서드가 있는데,
for..of나 forEach를 사용하면 셋의 값을 대상으로 반복 작업을 수행하게 할 수 있다.

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

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

// forEach를 사용해도 동일하게 동작합니다.
set.forEach((value, valueAgain, set) => {
  alert(value);
});

0개의 댓글