
많은 데이터를 효율적으로 탐색하고자 하는 요구에서 만들어진 자료구조
해시함수를 사용해 변환된 값을 인덱스로 삼아 키와,값을 저장해 빠른 데이터 탐색을 제공하는 자료구조
ㄴ 최종적으로 얻고자 하는 정보는 전화번호(value), 값을 검색할때 활용하는 정보는 이름(Key) / 그 사이에 해시함수가 있음. 해시함수는 키를 고정길이 해시코드(인덱스)로 치환해 값을 찾을 수 있게 해줌
// key를 받아 고유한 새 주소를 return하는 해시함수
hash(key){
let id = 0;
for(let i = 0; i < key.length; i++){
id += key.charCodeAt(i) * (i + 1);
}
return id % this.size;
}

Hash Table: 해시함수로 생성된 키와 대응한 값이 저장돼있는 공간
Bucket: 해시테이블의 각 데이터
특징:
단방향으로 동작 : 키→값 방향으로만 탐색 가능, 값을 통해 키를 찾을 수는 없음) ⇒ 외부에 정보를 안전하게 제공해 보안에 많이 활용됨O(1) : 키가 해시함수에 의해 바로 값이 있는 인덱스가 돼 별도로 값을 찾는 과정 필요X활용처
JS에선 Map으로 해시를 구현한다
Map과 Set은 기존 배열과 객체의 한계를 극복하기 위해 도입된 자료구조라고 할 수 있다.
: 해시 테이블을 기반으로 한 자료구조로 키가 있는 데이터를 저장한다는 점에서 객체와 비슷함,
반면 키에 다양한 자료형 허용(객체처럼 키를 string으로 변환하지 않음), 키의 삽입 순서를 기억함, 이터러블 객체임, 하나의 Map에서 키는 유일한 값임
const map = new Map([iterable]) // Map 생성. 키-값쌍이 있는 iterable 객체를 선택적으로 넘겨 맵 초기화에 사용 가능
map.set(key, value) // key를 이용해 value를 저장 후 map 자신 반환->메서드 체이닝 가능
map.get(key) // key에 해당하는 값을 반환. key가 존재하지 않으면 undefined를 반환. O(1)-해시테이블을 사용해 상수시간에 처리됨
map.has(key) // key가 존재하면 true, 존재하지 않으면 false를 반환 O(1)
map.delete(key) // key에 해당하는 값을 삭제. 성공시 true 실패시 false 반환 O(1)
map.clear() // 맵 안의 모든 요소를 제거, undefined 반환
map.size // 요소의 개수 반환
map.keys() // 키를 반복 가능한(iterable) 객체로 반환
map.values() // 값을 이터러블 객체로 반환
map.entries() // [키, 값]쌍을 가진 이터러블 객체를 반환
map.forEach(callbackFn,[, thisArg]) // 키-값 쌍에 대해 삽입 순서대로 callbackFn을 한 번씩 호출, thisArg 넣으면 각 콜백의 this 값으로 사용, undefined 반환 O(N)
특징
Map에서는 SameValueZero 알고리즘으로 (NaN과 NaN을 같다고 봄, 나머지 값은 === 연산자와 유사) 값의 일치 여부 확인함 -> 키를 비교하는 방식임
- 키로 다양한 자료형 허용
// 💡 객체를 키로 사용 가능
// 고객의 가게 방문 횟수
let visitsCountMap = new Map();
let john = { name: "John" };
visitsCountMap.set(john, 123);
console.log( visitsCountMap.get(john) ); // 123 출력
// 객체를 사용했다면?
let visitsCountObj = {};
visitsCountObj[john] = 123; // 객체는 모든 키를 string type으로 변환시킴. 이 과정에서 john은 문자형으로 변환되어 "[object Object]"가 됨
console.log( visitsCountObj["[object Object]"] ); // 123 출력
// 💡 NaN을 키로 사용 가능
// Map은 NaN과 NAN을 같다고 판단한다고 했음
const myMap = new Map();
myMap.set(NaN, "not a number");
myMap.get(NaN); // "not a number"
const otherNaN = Number("foo");
myMap.get(otherNaN); // "not a number"
```
for...of, forEach() 로 순회 가능
맵은 이터러블 객체이고 키-값쌍이 삽입된 순서를 기억하므로 키의 삽입 순서대로 순회 가능
const myMap = new Map();
myMap.set(0, "zero");
myMap.set(1, "one");
// [키,값] 쌍을 대상으로 순회
for (const [key, value] of myMap) {
console.log(`${key} = ${value}`);
}
// 0 = zero 출력
// 1 = one 출력
for (const [key, value] of myMap.entries()) {
console.log(`${key} = ${value}`);
}
// 0 = zero 출력
// 1 = one 출력
// Map()생성자 함수는 entries()메서드와 동일하게 키-값쌍의 이터레이터를 반환하도록 구현되어 있음
// entries()를 명시적으로 호출하는 방식은 조금 더 명확하게 의도를 드러내지만, 결과는 같음
// 키를 대상으로 순회
for (const key of myMap.keys()) {
console.log(key);
}
// 0 출력
// 1 출력
// 값을 대상으로 순회
for (const value of myMap.values()) {
console.log(value);
}
// zero 출력
// one 출력
// 배열과 유사하게 내장 메서드 forEach도 지원함
myMap.forEach((value, key, myMap) => {
console.log(`${key} = ${value}`);
});
// 0 = zero 출력
// 1 = one 출력
객체, 배열(키,값쌍을 가진 2차원 배열)로 Map 생성 / Map으로 객체, 배열 생성 가능
const kvArr = [
["key1", "value1"],
["key2", "value2"],
];
const obj= {
key1: "value1",
key2: "value2",
};
// 키,값쌍을 가진 2차원 배열을 Map으로 만듦
const mapFromArr = new Map(kvArr);
console.log(mapFromArr.get("key1")); // value1 출력
// 객체를 Map으로 만듦
const mapFromObj = new Map(Object.entries(obj)); // Object.entries로 객체 obj를 배열 [["name","John"], ["age", 30]]로 바꾸고, 이 배열로 새로운 맵을 만듦
console.log(mapFromObj.get("key1")); // value1 출력
console.log(mapFromArr===mapFromObj); // false - 내용은 같지만 메모리 주소(참조값)가 다름
// Map을 배열로 만듦
console.log(Array.from(mapFromArr));
console.log([...mapFromArr]);
// [ [ 'key1', 'value1' ], [ 'key2', 'value2' ] ] - kvArray와 같은 내용 출력
// Map을 객체로 만듦
console.log(Object.fromEntries(mapFromObj)); // Object.fromEntries는 이터러블 객체를 입력으로 받아 그 키-값 쌍을 객체로 변환해줌
// { key1: 'value1', key2: 'value2' } 출력
// 또는 키만, 값만 따로 배열로 만들 수 있음
console.log(Array.from(mapFromArr.keys())); // ["key1", "key2"] 출력
new Map()에 Map을 인자로 넣어 복사 가능
const original = new Map([[1, "one"]]);
const clone = new Map(original); // 얕은 복사 일어남
console.log(clone.get(1)); // one 출력
console.log(original === clone); // false 출력
```
const mySet = new Set([
["key1", "value1"],
["key2", "value2"]
]);
const mapFromSet = new Map(mySet);키 유일성을 유지한채로 병합 가능
const first = new Map([
[1, "one"],
[2, "two"],
[3, "three"],
]);
const second = new Map([
[1, "uno"],
[2, "dos"],
]);
// 두 맵을 병합할 때 키 값이 중복될 경우 마지막 키의 값을 따름
const merged = new Map([...first, ...second, [1, "eins"]]);
console.log(merged.get(1)); // eins 출력
console.log(merged.get(2)); // dos 출력
console.log(merged.get(3)); // three 출력
: 중복을 허용하지 않는 집합 자료구조. 해시 테이블을 기반으로 함
const set = new Set([iterable]) // 셋을 만듦. 이터러블 객체를 전달받으면(보통 배열) 그걸로 셋 초기화 함
set.add(value) // 값을 추가 후 셋 자신을 반환 O(1)
set.delete(value) // 값을 제거. 성공하면 true, 아니면 false를 반환 O(1)
set.has(value) // 셋 내에 값이 존재하면 true, 아니면 false를 반환 O(1)
set.clear() // 셋을 비움, undefined 반환
set.size // 값 개수 반환
set.keys() // 셋 내의 모든 값을 포함하는 이터러블 객체를 반환.
set.values() // set.keys와 동일한 작업을 함. 맵과의 호환성을 위해 만들어짐
set.entries() // 셋 내의 각 값을 이용해 만든 [value, value] 배열을 포함하는 이터러블 객체를 반환. 맵과의 호환성을 위해 만들어짐.
set.forEach(callbackFn,[, tisArg]) // 값에 대해 삽입 순서대로 callbackFn을 한 번씩 호출, thisArg 매개변수가 있으면 각 콜백의 this 값으로 사용됨, undefined 반환 O(N)
특징
집합 연산을 할 수 있는 메소드 제공

