37 Set과 Map

이재철·2021년 9월 24일
0

javaScript

목록 보기
14/19
post-thumbnail

Set

  • Set 객체는 중복되지 않는 유일한 값들의 집합
  • 배열과 유사
구분배열Set 객체
동일한 값을 중복하여 포함 여부OX
요소 순서에 의미OX
인덱스 요소에 접근 여부OX

Set 객체 생성

  • Set 객체는 생성자 함수로 생성
    • 인수를 전달하지 않으면 빈 Set 객체 생성
    • Set 생성자 함수는 이터러블을 인수로 전달받아 Set 객체 생성 ( 중복된 값은 저장하지 않음 )
const set = new Set();
console.log(set); // Set(0) {}

const set1 = new Set([1, 2, 3, 4]);
console.log(set1); // Set(3) {1, 2, 3}

const set2 = new Set('hello');
console.log(set2); // Set(4) {"h", "e", "l", "o"}
  • 중복을 허용하지 않는 Set 객체의 특성을 활용하여 배열의 중복된 요소를 제거
// 배열의 중복 요소 제거
const uniq = array => array.filter((v, i, self) => self.indexOf(v) === i);
console.log(uniq([2, 1, 2, 3, 4, 3, 4])); // [2, 1, ,3 ,4]

// Set을 이용한 배열 중복 요소 제거
const uniq = array => [...new Set(array)];
console.log(uniq([2, 1, 2, 3, 4, 3, 4])); // [2, 1, ,3 ,4]

요소 개수 확인

  • Set 객체의 요소 개수를 확인할 때 Set.prototype.size 프로퍼티 사용
    • size 프로퍼티는 setter함수 없이 getter함수만 있기 때문에 요소 개수를 변경 불가
const { size } = new Set([1, 2, 3, 4]);
console.log(size); // 3

// ex2)
const set = new Set([1, 2, 3]);
set.size = 10; // 무시됨
console.log(set.size); // 3 

요소 추가

  • Set 객체에 요소를 추가할 때 Set.prototype.add 메서드 사용
    • add 메서드는 새로운 요소가 추가된 Set 객체를 반환 -> 연속적 호출 가능(method chaining)
    • 중복된 요소의 추가 불가 (무시됨)
    • 일치 비교 연산자(===)을 사용하면 NaN과 NaN을 같다고 평가되어 중복추가 불가능
    • +0과 -0 같다고 평가되어 중복추가 불가능
    • 객체나 배열과 같이 자바스크립트의 모든 값을 요소로 저장가능
const set = new Set();
console.log(set); // Set(0) {}

set.add(1).add(2)
console.log(set) // Set(2) {1, 2}

set.add(1).add(2).add(2) // 중복요소 무시
console.log(set) // Set(2) {1, 2}

// ex2)
const set2 = new Set();
console.log(NaN === NaN); // false
console.log(0 === -0); // true

//중복 추가 허용 불가
set2.add(NaN).add(NaN); 
console.log(set2); // Set(1) {NaN}

set2.add(0).add(-0);
console.log(set2); // Set(2) {NaN, 0}

// ex3)
const set3 = new Set();

set3
 .add(1)
 .add('a')
 .add(true)
 .add(undefined)
 .add(null)
 .add({})
 .add([])
 .add(() => {});

console.log(set3); // Set(8) {1, "a", true, undefiend, null, {}, [], () -> {}}

요소 존재 여부 확인

  • 특정요소 존재 여부 확인 시 Set.prototype.has 메서드 사용
const set = new Set([1, 2, 3]);
console.log(set.has(2)); // true
console.log(set.has(4)); // false

요소 삭제

  • 특정요소를 삭제하려면 Set.prototype.delete 메서드 사용 (성공여부 불리언 값을 반환)
    • 인덱스가 아닌 삭제하려는 요소값을 인수로 전달 -> 순서에 의미가 없음, 인덱트x
    • 성공 여부를 불리언 값을 반환 -> 연속적 호출 불가능(method chaining)
const set = new Set([1, 2, 3]);
set.delete(2); // 요소 2 삭제
console.log(set); // Set(2) {1,3}


set.delete(1); // 요소 1 삭제
console.log(set); // Set(1) {3}

