Map/Set

박재현·2023년 4월 2일
0

🔎 Map/Set

  • ES6(ES2015)에 추가된 새로운 자료구조
    - Map: 객체(와 유사)
    - Set: 배열(와 유사)

📌 Map

  • Key, Value 쌍으로 이루어진 컬렉션으로 1:1 매칭(묶임)
  • 객체와 유사한 특성을 가지지만 Key, Value를 컨트롤할 수 있는 다양한 내장함수 사용할 수 있음
    - set, get, has, delete, clear, size...
  • key, value에 number, object, fubction 할당 할 수 있음
  • 해쉬 함수 구조 -> (Hash)Map

=> 결론적으로 데이터를 좀 더 유연하게 컨트롤할 수 있고, 내장함수를 통해 명확한 프로그래밍 구현이 가능하다.
또한 key, value를 추가하거나 제거할 때 Object에 비해 성능적으로 최적화 되어 있음

실제 MDN 문서에서도 Map vs Object 비교해놓게 있는데 Map 사용을 사용하는 걸 더 권장하는 걸 볼 수 있음
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Map

💡 new Map() Method

  • set(key, value): 지정된 키와 값을 맵에 추가합니다.
  • get(key): 지정된 키와 연결된 값을 반환합니다. 키가 존재하지 않으면 undefined를 반환합니다.
  • has(key): 지정된 키가 맵에 존재하는지 여부를 반환합니다.
  • delete(key): 지정된 키와 연결된 값을 맵에서 제거합니다.
  • clear(): 맵의 모든 키와 값을 제거합니다.
  • size: 맵의 항목 수를 반환합니다.
  • keys(), values(): { value: 값, done: boolean } iterator를 반환한다

Iterator

  • JavaScript에서 iterator는 데이터 집합(collection)을 반복(iterate)할 수 있는 객체(object)입니다.
  • next(): iterator에서 다음 요소에 대한 정보를 반환합니다. 반환값은 {value: 값, done: boolean} 형태의 객체입니다.
  • value 프로퍼티에는 다음 요소의 값이 들어 있고, done 프로퍼티에는 iterator에서 더 이상 요소가 없는지 여부가 들어 있습니다.
  • iterator 객체는 Symbol.iterator 메서드를 가지고 있습니다. 이 메서드는 iterator 객체 자신을 반환하므로, for..of 루프 등에서 iterator 객체를 사용할 수 있습니다.
  • 예를 들어, 다음과 같이 배열(array)을 iterator로 변환하고, for..of 루프를 사용하여 각 요소를 출력할 수 있습니다:
  • Iterator는 ES6(ES2015)부터 도입된 개념으로, Map, Set, Array 등의 내장 객체에서 사용할 수 있습니다. 또한, 직접 Iterator 객체를 구현하여 사용할 수도 있습니다.
const arr = [1, 2, 3];
const iterator = arr[Symbol.iterator]();
for (const value of iterator) {
  console.log(value);
}
// 출력:
// 1
// 2
// 3

ex)

const nm = new Map();

  nm.set('a', 0);
  nm.set('b', 1);
  nm.set('c', 2);
  
  for (const [k, v] of nm) {
    console.log(k, v)
    // 'a', 0
    // 'b', 1
    // 'c', 2
  }
  
  nm.forEach((v: number, k: string) => {
    console.log(k, v);
    // 'a', 0
    // 'b', 1
    // 'c', 2
  })

  // 키 값 존재 여부
  console.log(nm.has('a')) // true
  console.log(nm.has('z')) // false

  const obj = { a: 'b' };
  nm.set(obj, 123);

  console.log(nm.get(obj)) // 123;
  
  // delete
  nm.delete(obj)
  console.log(nm.has(obj)) // false;
  
  // 모든 Key, Value 삭제
  nm.clear();
  console.log(nm.size) // 0
  console.log(nm); // {}

💡 Map vs Object

예를들어 어떤 가게에서 손님 이름과 지역을 저장하는 배열이 있다고 가정

let guestArr = [
  { name: 'a', city: 'seoul' },
  { name: 'b', city: 'seoul' },
  { name: 'c', city: 'busan' },
  { name: 'd', city: 'busan' },
  { name: 'e', city: 'busan' },
  { name: 'f', city: 'incheon' },
  { name: 'g', city: 'hwaseong' },
  { name: 'h', city: 'jeju' },
]