위 메소드의 인자에는 유사 Set 객체 쓸 수 있음(size, has(), keys()를 갖는 객체)
const a = new Set([1, 2, 3]);
const b = new Map([
[1, "one"],
[2, "two"],
[4, "four"],
]);
/*function union(setA, setB) {
const _union = new Set(setA);
for (const elem of setB) {
_union.add(elem);
}
return _union;
}*/
// Map 객체는 size, has(), keys()를 가지고 있어 Set의 메서드를 사용했을때 아래처럼 키 Set처럼 동작함
console.log(a.union(b)); // Set(4) {1, 2, 3, 4} 출력
for...of, forEach() 로 순회 가능
셋은 이터러블 객체이고 키에 삽입된 순서를 기억하므로 값의 삽입 순서대로 순회 가능
const set = new Set([1, "text", { "a": 1, "b": 2 }, 5]);
for (const item of set){
console.log(item);
}
for (const item of set.keys()) {
console.log(item);
}
for (const item of set.vlaues()) {
console.log(item);
}
for (const [key, value] of mySet1.entries()) { // key와 value가 같음
console.log(key);
}
/*
1
text
{ a: 1, b: 2 }
5 출력
*/
// forEach를 사용해도 동일하게 동작 -
set.forEach((value, valueAgain, set) => { // 키가 없으니까 value, vlaueAgain은 같은 값임
// Map과 비슷하게 만든거고 실제로는 value만 쓰면 됨
console.log(value);
});
//Set과 배열의 관계
const nums = [1, 2, 2, 2, 3, 4]
console.log([...new Set(nums)]); // [1, 2, 3, 4] 출력
//Set과 문자열의 관계
new Set("firefox"); // Set(6) { 'f', 'i', 'r', 'e', 'o', 'x' }
| Object | Map | Set | |
|---|---|---|---|
| 프로토타입 여부 | Object.prototype을 상속받아 그에 정의된 메서드들과 직접 정의한 키가 충돌할 수 있음 | 직접 정의한 내용만 가짐 | 직접 정의한 내용만 가짐 |
| 키 유형 | string, symbol만 허용 나머지는 자동으로 문자열로 변환됨 | 모든 자료형 | 값만 저장(모든 자료형) |
| 키 순서 | 순서가 항상 유지되진 않으니 순서에 의존하지 말자(숫자 키는 자동으로 오름차순 정렬됨) | 항상 삽입 순서 유지됨 | 값의 삽입 순서 항상 유지됨 |
| 순회 | for...in으로 키는 직접 순회 가능, 객체에 직접 for...of는 못쓰고 Object.entries(), Object.keys(), Object.values()로 이터러블로 변환한 후 사용 가능 | iterable(for...of, Map.prototype.forEach() 사용 가능) | iterable(for...of, Set.prototype.forEach() 사용 가능) |
| 크기 확인 | 별도의 코드 필요 | map.size 메소드 | set.size 메소드 |
참고자료
- https://goldenrabbit.co.kr/2023/11/29/%EC%BD%94%EB%94%A9-%ED%85%8C%EC%8A%A4%ED%8A%B8-%ED%95%A9%EA%B2%A9%EC%9E%90-%EB%90%98%EA%B8%B0-%ED%95%B4%EC%8B%9C-1-%ED%95%B4%EC%8B%9C%EC%9D%98-%EA%B0%9C%EB%85%90/
- https://velog.io/@sean2337/JavaScript-해시-적용-방법
- https://ko.javascript.info/map-set
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map
- https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Set