Set 객체는 중복되지 않는 유일한 값들의 집합. 배열과 유사하지만 차이가 있다.
구분 | 배열 | Set 객체 |
---|---|---|
동일한 값을 중복하여 포함 할 수 있다. | O | X |
요소 순서에 의미가 있다. | O | X |
인덱스로 요소에 접근할 수 있다. | O | X |
이러한 Set 객체의 특성은 수학적 집합의 특성과 일치. Set은 수학적 집합을 구현하기 위한 자료구조.
Set 생성자 함수에 인수를 전달하지 않으면 빈 Set 객체가 생성.
const set = new Set();
console.log(set); // Set(0) {}
Set 생저 함수는 이터러블을 인수로 전달 받아 Set 객체를 생성. 이때 이터러블의 중복된 값은 Set 객체에 요소로 저장 x
const set1 = new Set([1, 2, 3, 3]);
console.log(set1); // Set(3) {1, 2, 3}
const set2 = new Set('hello');
console.log(set2); // Set(4) {"h", "e", "l", "o"}
// 배열의 중복 요소 제거
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]
const { size } = new Set([1, 2, 3, 3]);
console.log(size); // 3
size 프로퍼티는 setter 함수없이 getter 함수만 존재하는 접근자 프로퍼티 숫자를 할당하여 객체 요소 개수 변경 x
const set = new Set([1, 2, 3]);
console.log(Object.getOwnPropertyDescriptor(Set.prototype, 'size'));
// {set: undefined, enumerable: false, configurable: true, get: ƒ}
set.size = 10; // 무시된다.
console.log(set.size); // 3
const set = new Set();
console.log(set); // Set(0) {}
set.add(1);
console.log(set); // Set(1) {1}
add메서드를 연속적으로 호출할 수 있음.
const set = new Set();
set.add(1).add(2).add(2);
console.log(set); // Set(2) {1, 2}
중복 요소는 추가 허용 x 에러는 발생하지않고 무시.
일치비교 연산자를 사용하면 NaN과 NaN을 다르다고 평가함. 하지만 Set 객체는 NaN과 NaN을 같다고 평가하여 중복 추가를 허용x +0 -0은 일치비교 연산자와 마찬가지로 같다고 평가하여 중복추가 x
const set = new Set();
console.log(NaN === NaN); // false
console.log(0 === -0); // true
// NaN과 NaN을 같다고 평가하여 중복 추가를 허용하지 않는다.
set.add(NaN).add(NaN);
console.log(set); // Set(1) {NaN}
// +0과 -0을 같다고 평가하여 중복 추가를 허용하지 않는다.
set.add(0).add(-0);
console.log(set); // Set(2) {NaN, 0}
Set 객체에서 NaN과 NaN이 같다고 평가하는 이유
Set 객체는 자바스크립트의 일반적인 동치성 규칙을 따르지만, Set 객체는 자바스크립트의 일반적인 동치성 규칙을 따르지만, NaN과 NaN을 같은 것으로 취급하는 이유는 해당 객체가 내부적으로 동치성 비교를 위해 SameValueZero 알고리즘을 사용하기 때문.
SameValueZero 알고리즘은 자바스크립트의 동치 비교 알고리즘 중 하나로, NaN 값을 처리하는 방법이 정의되어 있습니다. 이 알고리즘은 두 값이 NaN인 경우에 동일하다고 판단.
Set 객체는 내부적으로 SameValueZero 알고리즘을 사용하여 요소들을 비교. 따라서 Set 객체에 NaN이 중복해서 추가되더라도 중복된 값으로 처리되지 않고 하나의 값으로 취급.
이러한 동작은 Set 객체에서 동일한 값을 고유하게 유지하고자 하는 목적에 부합하며, NaN 값을 제거하지 않고 유지하고자 할 때 유용.
function sameValueZero(x, y) {
// 두 값이 일치하거나 둘 다 NaN인 경우
if (x === y || (Number.isNaN(x) && Number.isNaN(y))) {
// true 반환
return true;
}
// 그 외의 경우 false 반환
return false;
}
console.log(sameValueZero(1, 1)); // true
console.log(sameValueZero(1, "1")); // false
console.log(sameValueZero(NaN, NaN)); // true
console.log(sameValueZero(0, -0)); // true
console.log(sameValueZero(-0, 0)); // true
SameValueZero
Set 객체는 객체나 배열과 같이 자바스크립트의 모든값을 요소로 저장 가능.
const set = new Set();
set
.add(1)
.add('a')
.add(true)
.add(undefined)
.add(null)
.add({})
.add([]);
console.log(set); // Set(7) {1, "a", true, undefined, null, {}, []}
const set = new Set([1, 2, 3]);
console.log(set.has(2)); // true
console.log(set.has(4)); // false
delete 메서드에는 인덱스가 아니라 삭제하려는 요소값을 인수로 전달해야함 . 순서에 의미가 없음 = 인덱스 x
const set = new Set([1, 2, 3]);
// 요소 2를 삭제한다.
set.delete(2);
console.log(set); // Set(2) {1, 3}
// 요소 1을 삭제한다.
set.delete(1);
console.log(set); // Set(1) {3}
존재하지 않는 객체의 Set 객체의 요소를 삭제하려 하면 에러없이 무시.
const set = new Set([1, 2, 3]);
// 존재하지 않는 요소 0을 삭제하면 에러없이 무시된다.
set.delete(0);
console.log(set); // Set(3) {1, 2, 3}
delete메서드는 add메서드와 달리 불리언 값을 반환하기에 연속적 호출이 안됨.
- 주어진 요소가 Set 객체에 존재하는지 확인.
- 요소가 존재하면 해당 요소를 제거하고, Set 객체의 크기를 감소.
- 제거가 성공적으로 이루어졌으면 true를 반환.
- 요소가 존재하지 않아 제거할 수 없는 경우 false를 반환.
언제나 undifined를 반환
const set = new Set([1, 2, 3]);
set.clear();
console.log(set); // Set(0) {}
Set 객체의 요소를 순회하려면 Set.prototype.forEach() 메서드 사용. 콜백함수와 forEach 메서드의 콜백 함수 내부에서 this로 사용될 객체를 인수로 전달. 3개의 인수를 전달받음.
- 첫번째 인수 : 현재 순회중인 요소값
- 두번째 인수 : 현재 순회중인 요소값
- 세번째 인수 : 현재 순회중인 Set 객체 자체
mdn
첫번째 인수와 두번째 인수는 같은값 .Array.prototype.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}
*/
Map 객체는 키와 값의 쌍으로 이루어진 컬렉션. 객체와 유사하지만 다음과 같은 차이.
구분 | 객체 | Map 객체 |
---|---|---|
키로 사용할 수 있는 값 | 문자열 또는 심벌값 | 객체를 포함한모든 값 |
이터러블 | X | O |
요소 개수 확인 | Object.keys(obj).length | map.size |
Map 객체는 Map생성자 함수로 생성. Set과 동일
const map = new Map();
console.log(map); // Map(0) {}
Map 생성자 함수는 이터러블을 인수로 전달받아 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
Map객체에선 중복된 값이 중복되면 덮어씌워짐. 중복된 키를 가질 수 없다.
Map.prototype.size프로퍼티 사용.
const { size } = new Map([['key1', 'value1'], ['key2', 'value2']]);
console.log(size); // 2
Set객체와 동일. getter - o / setter -x
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.prototype.set 메서드.
const map = new Map();
console.log(map); // Map(0) {}
map.set('key1', 'value1');
console.log(map); // Map(1) {"key1" => "value1"}
set 메서드는 새로운 요소가 추가된 Map 객체를 반환. 연속적호출 x
const map = new Map();
map
.set('key1', 'value1')
.set('key1', 'value2');
console.log(map); // Map(1) {"key1" => "value2"}
값이 덮어써진다.
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' }
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
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
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"}
연속적 호출 x
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
const lee = { name: 'Lee' };
const kim = { name: 'Kim' };
const map = new Map([[lee, 'developer'], [kim, 'designer']]);
map.clear();
console.log(map); // Map(0) {}
Set과 동일 forEach
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 객체는 이터러블.
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"]