오늘의 코딩 테스트 한줄 - 가장 가까운 같은 글자

Edwin·2023년 5월 30일
0
post-thumbnail

가장 가까운 같은 글자

function solution(s) {
    let stringArr = s.split("")
    let numArr = stringArr.map((el, index) => stringArr.indexOf(el) === index ? -1 : "음")
    return numArr
}

매개변수로 : "banana" 가 들어온다고 하자
string을 쪼개어, 배열에 처음있는 알파벳은 -1을, 중복되는 알파벳은 가장 가까운 같은 글자와의 거리를 구하는 문제이다.

나의 풀이

map 함수와 삼항조건문을 통하여 처음등장하는 알파벳에 대한 처리는 풀이했지만, 중복되는 문자에 대해서는 처리하지 못했다. 물론 중복되는 알파벳이 한 번인 경우에는 처리를 할 수 있었다. 그러나, "banana"와 같이 a가 세 번 등장할 때는 처리하지 못하였다.

해결은 Map 객체를 활용하는 방법

MDN 문서에 따르면, Map 객체는 키-값 쌍인 집합이며, 한 Map에서의 키는 오직 단 하나만 존재 합니다. ES6(ECMAScript 2015)에서 도입되었으며, 특히 객체가 아닌 원시 값을 키로 사용할 수 있고, 순서가 보장되는 순회 가능한(iterable) 컬렉션이다.

원시 값을 키로 사용할 수 있다는 말은, 기존의 객차가 문자열을 키로 사용하는 것에서 구분된다. 즉 키로 숫자열, 진위값 등을 사용할 수 있다는 말이다.

function solution(s) {
    const answer = [];
    
    // 1) 글자와, 위치(index)를 담을 Map 객체 생성
    const map = new Map();
    
    // 2) 글자들을 하나씩 반복하여 비교
    for(let i=0; i<s.length; i++){
    
        // 3) 동일한 글자가 있으면, (현재 위치 - 이전 위치) 값을 answer 담는다.
        if(map.has(s[i])) answer.push(i-map.get(s[i]));      
        
        // 4) 동일한 글자가 없다면, -1를 answer에 담는다.
        else answer.push(-1);    
        
        // 5) 최근 글자와, 위치(indx)로 값을 담는다.
        map.set(s[i], i);  
    };
    return answer;
}

주요한 메서드로는 다음과 같다.

  • set(), get(), has(), delete(), clear(), size

has()

Map 함수에 해당 키에 해당하는 요소가 존재하는지 묻는다. 결과는 진위값으로 반환되며, 결과에 따라서 조건문을 실행할 수 있게 된다. 처음 선언되었다면, answer의 빈배열에 -1이 입력된다.

set(), get()

조건과 상관없이 실행되면, Map 객체에 알파벳과, 해당 인덱스의 값이 담겨질 것이다.

has()를 통해서 만약 해당 값에 해당되는 값을 찾으면, answer에는 현재 인덱스에서 get() 을 통해서 가져온 s(i)의 값에 해단되는 값을 빼준 값이 기록된다.

"banana"를 예로 할 때, 중복된 값이 다시 등장하면, 기존의 Map 객체에 있었던 내용는 갱신된다. 즉 1이 3으로, 3이 5로 갱신되는 것이며, 이를 통해서 가까운 값에서 이후 등장하는 값을 빼가며 배열을 생성할 수 있는 것이다.

알고리즘 테스트

알고리즘 테스트는 확실히 코딩의 한계를 명확하게 보여준다. 그리고 찾아가는 과정에서 새로운 지식들을 가르쳐준다. 현업에서 사용하게 될지는 모르겠지만, 배열의 중복을 제거하는 Set 객체 타입에 대해서 어제 공부했고, 오늘은 Map 객체에 대해서 살펴보았다.

Map 객체를 사용하는 것은, 객체에 중복되지 않는 키-값 쌍을 저장할 수 있으며, 여러번 등장하는 상황에서도 모든 키-값의 쌍을 유지할 수 있다는 장점이 있다. 또한, Map 객체는 항복이 추가된 순서를 유지한다. 또한 has(), delete() 메서드를 통하여 효율적인 검색과 제거를 가능하게 한다.

반면에 일반적인 Object는 프로터티의 순서를 보장하지 않는다. 즉 프로퍼티들은 객체에 저장된 순서와 독립적이다. 이러한 점에서 해당 코드에서는 Map 객체를 사용하였다.

다른 더 간단한 방법은 없을까?

const solution = (s) =>
  [...s].map((char, i) => {
    const count = s.slice(0, i).lastIndexOf(char);
    return count < 0 ? count : i - count;
  });

Map 객체와 같은 복잡한 방법으로 풀의하지 않아도 된다. 여기서 전개구문에 대한 새로운 접근을 보았다. 에를 들어서 let string = "hello"라는 문자열이 있다고 하자. 해당 문자열에 있어서, 아래의 두개의 코드는 동일한 결과를 가져다준다.

let string = "hello"
let a1 = [...string];
let a2 = string.split("")

즉 문자열을 split("")와 같이 하지 않아도, 전개구문을 통하여 간편하게 구현이 가능한 것이다. 이렇게 된 후에, 배열을 순회하며, map을 통하여 새로운 배열을 생성한다. 해당 요소를 검색하는 과정에서 lastIndexOf가 사용되였다. indexOf는 선언된 첫자리수에 대한 탐색이라면, lastIndexOf는 선언된 마지막 자리수에 대한 탐색이 이뤄진다. 코드를 보면, slice를 통해서 배열의 요소 가운데 일 부분을 취한 다음에 lastIndexOf를 탐색했다. 만약 찾을 수 없으면 -1이 반환될 것이다. 그렇지 않다면, 현재 index에서 counct로 계산된 값을 빼줌으로써 가장 가까운 글짜를 찾는 문제를 접근할 수 있게 되는 것이다.

profile
신학전공자의 개발자 도전기!!

0개의 댓글