WeniVooks

검색

JavaScript 에센셜

Map과 Set

이번 챕터에서 배우는 개념은 익스플로러 10을 지원해야 하는 상황이라면 사용하지 못합니다. (Babel을 사용하면 사용 가능하지만 프로젝트에 따라 Babel을 사용하지 않을 수도 있습니다.)

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
}
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의 키와 값에 각각 접근할 때는 keysvalues 메서드를 사용합니다.

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);
//...
// 문제 : 회사 게시판에는 총 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);
//...
8.1 this8.3 JSON