on your mark

[JAVASCRIPT.INFO] 5.7 맵과 셋 본문

WEB/Javascript

[JAVASCRIPT.INFO] 5.7 맵과 셋

dev-olive 2023. 1. 9. 22:15

5.7 맵과 셋

키가 있는 데이터를 저장한다는 점에서 객체와 유사하지만 은 키에 다양한 자료형을 허용한다는 점에서 차이가 있다.

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

map[key]=2로 값을 설정할 수는 있으나, map을 일반 객체 취급하는 것이기 때문에 여러 제약이 생긴다. 따라서 map 을 사용할 때에는 set, get을 사용해야 한다.

맵은 키로 객체를 허용한다

let john = {name: "John"};
let visitsCountMap = new Map();
visitCountMap.set(john, 123);

map.set을 호출할 때마다 맵 자신이 반환되기 때문에, 이를 이용하면 map.set을 체이닝할 수 있다.

map.set('1', 'str1')
     .set(1, 'num1')
     .set(true, 'bool1');

맵의 요소에 반복 작업하기

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

  • map.keys() - 각 요소의 키를 모은 반복 가능한(iterable, 이터러블) 객체를 반환한다.
  • map.values() - 각 요소의 값을 모은 이터러블 객체를 반환한다.
  • map.entries() - 요소의 [키, 값]을 한 쌍으로 하는 이터러블 객체를 반환한다. 이 이터러블 객체는 for .. of 반복문의 기초로 쓰인다.
let recipeMap = new Map({
  ['cucumber', 500],
  ['tomatoes', 350],
  ['onion', 50]
});

for(let vegetable of recipeMap.keys()){
  alert(vegetable);        // cucumber, tomatoes, onion
}
for(let amount of recipeMap.values()){
  alert(amount);            // 500, 350, 50
}
for(let entry of recipeMap) {
  alert(entry);                // cucumber,500 ...
}

맵은 삽입 순서를 기억하기 때문에 값이 삽입된 순서대로 순회를 실시한다. (객체가 프로퍼티 순서를 기억하지 못하는 것과 차이가 있다.)

맵은 배열과 유사하게 내장 메서드 forEach를 지원한다.

recipeMap.forEach((value, key, map) => {
  console.log(`${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를 사용하면 맵을 객체로 바꿀 수 있다. 이 메서드는 각 요소가 [key, value] 쌍인 배열을 객체로 바꿔준다.

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

// 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);
let obj = Object.fromEntries(map.entries());        // 맵을 일반 객체로 변환

alert(obj.orange);        // 2

map.entries()를 호출하면 맵의 [key, value]를 요소로 가지는 이터러블을 반환한다. (Object.fromEntries를 사용하기 위한 형태)

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

셋 내에 동일한 값이 있다면 set.add(value)를 아무리 많이 호출하더라도 반응이 없다.

let set = new Set();
let john = {name: "John"};
let pete = {name: "Pete"};
let mary = {name: "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){
  console.log(user.name);        // John, Pete, Mary
}

셋의 값에 반복 작업하기

for .. offorEach를 사용하면 셋의 값을 대상으로 반복 작업을 수행할 수 있다.

let set = new Set(["oranges", "apples", "bananas"]);
for(let value of set) alert(value);

set.forEach((value, valueAgain, set) => {
  alert(value);
})

forEach에 쓰인 콜백함수는 세 개의 인수를 받는데, 첫 번째는 value, 두 번째도 같은 값인 valueAgain을 받는다. 동일한 값이 인수에 두 번 등장한다. 이렇게 구현된 이유는 Map과의 호환성 때문이다. MapforEach에 쓰인 콜백이 세 개의 인수를 받을 때를 위해 위와 같이 구현되어 있다.