해당 값에서 도시별로 누가 왔는지 알고 싶어서 아래와 같은 객체를 얻고 싶다면?

{
  seoul: [ { name: 'a', city: 'seoul' }, { name: 'b', city: 'seoul' } ],
  busan: [
    { name: 'c', city: 'busan' },
    { name: 'd', city: 'busan' },
    { name: 'e', city: 'busan' }
  ],
  incheon: [ { name: 'f', city: 'incheon' } ],
  hwaseong: [ { name: 'g', city: 'hwaseong' } ],
  jeju: [ { name: 'h', city: 'jeju' } ]
}

-> object 구현

let oGuest = {};
guestArr.forEach(item => {
  if (!oGuest[item.city]) {
    oGuest[item.city] = [];
  }

  oGuest[item.city].push(item);
}) 

console.log(oGuest)

위 예시에서 if 문안에 들어가 있는 값은 boolean(true, false) 값이 아님.
javascript에서는 if 문 안에 0, undefined인 경우도 false로 인식하기 때문에 위 if문에서 !oGuest[item.city] 는 !undefined => true로 인식하게 되어 있음
이를 통해 key로 지역, value로 빈 배열을 생성하고 해당 name, city를 추가하는 구조

=> 즉, 명확한 true, false 의도로 프로그래밍 할 수 없는 예외상황 발생할 수 있음

-> new Map() 구현

let mapGuest = new Map();
guestArr.forEach(item => {
  if (!mapGuest.has(item.city)) {
    mapGuest.set(item.city, []);

    mapGuest.get(item.city).push(item);
  }
})

console.log(mapGuest)

has(key) 메서드는 boolean(true, false)를 반환하기 떄문에 명확한 프로그래밍 구현 가능


💬 추가

기존 객체 반복문의 경우 for...in을 쓸때 프로토타입 속성까지 다 복사해버리기 떄문에
명확하게 원본 값만 반복하기 위해 hasOwnProperty() 와 같은 처리를 해줘야함

 const obj = { ... };
               '
 for (let i in obj2) {
 if (obj.hasOwnProperty(i)) {
     obj[i];
    }
 }

(만약 Typscript에서 쓴다면 정적인 타입을 지정해줘야 하기 떄문에 객체에 Record<string, unknown>와 같은 추가적인 처리를 더 해줘야 한다... )

하지만 new Map()의 경우 반복문, foreach를 사용할 수 있기 때문에

  const nm = new Map();

  nm.set('a', 0);
  nm.set('b', 1);
  nm.set('c', 2);
  
  for (const [k, v] of nm) {
    console.log(k, v)
    // 'a', 0
    // 'b', 1
    // 'c', 1
  }
  
  nm.forEach((v: number, k: string) => {
    console.log(k, v);
    // 'a', 0
    // 'b', 1
    // 'c', 1
  })

이런식으로 처리가 가능하다

📌 Set

  • 배열 자료구조를 사용하고 싶지만 중복을 허용하고 싶지 않을 때
const s = new Set();

s.add(1);
s.add(2);
s.add(3);

console.log(s); // { 1, 2, 3 }
console.log(s.size); // 3

// 반복, foreach 사용 가능

for (const a of s) {
  console.log(a); // 1, 2, 3
}

s.forEach(a => {
  console.log(a); // 1, 2, 3
})

s.delete(2);
console.log(s); // { 1, 3 }
s.clear();
console.log(s); // {}

기존 배열의 중복을 제거하고 싶을 때?
(알고리즘 써도 간단하게 되긴 하지만...)

const arr = [1, 3, 2, 7, 2, 6, 3, 5];
const ns = new Set(arr);
const result = Array.from(ns);
console.log(result); // [1, 3, 2, 7, 6, 5]


📌 new Map() vs new WeakMap()

  • WeakMap()의 경우 자바스크립트 엔진이 지시(Instruction)를 돌면서(가비지컬렉션) 해당 참조값이 없을 때 자동으로 제거
  • heap 메모리 누수 현상을 방지
  • 하지만 Key로 무조건 객체가 할당되어야 함 (원시 타입 사용 불가), Value는 모든 값 할당 가능
  • get(), set(), has(), delete()와 같은 메서드를 제공하지만, size 프로퍼티나 forEach() 메서드는 제공하지 않음

ex)

const obj = {};
const user = { id: 1, name: 'hong' }';

const wm = new WeakMap();

wm.set(obj, user)
...
...
user = null;

0개의 댓글

관련 채용 정보