문제 상황
다음과 같이 데모용 신용카드 번호들이 담긴 배열이 있다고 가정해 보겠습니다.
const arr = ['4916-2600-1804-0530', '4779-252888-3972', '4252-278893-7978', '4556-4242-9283-2260'];
이 배열을 인자로 받아 각 번호에서 숫자의 합이 가장 큰 신용카드 번호를 반환하는 함수를 작성해야 합니다. 만약 서로 다른 카드 번호가 같은 합을 가진다면, 그중 마지막에 위치한 카드 번호를 반환해야 한다는 조건이 붙습니다.
해결 접근 방식
이 문제는 다음과 같은 단계로 풀어갈 수 있습니다.
① 각 카드 번호 문자열을 하이픈(-)을 기준으로 분리합니다.
② 분리된 각 조각을 숫자로 변환한 뒤 모두 더해 합계를 구합니다.
③ 배열의 모든 카드 번호에 대해 위 과정을 반복하며 합계 목록을 만듭니다.
④ 합계 목록에서 최댓값을 찾고, 그 값이 마지막으로 등장하는 인덱스를 구합니다.
⑤ 해당 인덱스에 있는 원본 카드 번호를 결과로 반환합니다.
예제 코드
const arr = ['4916-2600-1804-0530', '4779-252888-3972', '4252-278893-7978', '4556-4242-9283-2260'];
const findGreatestNumber = (arr) => {
let n, i = 0, sums;
sums = [];
while (i < arr.length) {
sums.push(sum(arr[i]));
i++;
}
n = sums.lastIndexOf(Math.max.apply(null, sums));
return arr[n];
}
const sum = (num) => {
let i, integers, res;
integers = num.split(/[-]+/g);
i = 0;
res = 0;
while (i < integers.length) {
res += Number(integers[i]);
i++;
}
return res;
};
console.log(findGreatestNumber(arr));
코드 설명
sum() 함수는 정규 표현식 /[-]+/g를 사용해 카드 번호 문자열을 하이픈 기준으로 분리하고, 각 조각을 Number()로 변환해 하나씩 누적한 합계를 반환합니다.
findGreatestNumber() 함수는 while 루프를 돌며 각 카드 번호의 합계를 sums 배열에 차례대로 저장합니다. 이후 Math.max.apply(null, sums)로 최댓값을 구하고, lastIndexOf()로 그 값이 마지막으로 등장하는 인덱스를 찾습니다. 덕분에 최댓값이 여러 번 나타나더라도 항상 뒤쪽에 있는 카드 번호가 선택되어, 문제에서 요구한 조건을 그대로 만족합니다.
더 간결한 ES6 버전
map(), reduce(), 전개 연산자(spread operator)를 활용하면 같은 로직을 훨씬 짧고 읽기 쉽게 표현할 수 있습니다.
const arr = ['4916-2600-1804-0530', '4779-252888-3972', '4252-278893-7978', '4556-4242-9283-2260'];
// 하이픈으로 구분된 각 숫자 조각의 합을 계산하는 함수
const digitSum = num =>
num.split('-').reduce((acc, cur) => acc + Number(cur), 0);
// 합계 배열에서 최댓값이 마지막으로 등장하는 인덱스를 찾아 반환
const findGreatestNumber = arr => {
const sums = arr.map(digitSum);
return arr[sums.lastIndexOf(Math.max(...sums))];
};
console.log(findGreatestNumber(arr));
실행 결과
콘솔에는 다음과 같이 출력됩니다.
4252-278893-7978
네 개의 카드 번호 중 '4252-278893-7978'의 숫자 합이 가장 크기 때문에 이 번호가 최종 결과로 반환됩니다.