객체와 배열은 아래와 같은 특징을 가지고 있다.
객체와 배열이 가지고 있는 특징만으로 현실 세계를 반역하기에 어려움이 많아 Set과 Map이 등장했다.
: 유니크한 배열 만들기.
배열은 중복을 허락하면서 원하는 모든 값을 넣을 수 있다.
하지만 Set은 유니크한 값만 넣을 수 있다.
중복없이 유일한 값을 저장하려고 할때, 이미 존재하는지 체크할 때 유용하다.
let mySet = new Set();
mySet.add("Sarang");
mySet.add("Sojin");
mySet.add("Sarang");
mySet.forEach((v) => console.log(v));
// Sarang, Sojin (중복된 "Sarang" 은 하나만 나온다.)
console.log(mySet.has("Sarang"));
// true (.has를 이용하여 존재 체크 가능하다.)
mySet.delete("Sarang");
mySet.forEach((v) => console.log(v));
// Sojin (.delete를 이용하여 삭제가 가능하다.)
const set = new Set([1, 2, 3]);
for (const value of set) {
console.log(value);
// 1 2 3
// Set 객체는 for...of 문으로 순회할 수 있다.
}
console.log([...set]);
// [1, 2, 3]
// Set 객체는 스프레드 문법의 대상이 될 수 있다.
const [a, ...rest] = [...set];
console.log(a, rest);
// 1, [2, 3]
// Set 객체는 배열 디스트럭처링 할당의 대상이 될 수 있다.
추가학습자료
1. Set을 사용한 배열의 중복 요소 제거
const uniq = array => [...new Set(array)];
console.log(uniq([2, 1, 2, 3, 4, 3, 4]));
// [2, 1, 3, 4]
const setA = new Set([1, 2, 3, 4]);
const setB = new Set([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}
const setA = new Set([1, 2, 3, 4]);
const setB = new Set([2, 4]);
Set.prototype.union = function (set) {
return new Set([...this].filter(v => set.add(v)));
};
// 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}
const setA = new Set([1, 2, 3, 4]);
const setB = new Set([2, 4]);
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) {}
const setA = new Set([1, 2, 3, 4]);
const setB = new Set([2, 4]);
Set.prototype.isSuperset = function (subset) {
const supersetArr = [...this];
return [...subset].every(v => supersetArr.includes(v));
};
// setA가 setB의 상위 집합인지 확인한다.
console.log(setA.isSuperset(setB));
// true
// setB가 setA의 상위 집합인지 확인한다.
console.log(setB.isSuperset(setA));
// false
const SETTING = {
name: "LUCKY LOTTO!",
count: 6,
maxNumber: 45,
};
const { count, maxNumber } = SETTING;
// destructuring을 이용하여 필요 값만 사용
function getRandomNumber(count, maxNumber) {
let numberArray = [];
for (let i = 1; i <= maxNumber; i++) {
numberArray.push(i);
}
let set = new Set();
while (set.size <= count - 1) {
let randomNumber = Math.floor(Math.random() * maxNumber + 1);
set.add(randomNumber);
}
// 중복값을 제외하는 Set의 기능을 이용하여 6개의 Size를 채워준다.
set.forEach((v) => console.log(v));
console.log(set.values());
}
getRandomNumber(count, maxNumber);
객체 형태(만)를 중복없이 저장하려고 할때 유용하다.
let ws = new WeakSet();
let arr = [1, 2, 3, 4];
ws.add(arr);
console.log(ws);
// WeakSet {Array(4)}
ws.add("111");
ws.add(111);
ws.add(null);
console.log(ws);
// Error : Invalid value used in weak set
ws.add(function () {});
console.log(ws);
// WeakSet {Array(4), ƒ}
let ws = new WeakSet();
let arr = [1, 2, 3, 4];
let arr2 = [5, 6, 7, 8];
let obj = { arr, arr2 };
ws.add(arr);
ws.add(arr2);
ws.add(obj);
console.log(ws);
// WeakSet {Array(4), Array(4), {arr:Array(4), arr2:Array(4)}}
arr = null;
console.log(ws);
// WeakSet {Array(4), Array(4), {arr:Array(4), arr2:Array(4)}}
console.log(ws.has(arr), ws.has(arr2));
// false true
// 가비지가 된 arr을 has하고 있지 않다고 나온다.
Set과 유사하지만 ket & value를 함께 사용 한다는 점에서 차이가 있다.
(Set은 value만 사용 즉, Set은 배열에 많이 사용되고 Map은 객체에 많이 사용된다.)
<맵의 주요 메서드>
new Map() – 맵을 만듭니다.
map.set(key, value) – key를 이용해 value를 저장합니다.
map.get(key) – key에 해당하는 값을 반환합니다. key가 존재하지 않으면 undefined를 반환합니다.
map.has(key) – key가 존재하면 true, 존재하지 않으면 false를 반환합니다.
map.delete(key) – key에 해당하는 값을 삭제합니다.
map.clear() – 맵 안의 모든 요소를 제거합니다.
map.size – 요소의 개수를 반환합니다.
let map = new Map();
map.set('1', 'str1'); // 문자형 키
map.set(1, 'num1'); // 숫자형 키
map.set(true, 'bool1'); // 불린형 키
alert( map.get(1) ); // 'num1'
alert( map.get('1') ); // 'str1'
// 키로 객체도 허용한다.
let john = { name: "John" };
// 고객의 가게 방문 횟수를 세본다고 가정해 봅시다.
let visitsCountMap = new Map();
// john을 맵의 키로 사용하겠습니다.
visitsCountMap.set(john, 123);
alert( visitsCountMap.get(john) ); // 123
객체는 키를 문자형으로 변환한다.
맵은 키의 타입을 변환시키지 않고 그대로 유지한다.
map.keys() – 각 요소의 키를 모은 반복 가능한(iterable, 이터러블) 객체를 반환합니다.
map.values() – 각 요소의 값을 모은 이터러블 객체를 반환합니다.
map.entries() – 요소의 [키, 값]을 한 쌍으로 하는 이터러블 객체를 반환합니다. 이 이터러블 객체는 for..of반복문의 기초로 쓰입니다.
let recipeMap = new Map([
['cucumber', 500],
['tomatoes', 350],
['onion', 50]
]);
// 키(vegetable)를 대상으로 순회합니다.
for (let vegetable of recipeMap.keys()) {
alert(vegetable); // cucumber, tomatoes, onion
}
// 값(amount)을 대상으로 순회합니다.
for (let amount of recipeMap.values()) {
alert(amount); // 500, 350, 50
}
// [키, 값] 쌍을 대상으로 순회합니다.
for (let entry of recipeMap) { // recipeMap.entries()와 동일합니다.
alert(entry); // cucumber,500 ...
}
WeakSet과 마찬가지로 Map의 기능에서 참조를 가지고 있는 객체만을 다룬다.
let wm = new WeakMap();
let myFun = function () {};
wm.set(myFun, 0); // key : myFun value : 0
console.log(wm);
// WeakMap {ƒ => 0}
let count = 0;
for (let i = 0; i < 10; i++) {
count = wm.get(myFun); // key myFun의 value값 0
count++;
wm.set(myFun, count);
}
console.log(wm.get(myFun));
// 10
myFun = null; // myFun을 함수에서 null로 변경
console.log(wm.get(myFun));
// undefined