JavaScript/알고리즘

프로그래머스 영어가 싫어요 JS ( 객체리터럴, Object.keys() / split()과 join()을 replace처럼 사용하기 )

hihiha2 2023. 7. 16. 21:04

문제 설명

영어가 싫은 머쓱이는 영어로 표기되어있는 숫자를 수로 바꾸려고 합니다. 문자열 numbers가 매개변수로 주어질 때, numbers를 정수로 바꿔 return 하도록 solution 함수를 완성해 주세요.

 

 

 

🙋‍♀️ 내 생각

문제를 보고 각각에 해당되는 값들을 대체하기 위해서 replace()를 써야겠다고 생각했다.

그리고 replace()를 쓰기 위해서 zero=0, one=1과 같이 해당되는 값들을 연결하기 위해서 객체를 만들었다.

 

 

 

✅ 내 코드

function solution(numbers) {
const obj = {
    zero: 0,
    one: 1,
    two: 2,
    three: 3,
    four: 4,
    five: 5,
    six: 6,
    seven: 7,
    eight: 8,
    nine: 9
}
    const reg = new RegExp(Object.keys(obj).join('|'), 'g');
    const str = numbers.replace(reg,(match)=>obj[match])
    return  Number(str)
}

우선 문자열과 숫자를 담기위해서 obj라는 이름으로 객체를 만들어서 문자열key값으로, 숫자value로 넣어주었다.

(해당되는 key값을 찾아서 그 값을 value로 대체하는 코드를 짜기 위해서)

 

그런 다음 replace를 사용해서 key값이 일치하면, 그 값을 value로 바꾼다.

이를 위해서 우선 replace의 첫번째 매개변수로 사용하기 위한 정규식을 만든다.

 

💻 학습한 것

1️⃣ replace의 형태

var newStr = str.replace(regexp|substr, newSubstr|function)

replace는 첫번째 매개변수에 바뀌어서 없어질 값을 넣고, 두번째 매개변수에 새롭게 대체되어 들어올 값을 넣는다.

첫번째 매개변수: 정규식 / 문자열

두번째 매개변수: 문자열 / 함수 

의 형태가 들어올 수 있다.

 

위의 문제에서 대체되어서 없어질 값을 처리하기 위해서  정규식 생성자를 이용한다.

정규식 생성자를 사용하여 정규식을 만든다.

(정규식 생성자가 아니어도, 어차피 숫자를 zero-nine까지 10가지이므로 일일이 지정해줘도 된다)

 

new RegExp()를 통해서 정규식 생성자를 만드는데 정규식은 매개변수로 패턴과 flags가 들어간다.

형태는 아래와 같다.

new RegExp(pattern[, flags])

 

다시 코드로 들어와서 보면 new RegExp의 매개변수로는 위에 만들어둔 obj라는 객체의 key값들을 |로 연결하여 넣기 위해서 Object.key(obj)와 join('|')을 사용한다. flag는 g를 넣어서 전역적으로 동작하도록 한다.

const reg = new RegExp(Object.keys(obj).join('|'), 'g');

 

 

2️⃣  Object.keys(): 객체의 속성 이름들을 일반적인 반복문과 동일한 순서로 순회되는 열거할 수 있는 배열로 반환

Object.keys(obj)

Object.keys()를 통해서 객체의 key값들만 뽑아올수가 있다. 예시를 보면 아래와 같다.

// 배열형 객체
const obj = { 0: 'a', 1: 'b', 2: 'c' };
console.log(Object.keys(obj)); // console: ['0', '1', '2']

 

 

Object.keys()를 공부하면서 특이하다고 생각한 점2가지있었다.

 

1. 단순배열에 사용하면 인덱스의 값이 담긴다.

// 단순 배열
const arr = ['a', 'b', 'c'];
console.log(Object.keys(arr)); // console: ['0', '1', '2']

2. key값들이 알아서 정렬된다.

// 키와 순서가 무작위인 유사 배열 객체
const anObj = { 100: 'a', 2: 'b', 7: 'c' };
console.log(Object.keys(anObj)); // console: ['2', '7', '100']

위의 예시를 보면 무작위로 된 배열이 순서대로 담긴것을 확인할 수 있다.

 

 

다시 코드로 돌아와서 보면, replace()의 매개변수로 사용하기 위해서

const reg = new RegExp(Object.keys(obj).join('|'), 'g');

1. new RegExp()를 통해서 정규식 생성자를 통해서 정규식을 만드는데

2. Object.keys(obj)를 통해서 obj라는 객체의 key값만을 뽑아서 

3. join('|')를 통해서 or과 같은 역할을 하도록 만든다.

('zero|one|two|three|four|five|six|seven|eight|nine')

4. flag를 'g'로 선언하여 전역적으로 작동하도록 한다.

5. 해당값을 reg라는 변수에 담는다.

 

로 정리할 수 있다.

 

 

다음은 replace()를 통해서 위에서 만든 정규식을 활용하여 해당하는 값들을 숫자로 변환하도록 만든다.

const str = numbers.replace(reg,(match)=>obj[match])

