카테고리 없음

[자바스크립트] Map

Map

JS

이번에는 JS 에서 지원하는 자료구조인 Map에 대해서 살펴보려고 합니다. Map의 특징과 여러가지 메서드를 살펴보면서 정리해보도록 하겠습니다.

 

Map 객체는 키와 값이라는 쌍으로 이루어진 컬렉션입니다. Map 은 객체와 비슷하지만 아래와 같은 차이가 있습니다.

 

1. 키로 객체를 포함한 모든 값을 사용할 수 있다.

2. 이터러블이다.

3. map.size 로 요소 개수의 확인이 가능하다.

Map 객체 생성해보기

Map객체는 Map생성자 함수로 생성합니다. 만약 인수를 전달하지 않으면 빈 Map객체가 생성됩니다.

const map = new Map();
console.log(map); // Map(0) {}

 

Map 생성자 함수는 인수로 이터러블을 받습니다. 인수로 전달되는 이터러블은 키와 값의 쌍으로 구성되어야 합니다. 주의할 점은 만약 중복된 키를 가지는 요소가 존재한다면 뒤의 값으로 덮어 써집니다.

const map1 = new Map([['key1', 'value1'], ['key2', 'value2']]);
console.log(map1); // Map(2) {"key1" => "value1", "key2" => "value2"}

const map2 = new Map([1, 2]); // TypeError: Iterator value 1 is not an entry object

// 중복된 키가 있으면 덮어 써진다.
const map = new Map([['key1', 'value1'], ['key1', 'value2']]);
console.log(map); // Map(1) {"key1" => "value2"}

요소 개수 확인해보기

Map객체의 요소 개수를 확인하고 싶다면 Map.prototype.size 프로퍼티를 사용하면 됩니다. size 프로퍼티는 getter 함수만 존재하는 접근자 프로퍼티입니다. 따라서 size 프로퍼티에 숫자를 할당해서 size 개수를 변경하려는 시도는 무시됩니다.

const { size } = new Map([['key1', 'value1'], ['key2', 'value2']]);
console.log(size); // 2

const map = new Map([['key1', 'value1'], ['key2', 'value2']]);

console.log(Object.getOwnPropertyDescriptor(Map.prototype, 'size'));
// {set: undefined, enumerable: false, configurable: true, get: ƒ}

map.size = 10; // 무시된다.
console.log(map.size); // 2

요소 추가해보기

Map 객체에 요소를 추가하려면 Map.prototype.set메서드를 사용하면 됩니다. set메서드는 새로운 요소가 추가된 Map객체를 반환하기 때문에 set메서드를 호출한후 메서드 체이닝(메서드를 연속적으로 호출)이 가능합니다.

const map = new Map();
console.log(map); // Map(0) {}

map.set('key1', 'value1');
console.log(map); // Map(1) {"key1" => "value1"}

const map = new Map();

map
  .set('key1', 'value1')
  .set('key2', 'value2');

console.log(map); // Map(2) {"key1" => "value1", "key2" => "value2"}

 

일치 비교 연산자를 사용해서 NaN 과 NaN 을 비교하면 false 로 평가합니다. 하지만 Map객체에서는 NaN 과 NaN 을 같다고 평가하여 중복 추가를 허용하지 않습니다. 다른 예로는 -0 과 +0 또한 중복 추가를 허용하지 않습니다.

const map = new Map();

console.log(NaN === NaN); // false
console.log(0 === -0); // true

// NaN과 NaN을 같다고 평가하여 중복 추가를 허용하지 않는다.
map.set(NaN, 'value1').set(NaN, 'value2');
console.log(map); // Map(1) { NaN => 'value2' }

// +0과 -0을 같다고 평가하여 중복 추가를 허용하지 않는다.
map.set(0, 'value1').set(-0, 'value2');
console.log(map); // Map(2) { NaN => 'value2', 0 => 'value2' }

 

객체와 Map 객체의 가장 큰 차이는 프로퍼티 키로 문자열과 심볼만 사용한 것이 아닌 객체를 포함한 모든 값을 키로 사용할 수 있다는 점입니다.

const map = new Map();

const lee = { name: 'Lee' };
const kim = { name: 'Kim' };

// 객체도 키로 사용할 수 있다.
map
  .set(lee, 'developer')
  .set(kim, 'designer');

console.log(map);
// Map(2) { {name: "Lee"} => "developer", {name: "Kim"} => "designer" }

요소 취득하기

Map 객체에서 특정 요소의 존재 여부를 확인해 보려면 Map.prototype.get 메서드를 사용하면됩니다. get 메서드는 인수로 전달한 키가 존재하면 키가 가지는 값을 반환합니다. 만약 존재하지 않으면 undefined 를 반환합니다.

const map = new Map();

const lee = { name: 'Lee' };
const kim = { name: 'Kim' };

map
  .set(lee, 'developer')
  .set(kim, 'designer');

console.log(map.get(lee)); // developer
console.log(map.get('key')); // undefined

요소 존재 여부 확인하기

Map 객체에 특정 요소가 존재하는지 확인하고 싶으면 Map.prototype.has 메서드를 사용합니다. has 메서드는 특정 요소가 존재 하는지를 나타내는 불리언 값을 반환합니다.

const lee = { name: 'Lee' };
const kim = { name: 'Kim' };

const map = new Map([[lee, 'developer'], [kim, 'designer']]);

console.log(map.has(lee)); // true
console.log(map.has('key')); // false

요소 삭제해보기

Map 객체에서 특정 요소를 삭제하려면 Map .prototype.delete 메서드를 사용합니다. delete 메서드는 삭제 성공 여부를 나타내는 불리언 값을 리턴합니다. 만약 존재하지 않는 키로 Map 객체의 요소를 삭제하려고 하면 에러없이 무시됩니다.

