2025. 5. 8. 16:40ㆍ코딩/JS
개요
최근 코테준비를 다시 시작하면서 1단계를 비롯한 낮은 난이도 문제들을 해결하며 뿌듯해하고 있었다.
하지만, 정답률이 낮은 문제로 갈 수록 내가 아는 지식, 방식내에서 해결 안되는 문제들이 많았고 DFS, 탐욕법, 동적계획법 같은 개념들을 모르고 진행하는데에 한계를 느꼈다.
보통 해결되지 않는 상황은 크게 두 가지로 나누어지는데
1. 해당 문제를 풀기위한 작전, 방식 자체가 안떠올라 시작 자체를 못하는 경우
2. 어느정도 구현은 했으나 테스트 혹은 제출 단계에서 일부 테스트 케이스를 통과하지 못하는 경우
이 두 가지가 단순히 많이 풀어보기 보단 위에서 말한 익숙치 않은 알고리즘, 자료구조에 대한 이해도가 낮다 생각해 코테 문제를 풀며 접하는 여러 상황에 대해서 기록하고자 했다.
문제
프로그래머스 뉴스 클러스터링 문제
https://school.programmers.co.kr/learn/courses/30/lessons/17677
프로그래머스
SW개발자를 위한 평가, 교육, 채용까지 Total Solution을 제공하는 개발자 성장을 위한 베이스캠프
programmers.co.kr
두 개의 단어(a,b)가 주어지고 각 단어를 두 글자 씩 끊어 생성된 단어들을 비교하며 a,b에서 생성된 단어들 간의 교집합과 합집합을 구하는 문제이다.
처음 내가 접근한 방법으로는 각 단어들을 두 글자씩 끊어 배열에 넣어 두 배열을 includes로 비교하면서 교집합을 구하고, 총 단어 수의 합에서 교집합을 빼 합집합으로 구했다.
오류
내가 생각치 못한 부분중, 검증 기준이 되는 배열에 일치하면서 동일한 단어가 더 많은 경우엔 문제가 되지 않으나, 반대로 적은 경우 교집합의 개수가 잘못 측정되었다.
또한 배열의 경우 중복요소들에 대하여 단순 개별 저장되기에 배열의 길이가 늘어나는 경우 연산의 수도 수 없이 늘어나게 된다.
문제 해결을 위해 각 단어에 해당하는 빈도수를 기록하고 각 단어 간 같은 단어가 있는 경우 더 적은 빈도수를 택해 교집합의 수로 더하고 합집합의 경우 더 많은 수를 더하면 되기에 key-value로 이루어진 구조가 필요했다.
Map
단순 index를 통한 순서와 값들로 구성된 array 배열과는 다르게 Map의 경우 키-값 쌍으로 이루어져있다.
또한 객체와는 다르게 Key의 경우 다양한 자료형을 허용한다는 차이가 있으며 Map에서 제공하는 메서드를 통해 값을 더하거나 빼곤한다.
// 새로운 Map 객체를 생성
const map1 = new Map();
// 생성된 Map에 새로운 값을 삽입
map1.set("a", 1);
map1.set("b", 2);
map1.set("c", 3);
// 값을 읽는방법 get
console.log(map1.get("a"));
// Expected output: 1
// 배열 객체의 length에 해당하는 map 객체의 크기를 반환
console.log(map1.size);
// Expected output: 3
// Map의 키가 중복값이 있는 경우 나중값이 적용됨
let map1 = new Map([
['a',1],
['a',2],
['b',3]
]) // {"a" => 2, "b" => 3}
⚠️ 주의
객체와는 유사하나 값을 설정할 때 객체처럼 map[key]와 같이 사용한다면 일반 객체로 취급되어 여러 제약이 발생한다
따라서, 전용 메서드인 set, get을 사용해 Map의 값들에 대하여 접근해야한다.
응용
객체와의 차이 중 하나로 Map 자료형은 for..of 혹은 forEach 같은 순회가 가능하다. 물론, 객체도 가능하지만 객체의 경우 Object.keys를 거쳐야하기에 더 간단한 로직으로 순회할 수 있다.
myMap.forEach((value, key) => {
console.log(`${key} = ${value}`);
});
for (const [key, value] of myMap) {
console.log(`${key} = ${value}`);
}
for (const key of myMap.keys()) {
console.log(key);
}
for (const value of myMap.values()) {
console.log(value);
}
일반 배열 순회와는 다르게 value와 key를 접근해 내부 로직을 구현할 수 있다.
문제 해결
function solution(str1, str2) {
str1 = str1.toLowerCase();
str2 = str2.toLowerCase();
const getMultiset = (str) => {
const arr = [];
for (let i = 0; i < str.length - 1; i++) {
const pair = str.substr(i, 2);
if (/^[a-z]{2}$/.test(pair)) arr.push(pair);
}
return arr;
};
const arr1 = getMultiset(str1);
const arr2 = getMultiset(str2);
// 빈도수 맵 생성
const createFrequencyMap = (arr) => {
const map = new Map();
arr.forEach(item => {
map.set(item, (map.get(item) || 0) + 1);
});
return map;
};
const map1 = createFrequencyMap(arr1);
const map2 = createFrequencyMap(arr2);
먼저 각 문자열들만을 짝으로 맞추어 배열에 저장했다(arr1, arr2) 정규식을 통해 문자열인 경우에만 짝으로 분류하고, 배열을 순회하며 key에 해당하는 value를 1씩 증가시켜주어 map을 생성했다.
let intersection = 0;
let union = 0;
const allKeys = new Set([...map1.keys(), ...map2.keys()]);
allKeys.forEach(key => {
const count1 = map1.get(key) || 0;
const count2 = map2.get(key) || 0;
intersection += Math.min(count1, count2);
union += Math.max(count1, count2);
});
const similarity = union === 0 ? 1 : intersection / union;
return Math.floor(similarity * 65536);
이 부분부터는 내가 했던 방법이 너무 길어 gpt의 도움으로 가독성 좋게 수정했다
각 맵의 전체 존재하는 key로만 이루어진 set를 만들고, 해당 key로 각 map을 순회하며 다음과정을 거친다.
1. key에 해당하는 value가 한 개만 존재하는 경우, Math.min을 통해 교집합은 0으로 채택
2. key에 해당하는 value가 모두 존재하는 경우, Math.min을 통해 더 적은 개수를 교집합 수로 채택
3. key에 해당하는 value중 큰 수(Math.max)를 합집합의 수로 채택해 더함
합집합이 0인경우 즉 모두 공집합인 경우 조건에 나온대로 1을 반환
참고 및 마무리
적다보니 객체로 해도 충분히 풀렸을 거 같은데 힌트에서 Map을 보게되어 배우는김에 해당 방법으로 접근하게 되었다.
key도 문자열이고 입력되는 순서도 es6 이상부터는 상관없다니 다음부터 특수한 경우가 아니라면 객체가 더 가독성이 좋을 수도 있겠다고 생각했다.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map
Map - JavaScript | MDN
The Map object holds key-value pairs and remembers the original insertion order of the keys. Any value (both objects and primitive values) may be used as either a key or a value.
developer.mozilla.org
[JS] 📚 자바스크립트 자료형 Map 🚩 정리
자바스크립트는 객체와 배열이라는 강력한 자료구조를 제공합니다. 객체 – 키가 있는 컬렉션을 저장함 배열 – 순서가 있는 컬렉션을 저장함 하지만 현실 세계를 반영하기엔 이 두 자료구
inpa.tistory.com
'코딩 > JS' 카테고리의 다른 글
JS 모듈 패턴 + Dynamic Import (1) | 2025.05.15 |
---|---|
문자열 원하는 글자로 채우기 padStart, padEnd (1) | 2025.05.15 |
JS - Module Pattern (0) | 2025.03.30 |
Js문제 오답노트 반장뽑기(reduce(), 배열값 중복 계산) (4) | 2022.08.10 |
JS문제풀이 (로꾸거배열) + (&, |, 연산자 단축평가) (1) | 2022.08.08 |