set.delete(0); // 존재하지 않는 요소 0을 삭제하면 에러 없이 무시
console.log(set); // Set(1) {3}

set.delete(1).delete(2); // TypeError: set.delete(...) delete is not a function

요소 일괄 삭제

  • 객체의 모든 요소를 일괄 삭제 Set.prototype.clear 메서드 사용
    • clear 메서드는 언제나 undefined를 반환
const set = new Set([1, 2, 3]);
set.clear();
console.log(set); // Set(0) {}

요소 순회

  • 객체의 요소를 순회하려면 Set.prototype.forEach 메서드를 사용
    • Set 객체는 순서에 의미가 없어 인덱스x
    • Set 객체는 이터러블 -> for...of 문으로 순회 가능, 스프레드 문법과 배열 디스트럭처링의 대상
인수
첫번째현재 순회 중인 요소값
두번째현재 순회 중인 요소값
세번째현재 순회 중인 Set 객체 자체
const set = new Set([1, 2, 3]);
set.forEach((v, v2, set) => console.log(v, v2, set));
/*
1 1 Set(3) {1, 2, 3}
2 2 Set(3) {1, 2, 3}
3 3 Set(3) {1, 2, 3}
*/

// ex2)

const set = new Set([1, 2, 3]);

// Set 객체는 Symbol.iterator 메서드를 상속받는 이터러블
console.log(Symbol.iterator in set); // true

// 이터러블 Set 객체는 for...of 문으로 순회
for (const value of set) {
 console.log(value); // 1 2 3 
}

// 스프레드 문법의 대상 
console.log([...set]); // [1, 2, 3]

//디스트럭처링 할당의 대상
const [a, ...rest] = set;
console.log(a, rest); // 1, [2,3]

집합 연산

  • Set 객체를 통해 교집합, 합집합, 차집합 등을 구현 가능
// ex1) 교집합 (집합 A와 집합 B의 공통요소)
Set.prototype.intersection = function (set) {
 return new Set([...this].filter(v => set.has(v))); 
}

const setA = new Set([1, 2, 3, 4]);
const setB = new Set([2, 4]);

console.log(setA.intersection(setB)); // Set(2) {2, 4}
console.log(setB.intersection(setA)); // Set(2) {2, 4}


// ex2) 합집합 (집합 A와 집합 B의 중복 없는 모든 요소)
Set.prototype.union = function (set) {
  return new Set([...this, ...set]);
}

const setA = new Set([1, 2, 3, 4]);
const setB = new Set([2, 4]);

console.log(setA.union(setB)); // Set(2) {1, 2, 3, 4}
console.log(setB.union(setA)); // Set(2) {1, 2, 3, 4}

// ex3) 차집합 (집합 A에는 존재하지만 집합 B에는 존재하지 않는 요소)
Set.prototype.difference = function (set) {
 return new Set([...this].filter(v => !set.has(v)));
}

const setA = new Set([1, 2, 3, 4]);
const setB = new Set([2, 4]);

console.log(setA.difference(setB)); // Set(2) {3}
console.log(setB.difference(setA)); // Set(0) {}

// ex4) 부분 집합과 상위 집합 (집합 A가 집합 B에 포함, 집합 A는 집합 B의 부분 집합이며, 집합 B는 집합 A의 상위 집합)
Set.prototype.isSuperset = function (subset) {
 const supersetArr = [...this];
 return [...subset].every(v => supersetArr.includes(v)); 
}


const setA = new Set([1, 2, 3, 4]);
const setB = new Set([2, 4]);

console.log(setA.isSuperset(setB)); // true
console.log(setB.isSuperset(setA)); // false

Map

  • 키와 값의 쌍으로 이루어진 컬렉션, 객체와 유사
    • 인수로 전달되는 이터러블은 키와 값의 쌍으로 이루어진 요소로 구성되어야 함
    • 중복된 키를 갖는 요소가 존재하면 덮어씀 -> 중복 키를 갖는 요소 불가능
구분객체Map 객체
키로 사용할 수 있는 값문자열 또는 심벌 값객체를 포함한 모든 값
이터러블XO
요소 개수Object.keys(obj).lengthmap.size

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

// ex2)
const map = new Map([['key1', 'value1'], ['key1', 'value2']]);
console.log(map); // Map(1) {"key1" => "value2"}

