본문 바로가기

Map과 Set

1. Map

1.1 Map 이란?

Map 객체는 키-값 쌍을 저장할 수 있는 구조입니다. 여기서 키는 어떤 타입도 될 수 있습니다. Map은 각 키와 값의 쌍을 유지하며, 객체보다 유연하고 강력한 기능을 제공합니다.

let m = new Map();
console.log(m); // Map(0) {}

Map 문법은 ES6에 추가된 문법입니다. 다만 객체와 그 용도가 비슷하기 때문에 그동안 객체를 사용해왔던 개발자들은 Map을 잘 모르거나 사용하지 않는 경우가 많습니다. 하지만 Map은 객체보다 더 많은 기능을 제공하며, 객체보다 더 효율적으로 데이터를 다룰 수 있습니다. 이제는 충분히 성숙된 문법이므로, 안심하고 사용하셔도 됩니다.

위니북스에서 Map과 Set 전체가 출력되지 않습니다.

위니북스는 바로 뒤 챕터에서 배울 JSON.stringify를 사용하여 객체를 출력하기 때문에 Map과 Set은 출력되지 않습니다. 따라서 Map과 Set을 사용할 때는 위니북스가 아닌 콘솔 환경에서 테스트하세요.

다만 위니북스에서 쉽게 확인할 수 있도록 ...(스프레드 문법)을 사용하여 Map과 Set을 배열로 변환하여 출력하도록 해놓았습니다.

이렇게 굳이 위니북스에서 실행되게 해놓은 이유는 결국 실무에서도 비슷한 작업을 여러분이 해야하기 때문입니다.

1.2 Map에 값 추가하기

Map에 값을 추가할 때는 set 메서드를 사용합니다. 객체는 문자열만 키로 사용할 수 있지만, Map은 어떤 타입도 키로 사용할 수 있습니다. 아래 코드를 실행시켜 출력되는 값을 확인해보고 여러 타입의 값을 키로 활용해보세요.

let m = new Map();

m.set('하나', '1');
m.set(1, '하나');
m.set(true, 1);
m.set(false, 0);

m = [...m] // 위니북스에서만 이렇게 사용

console.log(m);

원래 map의 형태는 아래와 같이 출력됩니다. 콘솔에서 확인해보세요.

Map(4) {
  "하나" => "1",
  1 => "하나",
  true => 1,
  false => 0
}

1.3 Map에서 값 가져오기

Map에서 값을 가져올 때는 get 메서드를 사용합니다.

let m = new Map();
m.set('하나', '1');
m.set(true, 1);

console.log(m.get('하나')); // '1'
console.log(m.get(true)); // 1

1.4 Map에 값이 있는지 확인하기

Map에 특정 키가 있는지 확인할 때는 has 메서드를 사용합니다. 객체에서 사용했던 in연산자와 같은 역할을 하며 만약 map에서 in연산자를 사용하면 의도한 것처럼 동작하지 않으니 주의해주세요.

let m = new Map();
m.set('하나', '1');

console.log(m.has('하나')); // true
console.log(m.has(2)); // false

1.5 Map에서 값 제거하기

Map에서 값을 제거할 때는 delete 메서드를 사용합니다.

let m = new Map();
m.set('하나', '1');

console.log(m.delete('하나')); // true
console.log(m.has('하나')); // false

1.6 Map의 크기 확인하기

객체에서는 크기를 확인할 수 있는 속성이 없었으나 Map에서는 크기를 확인할 때는 size 속성을 사용할 수 있습니다.

let m = new Map();
m.set('하나', '1');
m.set(1, '하나');

console.log(m.size); // 2

1.7 Map의 모든 데이터 삭제하기

Map의 모든 데이터를 삭제할 때는 clear 메서드를 사용합니다.

let m = new Map();
m.set('하나', '1');
m.set(1, '하나');

m.clear();
console.log(m.size); // 0

1.8 Map 순회하기

Map은 다양한 방법으로 순회할 수 있습니다. for...of 반복문을 사용하면 키와 값을 쉽게 접근할 수 있습니다.

const data = new Map().set('name', 'licat').set('age', 10).set('height', 180);

for (const value of data) {
  console.log(value[0]);
  console.log(value[1]);
}

for (const [key, value] of data) {
  console.log(key);
  console.log(value);
}

1.9 Map의 키와 값에 접근하기

