[JS] 자료구조 Map으로 출력하기

준리·2022년 8월 21일
0
post-thumbnail

다음과 같이 부서와 직원 객체가 있을 때, deptMap과 empDept를 만들고, 개발팀 직원 이름 목록을 출력하시오.

const hrTeam = { id: 1, dname: "인사팀" };
const devTeam = { id: 2, dname: "개발팀" };
const depts = [hrTeam, devTeam];
const hong = { id: 1, name: "Hong", dept: 1 };
const kim = { id: 2, name: "Kim", dept: 2 };
const emps = [
    hong,
    kim,
    { id: 3, name: "Park", dept: 2 },
    { id: 4, name: "Choi", dept: 2 },
];

console.log(deptMap); // Map(2) { '인사팀' => { id: 1, dname: '인사팀' }, '개발팀' => { id: 2, dname: '개발팀' } }
console.log(empDept); // Map(4) { { id: 1, name: 'Hong' } => { id: 1, dname: '인사팀' }, { id: 2, name: 'Kim' } => { id: 2, dname: '개발팀' }, { id: 3, name: 'Park' } => { id: 2, dname: '개발팀' }, { id: 4, name: 'Choi' } => { id: 2, dname: '개발팀' } }
return;

console.log(empDept.get(kim).dname); // '개발팀'
// 개발팀 직원 목록 출력 ⇒ Kim, Park, Choi

Map?🎅🏽

Map은 사실 Array의 메서드... 신나게 배열돌려주는 것 밖에 몰랐다. 이 Map은 자료구조로 실무에서도 많이 사용된다고 한다. 조금 더 알아보자.

우선 단순한 객체의 약점을 알아보자.
const object = {}
1. 문자열이나 심볼만 키로 가능하다. 객체나 비 문자형은 키가 될 수 없다.
2. 프로퍼티 순서가 보장 되지 않는다. lengthsize도 없다.
Object.keys(obj).length 불가
3. 이터레이터를 반환하는 keys, values, entries 함수가 없어서 spread... 문법 불가
4. has, set, get, delete, clear 같은 편리한 메소드가 없다.

이러한 약점이 보완되는 Map이란 친구가 있다.

new Map(), new Map([[k1,v1], [k2, v2]]) - 키 : 값, 키는 객체도 가능

메서드

Map.has(k) // 있나 없나 true & false
Map.set(k, v) // 등록하기 
Map.get(k) // 키 값으로 밸류 값 찾기
Map.delete(k) // 삭제하기

//이터레이터로 반환
const Map = new Map([
    [1, 2],
    [3, 4],
]);

Map.keys() // 키 값만 모아오기 // [Map Iterator] { 1, 3 }
Map.values() // 밸류 값만 모아오기 // [Map Iterator] { 2, 4 }
Map.entries() // 키밸류 모아오기 //[Map Entries] { [ 1, 2 ], [ 3, 4 ] }
Map.size // 함수가 아니라 프로퍼티 // 키밸류 값의 수 2 반환

for(const [k, v] of map) {...}; // entries()가 맵의 기본 iterator!
멋진 기능 k와 v를 비구조 할당해서 for..of 문을 돌릴 수 있음

(주의) Map의 key가 reference type일 경우 GC 대상이 안됨!(: Map자체가 참조)

문제정의👱🏽‍♂️

DB를 짜 듯 자료를 알맞게 넣어줘야한다. 일일이 넣어서 Map을 만들 수 있지만, 자료가 많아지면 그게 어려워진다. 우리가 아는 메서드들을 활용하여 해당 문제를 풀어보자.

#1 귀여운 노가다 제출

//다음과 같이 부서와 직원 객체가 있을 때, deptMap과 empDept를 만들고,  개발팀 직원 이름 목록을 출력하시오.

const hrTeam = { id: 1, dname: "인사팀" };
const devTeam = { id: 2, dname: "개발팀" };
const depts = [hrTeam, devTeam];
const hong = { id: 1, name: "Hong", dept: 1 };
const kim = { id: 2, name: "Kim", dept: 2 };
const emps = [
    hong,
    kim,
    { id: 3, name: "Park", dept: 2 },
    { id: 4, name: "Choi", dept: 2 },
];

const deptMap = new Map([
    [hrTeam.dname, hrTeam],
    [devTeam.dname, devTeam],
]);

const empDept = new Map([
    [emps[0], hrTeam],
    [emps[1], devTeam],
    [emps[2], devTeam],
    [emps[3], devTeam],
]);

