Set 객체는 중복되지 않는 유일한 값들의 집합이고 배열과 유사하지만 차이가 있다.
구분 | 배열 | Set 객체 |
---|---|---|
동일한 값을 중복하여 포함할 수 있다. | O | X |
요소 순서에 의미가 있다. | O | X |
인덱스로 요소에 접근할 수 있다. | O | X |
const set = new Set();
console.log(set); // Set(0) {}
const set1 = new Set([1, 2, 3, 3]);
console.log(set1); // Set(3) {1, 2, 3}
const uniq = array => [...new Set(array)];
console.log(uniq([2, 1, 2, 4, 3, 3])); // [2, 1, 4, 3]
lenth
와 다르게 Set.prototype.size
프로퍼티를 통해 요소의 개수를 확인할 수 있다.const { size } = new Set([1, 2, 3]);
console.log(size) // 3
size
프로퍼티는setter
함수 없이getter
함수만 존재하는 접근자 프로퍼티다.
그러므로size
프로퍼티에 숫자 할당하여 변경 불가하다.const set = new Set([1, 2, 3]); console.log(set.size) // 3 set.size = 10; // 무시 console.log(set.size) // 3
Set.prototype.add
메서드 사용하여 요소 추가 가능하며, 연속적으로 호출도 가능하다.const set = new Set([1, 2, 3]);
set.add(4); // Set(4) {1, 2, 3, 4}
set.add(5).add(6); // Set(6) {1, 2, 3, 4, 5, 6}
set.add(1).add(2); // Set(6) {1, 2, 3, 4, 5, 6}
const set = new Set();
console.log(NaN === NaN); // false
// Set에서는 NaN과 NaN을 같다고 평가하여 중복 추가를 허용하지않는다.
const a = { id: 1 };
const deepA = { ...a };
console.log(a === deepA); // false
set.add(a).add(deepA);
// Set(2) {{…}, {…}}
// Set에서는 깊은 복사는 다르다고 평가하여 중복을 추가할 수 있습니다.
Set.prototype.delete
메서드를 사용하여 요소 삭제 가능하다.delete
메서드는 삭제 성공 여부를 나타내는 불리언 값을 반환한다.delete
메서드에는 인덱스가 아니라 요소 값을 인수로 전달해야 한다.(인덱스가 없기에 Set 객체는 순서에 의미가 없다.) const set = new Set([1, 2, 3]);
set.delete(2);
console.log(set); // Set(2) {1, 3}
// 존재하지 않는 요소 삭제하면 에러없이 무시된다.
set.delete(0);
console.log(set); // Set(2) {1, 3}
// delete는 불리언 값을 반환한다.
set.delete(3); // true
set.delete(0); // false
clear
메서드를 사용한다.const set = new Set([1, 2, 3]);
set.clear();
console.log(set); // Set(0) {size: 0}
Set.prototype.forEach
메서드를 사용하여 요소를 순회할 수 있다. Array.prototype.forEach
메서드와 비슷하지만 두 번째 인수가 다르다.첫 째 인수: 현재 순회 중인 요소값
두 번째 인수: 현재 순회 중인 요소값
세 번째 인수: 현재 순회 중인 Set 객체 자체
Array.prototype.forEach
메서드와 인터페이스를 통일하기 위함이며 다른 의미는 없다. 배열에서 forEach
의 두 번째 인수는 인덱스를 전달 받지만, 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}
for...of 문
을 사용할 수 있고, 스프레드 문법과 배열 디스트럭처링도 가능하다.const set = new Set([1, 2, 3]);
// 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 객체는 수학적 집합을 구현하기 위한 자료구조다. 밑에서 Set으로 구현해보자.
Set.prototype.intersection = function (set) {
const result = new Set();
for (const value of set) {
if (this.has(value)) result.add(value);
}
return result;
};
const setA = new Set([1, 2, 3, 4]);
const setB = new Set([2, 4]);
// setA와 setB의 교집합
console.log(setA.intersection(setB)); // Set(2) {2, 4}
// setB와 setA의 교집합
console.log(setB.intersection(setA)); // Set(2) {2, 4}
또 다른 방법
Set.prototype.intersection = function (set) {
return new Set([...this].filter(v => set.has(v)));
};
// setA와 setB의 교집합
console.log(setA.intersection(setB)); // Set(2) {2, 4}
// setB와 setA의 교집합
console.log(setB.intersection(setA)); // Set(2) {2, 4}
Set.prototype.union = function (set) {
return new Set([...this, ...set]);
};
// setA와 setB의 합집합
console.log(setA.union(setB)); // Set(4) {1, 2, 3, 4}
// setB와 setA의 합집합
console.log(setB.union(setA)); // Set(4) {2, 4, 1, 3}
Set.prototype.difference = function (set) {
return new Set([...this].filter(v => !set.has(v)));
};
// setA와 setB의 차집합
console.log(setA.difference(setB)); // Set(2) {1, 3}
// setB와 setA의 차집합
console.log(setB.difference(setA)); // Set(0) {}
집합 A가 집합 B에 포함되는 경우 집합 A는 집합 B의 부분집합이며,
집합 B가 집합 A의 상위 집합이다.
Set.prototype.isSuperset = function (subset) {
const supersetArr = [...this];
return [...subset].every(v => supersetArr.includes(v));
}
// setA가 setB의 상위집합인지 확인
console.log(setA.difference(setB)); // true
// setB가 setA의 상위집합인지 확인
console.log(setB.difference(setA)); // false
Map 객체는 키와 값의 쌍으로 이루어진 컬렉션이다. Map 객체는 객체와 유사하지만 차이가 있다.
구분 | 객체 | Map 객체 |
---|---|---|
키로 사용할 수 있는 값 | 문자열 또는 심벌 값 | 객체를 포함한 모든 값 |
이터러블 | X | O |
요소 개수 확인 | Object.keys(obj).length | map.size |
Map 생성자 함수는 이터러블을 인수로 전달받아 Map 객체를 생성한다. 이때 인수로 전달되는 이터러블은 키와 값의 쌍으로 이루어진 요소로 구성되어야 한다.
const map1 = new Map([['key1', 'value1'], ['key2', 'value2']]);
console.log(map1); // Map(2) {"key" => "value1", "key2" => "value2"}
const map2 = new Map([1, 2]) // 에러
Map 생성자 함수의 인수로 전달한 이터러블에 중복된 키를 갖는 요소가 존재하면 값이 덮어써진다. 따라서 Map 객체에는 중복된 키를 갖는 요소가 존재할 수 없다.
const map = new Map([['key1', 'value'], ['key1', 'value']]); console.log(map);// Map(1) {"key" => "value1"}
Map.prototype.size
프로퍼티를 사용해 요소개수를 확인할 수 있다.const { size } = new Map([['key1', 'value1'], ['key2', 'value2']]);
console.log(size); // 2
size
프로퍼티에 숫자 할당하여 변경 불가하다.Map.prototype.set
메서드를 사용해 요소를 추가할 수 있다.const map = new Map();
map.set('key1', 'value1');
console.log(map); // Map(1) {"key1" => "value1"}
map
.set('key2', 'value2')
.set('key3', 'value3')
.set('key3', 'value4');
console.log(map); // Map(3) {"key1" => "value1", "key2" => "value2", "key3" => "value4"}
```js
const map = new Map();
console.log(NaN === NaN); // false
// Map에서 NaN과 NaN을 같다고 평가하여 중복 추가를 허용하지않는다.
map.set(0, 'value1').set(-0, 'value2');
console.log(map); // Map(1) {0 => 'value2'}
객체는 문자열 또는 심벌만 키로 사용할 수 있다. 하지만 Map 객체는 키 타입에 제한이 없다. 따라서 객체를 포함한 모든 값을 키로 사용할 수있다는 것이 객체와 가장 큰 차이점이다.
const map = new Map();
const park = { name: 'Park' };
const kim = { name: 'Kim' };
map
.set(park, 'developer')
.set(kim, 'designer');
console.log(map);
// Map(2) { {name: "Park"} => "developer", {name: "Kim"} => "designer" }
Map.prototype.get
메서드를 사용해 특정 요소를 취득할 수 있다. get
메서드의 인수로 키를 전달하면 Map 객체에서 인수로 전달한 키를 갖는 값을 반환한다.(존재하지않으면 undefined
반환)const map = new Map();
const park = { name: 'Park' };
const kim = { name: 'Kim' };
map
.set(park, 'developer')
.set(kim, 'designer');
console.log(map.get(park)); // developer
console.log(map.get("lee")); // undefined
Map.prototype.has
메서드를 사용해 특정 요소의 존재 확인 가능하다.const map = new Map();
const park = { name: 'Park' };
const kim = { name: 'Kim' };
map
.set(park, 'developer')
.set(kim, 'designer');
console.log(map.has(park)); // true
console.log(map.get("lee")); // false
Map.prototype.delete
메서드를 사용해 요소를 삭제할 수있다.delete
메서드는 삭제 성공 여부를 나타내는 불리언 값으로 반환한다.const park = { name: 'Park' };
const kim = { name: 'Kim' };
const map = new Map([[park, "developer"], [kim, "designer"]]);
map.delete(kim).delete(park); // 에러 !
map.delete(kim); // true
console.log(map); // Map(1) { {name: "Park"} => "developer" }
map.delete("lee"); // false 존재하지 않는 키로 삭제 => 무시
console.log(map); // Map(1) { {name: "Park"} => "developer" }
Map.prototype.clear
메서드를 사용해 요소를 일괄 삭제할 수있다.const park = { name: 'Park' };
const kim = { name: 'Kim' };
const map = new Map([[park, "developer"], [kim, "designer"]]);
map.clear();
console.log(map); // Map(0) {}
Map.prototype.forEach
메서드를 사용하여 요소를 순회할 수 있다.첫 째 인수: 현재 순회 중인 요소값
두 번째 인수: 현재 순회 중인 요소키
세 번째 인수: 현재 순회 중인 Map 객체 자체
const park = { name: 'Park' };
const kim = { name: 'Kim' };
const map = new Map([[park, "developer"], [kim, "designer"]]);
map.forEach((v, k, map) => console.log(v, k, map));
console.log(map);
/*
developer {name: 'Park'} Map(2) {{…} => 'developer', {…} => 'designer'}
designer {name: 'Kim'} Map(2) {{…} => 'developer', {…} => 'designer'}
*/
Map 객체는 이터러블이기에
for...of 문
으로 순회할 수 있고, 스프레드 문법과 배열 디스트럭처링도 가능하다.
const park = { name: 'Park' };
const kim = { name: 'Kim' };
const map = new Map([[park, "developer"], [kim, "designer"]]);
// for..of
for (const entry of map) {
console.log(entry);
}
// 스프레드
cosnole.log([...map]);
// 디스트럭쳐링
const [a, b] = map;
Map 객체는 이터러블이면서 동시에 이터레이터인 객체를 반환하는 메서드를 제공한다.
const park = { name: 'Park' };
const kim = { name: 'Kim' };
const map = new Map([[park, "developer"], [kim, "designer"]]);
// Map.prototype.keys는 Map 객체에서 요소의 키를 값으로 갖는 이터레이터 반환한다.
for (const entry of map.keys()) {
console.log(entry); // { name: 'Park'} {name: 'Kim'}
}
// Map.prototype.values는 Map 객체에서 요소값을 값으로 갖는 이터레이터 반환한다.
for (const entry of map.values()) {
console.log(entry); // developer designer
}
// Map.prototype.entries는 Map 객체에서 요소키와 요소소값을 값으로 갖는 이터레이터를 반환한다.
const check = []
for (const entry of map.entries()) {
check.push(entry); // [{name: 'Park'}, 'developer'] [{name: 'Kim'}, 'designer']
}
const iterator = check[Symbol.iterator]();
iterator.next()
// {value: Array(2), done: false}
iterator.next()
// {value: Array(2), done: false}
iterator.next()
// {value: undefined, done: true}
위에서 배운 Set 객체도 이터러블이면서 동시에 이터레이터인 객체를 반환하는 메서드를 제공한다.
Map 객체는 요소의 순서에 의미를 갖지 않지만 Map 객체를 순회하는 순서는 요소가 추가된 순서를 따른다.??