replace()를 통해서 reg를 숫자로 변환하는데, replace()의 두번째 매개변수를 함수로 만들어서 숫자변환을 하도록 한다.

(match)=> obj[match]를 통해서 reg와 일치하는 값(obj의 key값들)obj의 value로 변환한다.

 

이렇게하면 numbers에서 해당key값과 일치하면, 그 문자열을 숫자로 대체한다.

그런 다음 그 값을 str이라는 변수에 담는다.

 

마지막으로 문자열 str을 Number()를 통해서 숫자로 바꾼다음 리턴한다.

 

 

 

 

💻  다른사람 코드중에 배울 것 1

function solution(numbers) {
    const obj = {
        zero: 0, one: 1, two: 2, three: 3, four: 4,
        five: 5, six: 6, seven: 7, eight: 8, nine: 9
    };

    const num = numbers.replace(/zero|one|two|three|four|five|six|seven|eight|nine/g, (v) => {
        return obj[v];
    });

    return Number(num);
}

obj라는 객체를 만들고 그 값을 replace()를 통해서 숫자로 변환한것은 똑같다.

 

다른점은 replace()의 첫번째 매개변수로 들어가는 '변경되어 사라질 값'을 정규식 생성자가 아니라 정규식 리터럴을 사용했다는 점이다.

정규식 생성자로 만들어서 가공하는 코드가 없어서 코드가 조금 더 간결해보인다.

만약 key값으로 사용될 값이 엄청 나게 많다면 정규식 생성자를 사용하는 것이 더 효율적이겠지만, 위의 문제에서 처리해야 할 key값은 zero-nine까지 총10개밖에 안되므로 위와 같이 해당하는 값을 일일이 입력하는 리터럴의 방식도 좋을것같다는 생각이 들었다.

 

 

 

💻  다른사람 코드중에 배울 것 2

function solution(numbers) {
    const number = ["zero","one","two","three", "four", "five", "six", "seven", "eight", "nine"]
    for(let i = 0 ; i<number.length; i++){
        numbers = numbers.split(number[i]).join(i)
    }
    return +numbers
}

배열 split()을 사용해서 나와는 완전히 다른 방식으로 푼 코드가 있어서 공부해보았다.

우선 number라는 이름의 변수에 "zero"-"nine"까지의 값을 배열로 담는다.

 

그런다음, for문을 이용해서 number배열을 순회한다.

배열을 순회하면서 numbers.split(number[i])를 통해 numbers배열을 number의 i번째 인덱스를 구분자로 사용해서 문자열을 쪼갠 다음에 배열에 담고, 그 값을 다시 i를 사용해서 잇는다. 

split의 구분자로 사용되어진 문자열은 ""로 변화되는 성질과 join(i)를 사용하면 i로 잇는 성질을 이용하여 대체되는 효과를 얻을 수 있다.

 

 

이 부분을 이해하기 위해서 조금 자세하게 공부해보았는데, 위의 테스트2를 기준으로 예시를 통해서 이해하니까 조금 더 이해가 빨랐다.

"onefourzerosixseven"라는 문자열이 numbers를 통해서 들어간다고 해보자.

for문을 통해서 i=0부터 순회할 것이다.

 

i = 0인 경우, number[0]는 "zero"이므로 ["onefour","sixseven"]와 같이 zero를 기준으로 문자열을 나누고 다시 배열에 담길 것이다.

 그런다음 다시 join(i)를 통해 i로 떨어진 두개의 문자열을 연결하면서 하나의 문자열로 변환한다. 

그래서 "onefourzerosixseven"은 "onefour0sixseven"으로 바뀔 것이다.

 

i=1인 경우, number[1]은 "one"이므로 ["""four0sixseven"]로  나뉠 것이다. 다시 이 값을 join(i)를 통해 연결하면 "1four0sixseven"이 된다.

 

이런 과정을 i=2, i=3..과 같이 number.length만큼 반복한다.

 

그런 다음 마지막으로 +를 통해 값을 숫자로 변환한뒤 리턴한다.

 

 

 

 

🙋‍♀️ 내 생각

지난 번에 다른 문제를 풀면서 Object.values()를 이용해서 객체의 value값만을 얻는 방법을 익혔는데 이번 문제를 통해서 Object.keys()로 key값만 추출하는 것도 알게되었다. 알고리즘 문제이지만 자바스크립트에서 객체를 어떤식으로 처리하는지 직접 쳐보면서 언어에 대해 더 깊숙히 이해할 수 있는 기회가 되는것 같아서 좋다.

 

또 이번문제를 풀면서 깨달은 것은 join()의 유용함이다. 평소에도 자주 쓰던 메서드이지만 위의 문제를 풀때처럼 join('|')과 같이 사용하여 정규식 생성자로 사용할 값을 |를 통해서 모두 or처리를 해준다던지, join(i)를 통해서 ""인 부분들을 i라는 숫자의 값을 넣을 수 있는 것이 매우 편하게 느껴졌다. 또 이런 성질을 이용해서 split()에 적으면 마치 값을 대체하는 역할을 할 수 있는 점이 흥미로웠다.