객체는 키와 값에 접근할 때 Object.keys, Object.values 메서드를 사용했지만, Map의 키와 값에 각각 접근할 때는 keys와 values 메서드를 사용합니다.

const data = new Map().set('name', 'hojun').set('age', 10).set('height', 180);

console.log([...data.keys()]); // ["name", "age", "height"]
for (const key of data.keys()) {
  console.log(key);
}

console.log([...data.values()]); // ["hojun", 10, 180]
for (const value of data.values()) {
  console.log(value);
}

Map의 keys, values 메서드는 컬렉션의 요소를 순회할 수 있게 하는 객체인 이터레이터를 반환합니다.

이터레이터(Iterator)란?
이터레이터는 컬렉션의 요소를 순회할 수 있게 하는 객체입니다. 이터레이터는 next 메서드를 가지고 있으며, next 메서드를 호출할 때마다 컬렉션의 요소를 순차적으로 반환합니다. 이터레이터는 Symbol.iterator 메서드를 가지고 있어야 하며, 이 메서드를 호출하면 이터레이터를 반환합니다.

const arr = [1, 2, 3];
const iter = arr[Symbol.iterator]();
console.log(iter.next()); // {value: 1, done: false}
console.log(iter.next()); // {value: 2, done: false}
console.log(iter.next()); // {value: 3, done: false}
console.log(iter.next()); // {value: undefined, done: true}

1.10 배열과 Map의 변환

배열을 Map으로 변환하거나, 그 반대로 변환할 수 있습니다.

// 배열을 Map으로 변환
let temp = new Map([
  [1, 10],
  [2, 20],
  [3, 30],
  [4, 40],
]);
temp = [...temp]; // 위니북스에서만 이렇게 사용
console.log(temp); // Map(4) {1 => 10, 2 => 20, 3 => 30, 4 => 40}

// 객체를 Map으로 변환
let temp2 = new Map(Object.entries({ one: 1, two: 2 }));
temp2 = [...temp2]; // 위니북스에서만 이렇게 사용
console.log(temp2); // Map(2) {"one" => 1, "two" => 2}

// Map을 객체로 변환
const temp3 = Object.fromEntries(temp2);
console.log(temp3); // { one: 1, two: 2 }

1.11 Map과 Object의 차이

Map 객체와 Object는 모두 키-값 쌍을 저장하지만, 몇 가지 중요한 차이점이 있습니다.

  1. Object의 키는 문자열과 심볼 타입만 가능하지만, Map의 키는 모든 값을 가질 수 있습니다.
  2. Object는 크기를 수동으로 계산해야 하지만, Map은 size 속성을 통해 쉽게 크기를 확인할 수 있습니다.
// 1. Map의 키
let objMap = new Map();
objMap.set('one', 1);
objMap.set(2, 'two');
objMap.set(true, 3);

objMap = [...objMap]; // 위니북스에서만 이렇게 사용
console.log(objMap); // Map(3) {"one" => 1, 2 => "two", true => 3}

// 2. 크기 계산
let obj = { hi: 1, hi2: 2, hi3: 3, hi4: 4 };
console.log(Object.keys(obj).length); // 4

console.log(objMap.size); // 3
  1. Map은 데이터를 추가하거나 제거하는 작업에서 Object 보다 더 나은 성능을 보입니다.

2. Set

2.1 Set 이란?

Set 객체는 중복되지 않는 값들의 집합을 저장할 수 있는 구조입니다. Set은 각 값을 유일하게 유지하며, 중복된 값을 허용하지 않습니다. Set은 Map과 같이 ES6에 추가된 문법입니다. 역시나 충분히 성숙된 문법이니 안심하고 사용하셔도 됩니다.

let s = new Set();
console.log(s); // Set(0) {}

2.2 Set에 값 추가하기

Set에 값을 추가할 때는 add 메서드를 사용합니다.

let s = new Set();

s.add('apple');
s.add('banana');
s.add('orange');

s = [...s]; // 위니북스에서만 이렇게 사용

console.log(s); // Set(3) {"apple", "banana", "orange"}

중복을 허용하는지 테스트 해보도록 하겠습니다.

let s = new Set();
s.add(1);
s.add(1);
s.add(2);
s.add(2);
s.add(3);

s = [...s]; // 위니북스에서만 이렇게 사용

console.log(s); // Set(3) {1, 2, 3}