console.log(deptMap); // Map(2) { '인사팀' => { id: 1, dname: '인사팀' }, '개발팀' => { id: 2, dname: '개발팀' } }
console.log(empDept); // Map(4) { { id: 1, name: 'Hong', dept: 1 } => { id: 1, dname: '인사팀' }, { id: 2, name: 'Kim' } => { id: 2, dname: '개발팀' }, { id: 3, name: 'Park' } => { id: 2, dname: '개발팀' }, { id: 4, name: 'Choi' } => { id: 2, dname: '개발팀' } }

console.log(empDept.get(kim).dname); // '개발팀'

for (const key of empDept.keys()) {
    if (empDept.get(key).dname === "개발팀") {
        console.log(key.name);
    }
}

// 개발팀 직원 목록 출력 ⇒ Kim, Park, Choi

비롯 그제 짠 코드지만 차곡차곡 쌓아서 넣은 값들이 귀엽기까지 하다.
결과적으로 정답은 나왔지만 효율적이진 않았다.

#2 효율성을 높인 코드 array.map

deptMap

const hrTeam = { id: 1, dname: "인사팀" };
const devTeam = { id: 2, dname: "개발팀" };
const depts = [hrTeam, devTeam];

const deptMap = new Map(depts.map((x) => [x.dname, x]));

console.log(deptMap); 
// Map(2) { '인사팀' => { id: 1, dname: '인사팀' }, '개발팀' => { id: 2, dname: '개발팀' } }

Map 안에 map을 사용하였다. 우리가 원하는 객체를 담고있는 depts 배열을 map으로 돌려서 x 파라미터의 .dname을 가져오고 x전체를 배열에 감싸서 가져왔다. 처음에 배열에 감싸지 않고,
const deptMap = new Map(depts.map((x) => x.dname, x)); 형태로 출력하려하니 뒤에 사용된 x가 정의 되지 않았다고 했다.

empDept 1번째 방법 // index 활용

const hrTeam = { id: 1, dname: "인사팀" };
const devTeam = { id: 2, dname: "개발팀" };
const depts = [hrTeam, devTeam];
const hong = { id: 1, name: "Hong", dept: 1 };
const kim = { id: 2, name: "Kim", dept: 2 };
const emps = [
    hong,
    kim,
    { id: 3, name: "Park", dept: 2 },
    { id: 4, name: "Choi", dept: 2 },
];

const empDept = new Map(emps.map((x) => [x, depts[x.dept - 1]]));

console.log(empDept); 
// Map(4) { { id: 1, name: 'Hong', dept: 1 } => { id: 1, dname: '인사팀' }, { id: 2, name: 'Kim' } => { id: 2, dname: '개발팀' }, { id: 3, name: 'Park' } => { id: 2, dname: '개발팀' }, { id: 4, name: 'Choi' } => { id: 2, dname: '개발팀' } }

emp 배열에 map을 돌려서 x를 키값으로 가져오고 밸류값을 depts[1 or 2 - 1] 해서 인덱스를 이용하는 방법이다. 보시다시피 불안정하고 알맞게 찾아가는 방법은 아닌 것 같다.
array 인덱스 값을 바로 이용하는 것은 위험하다.

empDept 2번째 방법 // arr.find() 활용

const hrTeam = { id: 1, dname: "인사팀" };
const devTeam = { id: 2, dname: "개발팀" };
const depts = [hrTeam, devTeam];
const hong = { id: 1, name: "Hong", dept: 1 };
const kim = { id: 2, name: "Kim", dept: 2 };
const emps = [
    hong,
    kim,
    { id: 3, name: "Park", dept: 2 },
    { id: 4, name: "Choi", dept: 2 },
];

const empDept = new Map(
    emps.map((x) => [x, depts.find((d) => d.id === x.dept)])
);

console.log(empDept); 
// Map(4) { { id: 1, name: 'Hong', dept: 1 } => { id: 1, dname: '인사팀' }, { id: 2, name: 'Kim' } => { id: 2, dname: '개발팀' }, { id: 3, name: 'Park' } => { id: 2, dname: '개발팀' }, { id: 4, name: 'Choi' } => { id: 2, dname: '개발팀' } }

array.find() 메서드를 활용하는 것이다. 나쁘지 않은 접근이지만 map도 돌고 find도 돌고 반복문이 자주 돌아서 약간 효율이 떨어질 것 같다.

empDept 3번째 방법 // Map.get() 메서드

const hrTeam = { id: 1, dname: "인사팀" };
const devTeam = { id: 2, dname: "개발팀" };
const depts = [hrTeam, devTeam];
const hong = { id: 1, name: "Hong", dept: 1 };
const kim = { id: 2, name: "Kim", dept: 2 };
const emps = [
    hong,
    kim,
    { id: 3, name: "Park", dept: 2 },
    { id: 4, name: "Choi", dept: 2 },
];

const deptIdMap = new Map(depts.map((d) => [d.id, d]));
// { 1 => { id: 1, dname: '인사팀' }, 2 => { id: 2, dname: '개발팀' } }