요소 개수 확인

  • Map.prototype.size 프로퍼티 사용
    • setter 함수 없이 getter 함수만 존재하는 접근자 프로퍼티 -> 숫자를 할당하여 Map 요소 개수 변경 불가
console.log(size); // 2

const map = new Map([['key1', 'value1'], ['key2', 'value2']]);
map.size = 10; // 무시
console.log(map.size); // 2

요소 추가

  • Map.prototype.set 메서드 사용
    • 연속적 호출(method chaining)가능
    • 일치 비교 연산자(===)을 사용하면 NaN과 NaN를 같다고 평가하여 중복 추가 불가능
    • +0과 -0를 같다고 평가하여 중복 추가 불가능
    • 키 타입에 제한이 없음 -> 객체를 포함한 모든 값을 키로 사용 가능
const map = new Map();
map
  .set('key1', 'value1')
  .set('key2', 'value2');
  .set('key2', 'value3'); // 중복된 키 값 요소는 덮어씀(에러 발생x)
console.log(map); // Map(2) {"key1" => "value1", "key2" => "value2"}


const map = new Map();
map.set(NaN, 'value1').set(NaN, 'value2');
console.log(map); // Map(1) {"NaN" => "value2"}

map.set(0, 'value1').set(-0, 'value2');
console.log(map); // Map(2) {"NaN" => "value2", 0 => "value"}


// ex2) 

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.prototype.get 메서드 사용
    • Map 객체에서 인수로 전달한 키를 갖는 요소가 존재하지 않으면 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.prototype.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.prototype.delete 메서드 사용
    • 삭제 성공 여부를 나타내는 불리언 값을 반환
    • 존재하지 않는 키로 Map 객체의 요소를 삭제하면 에러 없이 무시
    • set 메서드와 달리 연속적으로 호출 불가능
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.delete(kim);
map.delete('key2');
console.log(map); // Map(1) { {name: "Lee"} => "developer" }

요소 일괄 삭제

  • Map.prototype.clear 메서드 사용
    • 언제나 undefiend 반환
const lee = { name : 'Lee' };
const kim = { name : 'Kim' };
const map = new Map([[lee, 'developer'], [kim, 'designer']]);
map.clear();
console.log(map); // Map(0) {}

요소 순회

  • 객체의 요소를 순회하려면 Map.prototype.forEach 메서드를 사용
    • Map 객체는 이터러블 -> for...of 문으로 순회 가능, 스프레드 문법과 배열 디스트럭처링의 대상
    • Map 객체는 요소의 순서에 의미를 갖지 않지만 Map 객체를 순회하는 순서는 요소가 추가된 순서를 따름 (이터러블의 순회와 호환성 유지)
인수
첫번째현재 순회 중인 요소값
두번째현재 순회 중인 요소값
세번째현재 순회 중인 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",
    }
*/

// ex2)
const lee = { name: 'Lee' };
const kim = { name: 'Kim' };
const map =new Map([[lee, 'developer'], [kim, 'designer']]);

for (const entry of map) {
 console.log(entry); // [{name: "Lee"}, "developer"],[{name: "Kim"}, "designer"]
}

// 스프레드 문법
console.log([...map]); // [{name: "Lee"}, "developer"],[{name: "Kim"}, "designer"]

// 배열 디스트럭처링 할당
const [a, b] = map;
console.log(a, b); // [{name: "Lee"}, "developer"],[{name: "Kim"}, "designer"]
Map 메서드설명
Map.prototype.keysMap 객체에서 요소키를 값으로 갖는 이터러블, 이터레이터인 객체 반환
Map.prototype.valuesMap 객체에서 요소값을 값으로 갖는 이터러블, 이터레이터인 객체 반환
Map.prototype.entriesMap 객체에서 요소키와 요소값를 값으로 갖는 이터러블, 이터레이터인 객체 반환
const lee = { name: 'Lee' };
const kim = { name: 'Kim' };

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

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

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

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

📖 참고도서 : 모던 자바스크립트 Deep Dive 자바스크립트의 기본 개념과 동작 원리 / 이웅모 저 | 위키북스

profile
혼신의 힘을 다하다 🤷‍♂️

0개의 댓글