const lee = { name: 'Lee' };
const kim = { name: 'Kim' };

const map = new Map([[lee, 'developer'], [kim, 'designer']]);

map.delete(kim);
console.log(map); // Map(1) { {name: "Lee"} => "developer" }

// 존재하지 않는 키로 삭제하려는 경우
const map = new Map([['key1', 'value1']]);

// 존재하지 않는 키 'key2'로 요소를 삭제하려 하면 에러없이 무시된다.
map.delete('key2');
console.log(map); // Map(1) {"key1" => "value1"}

 

set 메서드와 다른 점은 delete 메서드가 삭제 성공 여부를 나타내는 불리언 값을 반환한다는 점입니다. 이 때문에 map 은 메서드 체이닝이 불가능합니다.

const lee = { name: 'Lee' };
const kim = { name: 'Kim' };

const map = new Map([[lee, 'developer'], [kim, 'designer']]);

map.delete(lee).delete(kim); // TypeError: map.delete(...).delete is not a function

요소 일괄 삭제해보기

만약 Map객체의 모든 요소들을 일괄 삭제하고 싶다면 Map.prototype.clear 메서드를 사용하면 됩니다. 언제나 undefined 를 반환합니다.

const lee = { name: 'Lee' };
const kim = { name: 'Kim' };

const map = new Map([[lee, 'developer'], [kim, 'designer']]);

map.clear();
console.log(map); // Map(0) {}

요소 순회하기

Map 객체에서 요소를 순회하고 싶다면 Map.prototype.forEach 메서드를 사용하면 됩니다. 이는 Map.prototype.forEach 메서드와 유사하게 콜백 함수와 forEach 메서드의 콜백 함수 내부에서 this로 사용할 객체를 인수로 전달합니다. 아래와 같은 3개의 인수를 전달받습니다.

 

1. 현재 순회 중인 요소값

2. 현재 순회 중인 요소키

3. 현재 순회 중인 Map 객체 자체

const lee = { name: 'Lee' };
const kim = { name: 'Kim' };

const map = new Map([[lee, 'developer'], [kim, 'designer']]);

map.forEach((v, k, map) => console.log(v, k, map));
/*
developer {name: "Lee"} Map(2) {
  {name: "Lee"} => "developer",
  {name: "Kim"} => "designer"
}
designer {name: "Kim"} Map(2) {
  {name: "Lee"} => "developer",
  {name: "Kim"} => "designer"
}
*/

 

Map 객체는 이터러블이므로 for...of 사용과 스프레드 문법, 배열 디스터럭처링 할당의 대상이 되는것이 가능합니다.

const lee = { name: 'Lee' };
const kim = { name: 'Kim' };

const map = new Map([[lee, 'developer'], [kim, 'designer']]);

// Map 객체는 Map.prototype의 Symbol.iterator 메서드를 상속받는 이터러블이다.
console.log(Symbol.iterator in map); // true

// 이터러블인 Map 객체는 for...of 문으로 순회할 수 있다.
for (const entry of map) {
  console.log(entry); // [{name: "Lee"}, "developer"]  [{name: "Kim"}, "designer"]
}

// 이터러블인 Map 객체는 스프레드 문법의 대상이 될 수 있다.
console.log([...map]);
// [[{name: "Lee"}, "developer"], [{name: "Kim"}, "designer"]]

// 이터러블인 Map 객체는 배열 디스트럭처링 할당의 대상이 될 수 있다.
const [a, b] = map;
console.log(a, b); // [{name: "Lee"}, "developer"]  [{name: "Kim"}, "designer"]

Map.prototype.keys, Map.prototype.values, Map.prototype.entries 

Map 객체는 이터러블이면서 동시에 이터레이터인 객체를 반환할 수 있는 메서드를 제공합니다.

1. Map.prototype.keys

Map 객체에서 요소키를 값으로 가지는 이터러블이면서 동시에 이터레이터인 객체를 반환합니다.

const lee = { name: 'Lee' };
const kim = { name: 'Kim' };

const map = new Map([[lee, 'developer'], [kim, 'designer']]);

// Map.prototype.keys는 Map 객체에서 요소키를 값으로 갖는 이터레이터를 반환한다.
for (const key of map.keys()) {
  console.log(key); // {name: "Lee"} {name: "Kim"}
}

2. Map.prototype.values

Map 객체에서 요소값을 값으로 가지는 이터러블이면서 동시에 이터레이터인 객체를 반환합니다.

// Map.prototype.values는 Map 객체에서 요소값을 값으로 갖는 이터레이터를 반환한다.
for (const value of map.values()) {
  console.log(value); // developer designer
}

3. Map.prototype.entries

Map 객체에서 요소키와 요소값을 값으로 가지는 이터러블이면서 동시에 이터레이터인 객체를 반환합니다.

// Map.prototype.entries는 Map 객체에서 요소키와 요소값을 값으로 갖는 이터레이터를 반환한다.
for (const entry of map.entries()) {
  console.log(entry); // [{name: "Lee"}, "developer"]  [{name: "Kim"}, "designer"]
}

마무리

이번에는 Map자료구조에 대해서 알아봤습니다. Map 은 기존의 객체와는 다르게 프로퍼티 키를 객체를 포함한 모든 값으로 사용 가능하다는 장점이 있습니다.

 

보다 더 자세한 내용은 아래 링크에서 확인 가능하고, 저 또한 내용들을 공부하며 정리하기 위한 목적으로 글을 남기는 것을 알려드립니다. 읽어주셔서 감사합니다!

 

출처 : https://poiemaweb.com/