const empDept = new Map(emps.map((x) => [x, deptIdMap.get(x.dept)]));


console.log(empDept); 
// Map(4) { { id: 1, name: 'Hong', dept: 1 } => { id: 1, dname: '인사팀' }, { id: 2, name: 'Kim' } => { id: 2, dname: '개발팀' }, { id: 3, name: 'Park' } => { id: 2, dname: '개발팀' }, { id: 4, name: 'Choi' } => { id: 2, dname: '개발팀' } }
const deptIdMap = new Map(depts.map((d) => [d.id, d]));
// { 1 => { id: 1, dname: '인사팀' }, 2 => { id: 2, dname: '개발팀' } }

시작 전에 deptIdMap을 정의한다. 이 Map의 value 값이 우리가 만들길 원하는 empDept의 value값과 동일하다. 이 코드가 #2 보다 나은 이유는 get메서드는 루프를 돌지 않고 hash값으로 바로 해당 메모리에 접근하기 때문에 더 나은 코드라고 할 수 있다.

empDept 4번째 방법 // Map.get() 메서드 && 중복값 지우기

const hrTeam = { id: 1, dname: "인사팀" };
const devTeam = { id: 2, dname: "개발팀" };
const depts = [hrTeam, devTeam];
const hong = { id: 1, name: "Hong", dept: 1 };
const kim = { id: 2, name: "Kim", dept: 2 };
const emps = [
    hong,
    kim,
    { id: 3, name: "Park", dept: 2 },
    { id: 4, name: "Choi", dept: 2 },
];

const deptIdMap = new Map(depts.map((d) => [d.id, d]));
// { 1 => { id: 1, dname: '인사팀' }, 2 => { id: 2, dname: '개발팀' } }

const empDept = new Map(
    emps.map((e) => {
        const d = deptIdMap.get(e.dept);
        return [(delete e.dept, e), d];
    })
);
console.log(empDept); 
// Map(4) { { id: 1, name: 'Hong' } => { id: 1, dname: '인사팀' }, { id: 2, name: 'Kim' } => { id: 2, dname: '개발팀' }, { id: 3, name: 'Park' } => { id: 2, dname: '개발팀' }, { id: 4, name: 'Choi' } => { id: 2, dname: '개발팀' } }

정제된 데이터를 만드는 버전이다. d 변수에 만들고 싶은 value를 먼저 저장한다. return 값으로 쉼표괄호 연산자를 사용하여 (delete e.dept, e) e.dept를 지우면서 e를 출력한다. 그리고 담겨진 d를 불러온다. e.dept가 지워졌어도 d는 이미 담겨있기 때문에 좀 더 깔끔한 Map이 출력된다.

👩🏽‍💻개발팀 직원 목록 출력하기

#1 for..of 문 활용

for (const key of empDept.keys()) {
    if (empDept.get(key).dname === devTeam.dname) {
        console.log(key.name);
    }
}

for...of 문으로 해결했는데 empDept의 keys() 메서드를 사용해 key 값들을 돌린 뒤 조건문에 개발팀인 애들만 찾아서 key의 이름을 출력해줬다. 뭐 어쨋든 답은 나왔다.

#2 filter와 map

const devEmpNames = [...empDept]
    .filter((ed) => ed[1].dname === devTeam.dname)
    .map((ed) => ed[0].name);

console.log(devEmpNames.join(", ")) // Kim, Park, Choi

-------------------// 키와 밸류는 비구조할당해서 나눠쓸 수 있다.

  const devEmpNames = [...empDept]
    .filter(([_, dept]) => dept.dname === devTeam.dname)
    .map(([e, _]) => e.name);

스프레드 문법으로 땡겨오는 이유는 map는 iterator가 아니기 때문이다.
그 배열에 1차적으로 filter를 걸어서 개발팀인 애들을 추리고 다시 map을 돌려 원하는 값을 뽑아냈다. ed[0]은 key 값이고, ed[1]을 value 값이다. 배웠다시피 Map은 key 값에 객체를 받을 수 있다.

결론

Map에 대하여 알아보았다. 확실히 탄탄한 자료구조를 짤 수 있을 것이라 생각이든다. array의 메서드들을 더 잘알아야겠다는 생각이 들었고, 반복하며 익히는게 무척 중요하다고 생각했다.

출처

SSAC 영등포 교육기관에서 풀스택 실무 프로젝트 과정을 수강하고 있다. JS전반을 깊이 있게 배우고 실무에 사용되는 프로젝트를 다룬다. 앞으로 그 과정 중의 내용을 블로그에 다루고자 한다.

profile
트렌디 풀스택 개발자

0개의 댓글