반응형
Map
- Map 은 객체 형태다.
- key-value 쌍과, 키의 삽입 순서를 기억한다.
- 모든 값을 키로 사용할 수 있다. (그냥 객체는 String, Symbol만 가능)
- 키는 unique한 값이다.
- for...of 루프를 돌면 [key, value] 로 이뤄진 배열을 반환한다. 삽입된 순서대로 반복한다. (set을 한 순서대로)
- 키 동일성은 SameValueZero를 기반으로 한다.
- 0 ≠ -0
- Nan === Nan
- 객체 vs 맵
- 둘이 유사하다.
- 맵은 아이템 수를 size 로 쉽게 가져올 수 있지만, 객체는 일일히 봐야함
- 맵은 순회가능(iterator)하므로 for…of문 사용 가능하지만, 객체는 아니다.
- 맵이 객체보다 성능이 더 좋다. (키-값 쌍이 빈번하게 추가/제거되는 상황에 최적화되어 있음)
메서드
- set(key, value): 값 넣기
- get(key): value 가져오기
- size: 크기 가져오기
- clear(): 모든 값(키-값 쌍)을 제거
- delete(key): key를 가진 요소 제거. 잘 제거 되었으면 true를 반환하고, 그렇지 않으면 false를 반환한다.
- has(key): key를 가지고 있는지 boolean값 반환. delete()후 has()를 살펴보면 false가 뜬다.
- @iterator(): 삽입된 순서대로 [key, value] 두 배열을 포함하는 새로운 반복자(iterator)를 반환한다.
- keys(): 삽입된 순서대로 모든 키들을 포함하는 새로운 반복자(iterator)를 반환한다.
- values(): 삽입된 순서대로 모든 값들을 포함하는 새로운 반복자(Iterator)를 반환한다.
- entries(): 삽입된 순서대로 모든 키, 값들을 포함하는 새로운 반복자(iterator)를 반환한다.
- forEach(): 각 키-값 쌍에 대해 삽입된 순서대로 callbackFn을 한 번씩 호출한다.
예제
간단한 예제
const map1 = new Map();
map1.set('a', 1);
map1.set('b', 2);
map1.set('c', 3);
console.log(map1.get('a')); // 1
map1.set('a', 97);
console.log(map1,get('a')); // 97
console.log(map1.size); // 3
map1.delete('b');
console.log(map1.size); // 2
이렇게 사용하면 안돼요!
객체와 비슷하게 생겼다고 이렇게 꺼내서 값을 넣으면(?) 안된다. 무조건 set(key, value)로만 넣어야 한다.
const wrongMap = new Map();
**wrongMap['bla'] = 'blaa';
wrongMap['bla2'] = 'blaa2';**
console.log(wrongMap); // Map { bla: 'blaa', bla2: 'blaa2' }
wrongMap.has('bla'); // false (실패)
wrongMap.delete('bla'); // false (실패)
console.log(wrongMap); /// Map {bla: 'blaa', bla2: 'blaa2' }
콘솔로 찍었을 때 넣은 값들이 잘 나오니까 동작도 잘할 것처럼 보이지만,
has, delete 와 같이 메서드들을 사용하니 전혀 동작하지 않고, Map 데이터 구조와 상호작용하지 않는 모습을 보여준다.
속성 설정은 일반 객체의 기능을 사용한다. 그래서 ‘bla’ 값은 Map에 저장되지 않는다.
다른 예제 - Map 객체 사용하기
coonst myMap = new Map();
const keyString = 'a string';
const keyObj = {};
const keyFunc = function() {};
// setting value
myMap.set(keyString, "value associated with a string");
myMap.set(keyObj, "value associated with keyObj");
myMap.set(keyFunc, "value associated with keyFunc");
console.log(myMap.size); // 3
// get value
console.log(myMap.get(keyString)); // "value associated with a string"
console.log(myMap.get(keyObj)); // "value associated with keyObj"
console.log(myMap.get(keyFunc)); // "value associated with keyFunc"
console.log(myMap.get('a string')); // "value associated with a string"
// keyString === 'a string' (primitive type)
console.log(myMap.get({}); // undefined.
// keyObj !== {} (reference type)
console.log(myMap.get(function() {})); // undefined.
// keyFunc !== function() {} (reference type)
다른 예제 - Map 키로 NaN 사용하기
원래는 모든 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로 맵 순회하기
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 of myMap.keys()) {
console.log(key);
}
// 0
// 1
for (const value of myMap.values()) {
console.log(value);
}
// zero
//one
for (const [key, value] of myMap.entries) {
console.log(`${key} = ${value}`);
}
// 0 = zero
// 1 = one
다른 예제 - forEach((value, key)) 로 맵 순회하기
맵은 Iterator하기 때문에 forEach로도 순회할 수 있다.
myMap.forEach((value, key) => {
console.log(`${key} = ${value}`);
});
// 0 = zero
// 1 = one
배열과 맵
2차열의 Key-value 배열을 Map으로, Map을 배열로 바꿀 수 있다.
- Map 생성자 사용하기
const kvArray = [['key1', 'value1'], ['key2', 'value2']];
**const myMap = new Map(kvArray);**
myMap.get('key1'); // "value1"
- Array.prototype.from() 사용하기
**Array.from(myMap)**; // { key1: "value1", key2: "value2" }
- spread operator 사용하기 (맵을 배열로 변환한다)
**[...myMap];** // kvArray와 동일
- keys(), values() iterators를 사용하기
**Array.from(myMap.keys())**; // ["key1", "key2"]
맵 복제하기
Array와 동일하게 복제가 가능하다. 하지만 Reference Type이므로 원본과 복제본이 동일하진 않다.
const original = new Map([
[1, 'one'],
]);
const clone = new Map(original);
console.log(clone.get(1)); // one
console.log(original === clone); // false
맵 합치기(병합)
- 병합시, 키가 중복되면 마지막 키의 값을 따른다.
- spread operator는 맵을 배열로 변환한다.
const first = new Map([
[1, 'one'],
[2, 'two'],
[3, 'three'],
]);
const second = new Map([
[1, 'uno'],
[2, 'dos'],
]);
const merged = new Map([...first, ...second]);
merged.get(1); // uno
merged.get(2); // dos
merged.get(3); // three
- 맵과 배열이 함께 병합할 수도 있다.
const merged = new Map([...first, ...second, [1, 'eins']]);
merged.get(1); // ein
Map.prototype@@iterator
- @@iterator 메서드는 반복 가능하도록 한다.
- 그래서 맵이 spread operator와 for ... of 같이 루프 반복이 필요한 구문에서 사용될 수 있도록 한다.
- 키-값 쌍을 생성하는 반복자를 반환한다. (즉, Map.prototype.entries 와 동일한 값을 반환함)
예제 - for of
const map1 = new Map();
map1.set('0', 'foo');
map1.set(1, 'bar');
const iterator1 = map1[Symbol.iterator]();
for (const item of iterator1) {
console.log(item);
}
// ["0", "foo"]
// [1, "bar"]
예제 - for [key, value] of
const myMap = new Map();
myMap.set("0", "foo");
myMap.set(1, "bar");
myMap.set({}, "baz");
for (const entry of myMap) {
console.log(entry);
}
// ["0", "foo"]
// [1, "bar"]
// [{}, "baz"]
for (const [key, value] of myMap) {
console.log(`${key}: ${value}`);
}
// 0: foo
// 1: bar
// [Object]: baz
예제 - 수동으로 반복자 돌리기: next()
next() 메서드를 사용하면, 수동으로 다음 요소를 호출할 수 있다.
이렇게 하면 반복 프로세스를 최대로 제어할 수 있다.
const myMap = new Map();
myMap.set("0", "foo");
myMap.set(1, "bar");
myMap.set({}, "baz");
const mapIter = myMap[Symbol.iterator]();
console.log(mapIter.next().value); // ["0", "foo"]
console.log(mapIter.next().value); // [1, "bar"]
console.log(mapIter.next().value); // [Object, "baz"]
반응형
'Front-End: Web > JavaScript' 카테고리의 다른 글
crypto로 비밀번호 암호화하기 (0) | 2023.07.25 |
---|---|
[js]tilde(~)와 double tilde(~~) (0) | 2022.12.06 |
[js] 변수를 사용하기 전에 꼭 알아야할 "TDZ(Temporal Dead Zone)" (0) | 2022.11.29 |
[js] 객체 복사: Object.assign vs. object spread (0) | 2022.11.28 |
[JavaScript] eval() 함수를 사용하지 마세요! eval() is evil! (0) | 2022.11.27 |