아래처럼 문자열을 바로 Set으로 변경하여 중복을 허락하는지 테스트 해볼 수도 있습니다.

let s = new Set('abcdeeeeeeeee');
s = [...s]; // 위니북스에서만 이렇게 사용
console.log(s);
console.log(s.size);

2.3 Set에서 값 확인하기

Set에 특정 값이 있는지 확인할 때는 has 메서드를 사용합니다.

let s = new Set(['apple', 'banana', 'orange']);

console.log(s.has('apple')); // true
console.log(s.has('grape')); // false

2.4 Set에서 값 제거하기

Set에서 값을 제거할 때는 delete 메서드를 사용합니다.

let s = new Set(['apple', 'banana', 'orange']);

console.log(s.delete('banana')); // true
console.log(s.has('banana')); // false

2.5 Set의 크기 확인하기

Set의 크기를 확인할 때는 size 속성을 사용합니다.

let s = new Set(['apple', 'banana', 'orange']);

console.log(s.size); // 3

2.6 Set의 모든 데이터 삭제하기

Set의 모든 데이터를 삭제할 때는 clear 메서드를 사용합니다.

let s = new Set(['apple', 'banana', 'orange']);

s.clear();
console.log(s.size); // 0

2.7 Set 순회하기

Set은 다양한 방법으로 순회할 수 있습니다. for...of 반복문을 사용하면 값을 쉽게 접근할 수 있습니다.

let s = new Set(['apple', 'banana', 'orange']);

for (let value of s) {
  console.log(value);
}

2.8 배열과 Set의 변환

배열을 Set으로 변환하거나, 그 반대로 변환할 수 있습니다.

// 배열을 Set으로 변환
let arr = ['apple', 'banana', 'orange', 'banana'];
let s = new Set(arr);
console.log(s); // Set(3) {"apple", "banana", "orange"}

// Set을 배열로 변환
let newArr = [...s];
console.log(newArr); // ["apple", "banana", "orange"]

2.9 Set을 이용한 중복 제거

Set을 사용하면 배열의 중복된 값을 쉽게 제거할 수 있습니다.

let arr = ['apple', 'banana', 'orange', 'banana'];
let uniqueArr = [...new Set(arr)];
console.log(uniqueArr); // ["apple", "banana", "orange"]

2.10 Set의 교집합, 합집합, 차집합

Set을 사용하여 교집합, 합집합, 차집합을 구할 수 있습니다.

let setA = new Set(['apple', 'banana', 'orange']);
let setB = new Set(['banana', 'kiwi', 'orange']);

// 교집합
let intersection = new Set([...setA].filter(x => setB.has(x)));
intersection = [...intersection]; // 위니북스에서만 이렇게 사용
console.log(intersection); // Set(2) {"banana", "orange"}

// 합집합
let union = new Set([...setA, ...setB]);
union = [...union]; // 위니북스에서만 이렇게 사용
console.log(union); // Set(4) {"apple", "banana", "orange", "kiwi"}

// 차집합
let difference = new Set([...setA].filter(x => !setB.has(x)));
difference = [...difference]; // 위니북스에서만 이렇게 사용
console.log(difference); // Set(1) {"apple"}

3. 연습문제

// 문제 : 회사 게시판에는 총 6개의 게시물이 있고, 각각의 게시물에 게시자 데이터를 뽑은 것이 '회사게시판 변수이다.'
const 회사게시판 = ['라이캣', '라이캣', '라이캣', '빙키', '소울곰', '뮤라'];

// 문제 1 : 몇 명이 게시판에 게시물을 썼나요? (인원이 유일해야 합니다.)
let set = new Set(회사게시판);
set.size;

// 문제 2 : 각각 몇 개의 게시물을 작성하였나요?
for (const i of set) {
  console.log(
    i,
    회사게시판.filter((e) => e === i),
  );
}

for (const i of set) {
  console.log(i, 회사게시판.filter((e) => e === i).length);
}

let map = new Map();
for (const i of 회사게시판) {
  map.set(i, (map.get(i) || 0) + 1); // 순회 돌면서 해당 key값을 업데이트 해주는 식으로
}

// 위 코드 설명
map.set('라이캣', (map.get('라이캣') || 0) + 1);
map.set('라이캣', (map.get('라이캣') || 0) + 1);
map.set('라이캣', (map.get('라이캣') || 0) + 1);
map.set('빙키', (map.get('빙키') || 0) + 1);
//...