<수식 최대화> 문제는 인자로 들어오는 "100-200*300-500+20"
형식의 문자열을 3가지의 연산 문자 (+, -, *)
의 우선순위를 다르게 하여 계산한 결과 중 가장 큰 값을 반환시키는 문제다.
이 문제를 풀기 위해 먼저 문자열을 split()
메서드로 나눠야겠다는 생각을 했다.
const a = '100-200*300-500+20';
const b = a.split('');
console.log(b);
// ['1', '0', '0', '-', '2', '0', '0', '*', '3', '0', '0', '-', '5', '0', '0', '+', '2', '0']
확실히 위의 방법으로 나누어선 안되겠다. 숫자와 연산자를 분리해서 나누고 싶었는데, 반복문이나 reduce()
메서드를 사용하지 않고 split()
메서드만을 사용하여 나누는 방법이 있을 것만 같았다. 검색을 해보니 구분자를 포함하려면 정규식에 괄호()
를 넣어야한다고 했다. 그렇게 만들어진 식이다.
const a = '100-200*300-500+20';
const b = a.split(/(\*|\+|\-)/);
console.log(b);
// ['100', '-', '200', '*', '300', '-', '500', '+', '20'];
사실 이 친구를 아래와 같이 쓸 수도 있다.
const a = '100-200*300-500+20';
const b = a.split(/\b/);
console.log(b);
// ['100', '-', '200', '*', '300', '-', '500', '+', '20'];
정규 표현식 \b
는 이전 포스팅에서도 다뤘었다. word boundary, 즉 단어의 경계에 대응하는 정규 표현식이다. non-word character ([^_a-zA-Z0-9])
인 요소들을 가리킨다. 사실 위와 같이 쓰면 연산자들이 제거된 상태로 할당될 줄 알고 a.split(/(\b)/)
의 형식으로 써야 하는 게 아닌가 생각했는데, 아니었다!!!! \b
는 어떤 지점을 의미하는 것이지, 특정 문자와 매칭되는 것이 아니기 때문이다. 충 격 !
splice의 인자들에 대한 이해가 부족했다!
const cal = {
'+': (a, b) => a + b,
'*': (a, b) => a * b,
'-': (a, b) => a - b
}
const search = (arr, o) => {
for (let i = arr.indexOf(o); i !== -1; i = arr.indexOf(o)) {
arr[i - 1] = cal[o](arr[i - 1], arr[i + 1]);
arr.splice(i, 2);
}
return arr;
}
위의 search()
함수에는 [200, '+', 300, '-', 10, '*', 200, '-', 30]
의 형태의 인자 arr
과 '+'
, '-'
, '*'
형태의 인자 o
가 들어온다. 반복문을 돌면서 만약 arr
에 o
가 포함되어 있는 경우 해당 인덱스(i
) 앞(i - 1
), 뒤(i + 1
)를 cal()
함수로 계산한 뒤 앞의 인덱스(i - 1
)에 재할당하고 뒤의 두개(i, i + 1
)를 splice()
메서드로 제거하는 식이다. 이는 내가 splice() 메서드를 제대로 이해하지 못하고 있다는!!!!!!! 뜻이었다!!!!!!
array.splice(start[, deleteCount[, item1[, item2[, ...]]]]);
splice()
함수는 3개의 인자를 받을 수 있다. 지금까지 2개만 사용해왔기 때문에 마지막 인자를 사용해 본 적이 없는 것 같다.
- start : 배열의 변경을 시작할 인덱스
- deleteCount : 배열에서 제거할 요소의 수
- item : 배열에 추가할 요소
세 번째 인자를 활용하면 위의 식은 다음과 같이 바뀔 수 있다!
const search = (arr, o) => {
for (let i = arr.indexOf(o); i !== -1; i = arr.indexOf(o)) {
arr.splice(i - 1, 3, cal[o](arr[i - 1], arr[i + 1]));
}
return arr;
}
<방금그곡>의 인자에는 [C, C#, D, D#, E, F, F#, G, G#, A, A#, B]
으로 구성된 악보들이 주어진다. 악보가 문자열로 주어지기 때문에 #
이 붙은 음들이 불편해지기 시작한다. 때문에 #
이 붙은 녀석들을 다른 문자로 치환하기로 했다.
const replace = (o) => {
const n = {'C#': 'H', 'D#': 'I', 'F#': 'J', 'G#': 'K', 'A#': 'L'};
Object.keys(n).forEach((el) => {
if (o.includes(el)) o = o.split(el).join(n[el])
});
return o;
}
나는 반복문을 통해 n
의 키로 split()
하고 해당 키의 값으로 join()
하는 방법을 사용했다. 하지만 replaceAll()
이라는 메서드를 사용하면 그럴 필요가 없다!
const replace = (o) => {
const n = {'C#': 'H', 'D#': 'I', 'F#': 'J', 'G#': 'K', 'A#': 'L'};
Object.keys(n).forEach((el) => o = o.replaceAll(el, n[el]));
return o;
}
replaceAll()
메서드는 replace()
메서드와 같은 방식으로 작동한다. 다른 점은 replace()
메서드는 일치하는 문자열이 여러 개인 경우 가장 먼저 찾은(앞에 있는) 문자열만 치환해 주지만, replaceAll()
메서드는 일치하는 모든 문자열을 치환해 준다. 두 메서드 모두 원본을 변형시키지 않고 새로운 문자열을 반환한다.