가장 큰 값 찾기는 첫 값을 지금까지 가장 큰 값으로 두고, 배열에서 값을 하나씩 꺼내 그보다 크면 바꿔 가는 틀입니다. 값 하나만 필요하면 Math.max(...prices)처럼 한 줄로도 구합니다.
메뉴 가운데 가장 비싼 값, 한 주 가운데 가장 많이 판 날의 잔 수처럼 '가장 ○○한 값'을 찾는 일은 자주 나옵니다. 사람은 눈으로 한꺼번에 훑지만, 코드는 한 번에 두 값만 견줍니다. 그래서 지금까지 본 값 가운데 가장 큰 값에 이름 하나를 붙여 두고, 새 값을 꺼낼 때마다 그것과 견줍니다. 이 레슨은 가장 큰 값을 찾는 코드를 직접 쓰는 틀 레슨입니다. 풀이 예제를 하위 목표 셋으로 나눠 읽고, 빈칸을 채우며 쓰기로 넘어갑니다.
1가장 큰 값 찾기의 세 하위 목표
가격 셋 가운데 가장 큰 값을 찾는 풀이 예제입니다. 그림은 이 코드에서 주석 머리줄 셋을 뺀 여덟 줄이라 줄 번호가 다릅니다.
const prices = [900, 4500, 700];
// 1. 첫 값을 최고로
let best = prices[0];
// 2. 하나씩 꺼내 견주어 바꾸기
for (const price of prices) {
if (price > best) {
best = price;
}
}
// 3. 끝나고 쓰기
console.log(best);
4500- 1. 첫 값을 최고로: 값을 하나만 봤다면 그 값이 지금까지 가장 큰 값입니다. 3번 줄이
best를 첫 값prices[0], 곧 900에 붙입니다. 반복 안에서 다시 붙일 이름이라let으로 만듭니다. - 2. 하나씩 꺼내 견주어 바꾸기: 5번 줄의
for...of가 칸 값을 하나씩 꺼내price를 붙입니다. 6번 줄이 꺼낸 값과 지금의best를 견주어, 더 크면 7번 줄이best를 그 값에 다시 붙입니다. - 3. 끝나고 쓰기: 반복이 다 끝난 뒤 11번 줄이 한 번 찍습니다.
추적 표로 따라가면 best가 언제 바뀌는지 보입니다.
| 줄 | 바뀐 이름 | 값 | 출력 |
|---|---|---|---|
| 1 | prices | ① [ 900, 4500, 700 ] | |
| 3 | best | 900 | |
| 5 | price | 900 | |
| 6 | 조건 price > best | false | |
| 5 | price | 4500 | |
| 6 | 조건 price > best | true | |
| 7 | best | 4500 | |
| 5 | price | 700 | |
| 6 | 조건 price > best | false | |
| 5 | 꺼낼 수 없음 — 반복 끝 | ||
| 11 | 4500 |
첫 바퀴는 900과 900을 견줍니다. 같은 값은 더 크지 않아 best가 바뀌지 않습니다. 셋째 바퀴의 700은 지금의 best 4500보다 작아 그대로입니다.
best처럼 지금까지 본 값 가운데 가장 좋은 값을 붙들어 두는 이름을 가장 좋은 값지기라고 부릅니다. price는 합 모으기의 n처럼 꺼낸 값을 차례로 받는 걸음꾼입니다.
2무엇과 견주는가
한 주 동안 날마다 판 잔 수에서 가장 많은 값을 찾습니다. best가 바뀔 때마다 찍게 했습니다. 마지막 줄까지 무엇이 찍힐지 정한 뒤 펼쳐 보세요.
const cups = [12, 20, 9, 15];
let best = cups[0];
for (const n of cups) {
if (n > best) {
best = n;
console.log(best);
}
}
실행 결과 보기
20예상과 다르면 그곳이 배울 곳입니다. 첫 바퀴의 12는 best와 같아 바뀌지 않고, 둘째 바퀴의 20에서 한 번 바뀝니다. 넷째 바퀴의 15는 바로 앞 값 9보다 크지만 지금의 best 20보다 작아 바뀌지 않습니다.
꺼낸 값은 바로 앞 값이 아니라 지금까지 가장 큰 값과 견줍니다. 그래서 best가 바뀌는 횟수는 바퀴 수보다 적습니다.
3Math.max와 견주기
자바스크립트에서는 사칙 계산과 나머지에서 본 수학 도구 묶음 Math의 Math.max가 가장 큰 값을, Math.min이 가장 작은 값을 돌려줍니다. 둘 다 견줄 값을 인자로 하나씩 받습니다.
const prices = [900, 4500, 700];
const max = Math.max(...prices);
const min = Math.min(...prices);
console.log(max);
console.log(min);
4500
700Math.max(...prices)는 칸 값을 인자 자리에 펼쳐 Math.max(900, 4500, 700)으로 부른 것과 같습니다. 배열 바꾸기와 복사에서 대괄호 안에 펼쳐 사본을 만든 그 펼치기입니다.
틀로 찾은 4500과 같습니다. 값 하나만 필요하면 Math.max로 충분합니다. 틀은 best가 바뀐 횟수를 세거나 조건을 더하는 것처럼 Math.max 하나로는 안 되는 일로 넓혀 씁니다.
예제
빈 배열에서 찾으면
배열이 비어 있으면 가장 큰 값이 없습니다. 틀로 쓴 코드와 Math.max가 무엇을 내놓는지 봅니다.
const prices = [];
let best = prices[0];
for (const price of prices) {
if (price > best) {
best = price;
}
}
const max = Math.max(...prices);
console.log(best, max);
undefined -Infinity빈 배열에는 0번 칸이 없어 2번 줄의 best가 undefined에 붙고, 반복은 한 바퀴도 돌지 않습니다. Math.max는 견줄 값이 하나도 없으면 -Infinity(음의 무한대)를 돌려줍니다.
둘 다 오류 없이 지나가서, 틀린 값이 다음 계산으로 그대로 흘러갑니다. 비어 있을 수 있는 배열이라면 찾기 전에 prices.length가 0인지 먼저 봅니다.
자주 틀리는 곳
틀린 생각: 가장 큰 값을 찾을 때도 합 모으기처럼 best를 0에서 시작하면 된다.
합은 아무것도 더하지 않았을 때가 0이라 0에서 시작했습니다. 가장 큰 값은 다릅니다. 값이 모두 0보다 작으면 0이 끝까지 가장 큰 값으로 남습니다.
const temps = [-5, -2, -8];
let best = 0;
for (const temp of temps) {
if (temp > best) {
best = temp;
}
}
console.log(best);
0아침 기온 셋 가운데 가장 높은 값은 -2인데, 배열에 없는 0이 찍혔습니다.
맞는 규칙: best는 지금까지 본 값 가운데 가장 큰 값이라, 배열에 없는 0이 아니라 첫 값에서 시작합니다. 그러면 값이 모두 음수여도 맞습니다.
틀린 생각: 첫 값을 최고로 두는 줄은 반복 안에 둬도 된다.
처음에 한 번 정하는 값이라 어디에 두든 같아 보이지만, 중괄호 안의 줄은 바퀴마다 실행됩니다. 바퀴마다 best를 찍어 봅니다.
const prices = [900, 4500, 700];
for (const price of prices) {
let best = prices[0];
if (price > best) {
best = price;
}
console.log(best);
}
900
4500
9003번 줄이 바퀴마다 새 best를 첫 값 900에 붙여서, 둘째 바퀴에 찾은 4500을 셋째 바퀴에 잊습니다. 7번 줄을 반복 뒤로 옮기면 합 모으기와 세기의 total처럼 best가 이미 사라져 best is not defined로 멈춥니다.
맞는 규칙: 1번 하위 목표는 반복 앞에 한 번 둡니다. 반복 안에 두면 바퀴마다 첫 값으로 돌아가고, 그 안에서 let으로 만든 이름은 그 중괄호 안에서만 삽니다(규칙 4).
틀린 생각: Math.max에 배열을 그대로 주면 가장 큰 칸 값을 돌려준다.
파이썬의 max(prices)는 리스트를 통째로 받아 가장 큰 값을 돌려줍니다. 자바스크립트에서 같은 꼴로 쓰면 오류 없이 엉뚱한 값이 나옵니다.
const prices = [900, 4500, 700];
console.log(Math.max(prices));
NaNMath.max는 받은 인자를 수로 바꿔 견줍니다. 칸이 여럿인 배열을 통째로 받으면 수가 되지 못해 '수가 아님' NaN이 됩니다(글자를 수로: Number와 NaN).
맞는 규칙: 배열의 값으로 Math.max를 부를 때는 Math.max(...prices)처럼 펼쳐서 칸 값을 하나씩 넘깁니다.
확인 문제
1. 1절 풀이 예제에서 가격의 차례만 바꿨습니다. 무엇이 찍힐까요?
const prices = [4500, 900, 700];
// 1. 첫 값을 최고로
let best = prices[0];
// 2. 하나씩 꺼내 견주어 바꾸기
for (const price of prices) {
if (price > best) {
best = price;
}
}
// 3. 끝나고 쓰기
console.log(best);
힌트
바퀴마다 6번 줄 조건의 값을 추적 표에 적어 봅니다.
정답
4500best가 첫 값 4500에 붙은 뒤, 세 바퀴 모두 6번 줄 조건이 false라 한 번도 바뀌지 않습니다.
흔한 오답은 700입니다. best가 마지막에 꺼낸 값에 붙는다고 본 것입니다(마지막 값이 남는다는 생각). best는 6번 줄 조건이 true일 때만 바뀝니다.
2. 빈칸을 채워 코드를 완성하세요.
(가) 90이 찍히도록 한 줄의 빈칸을 채우세요.
const scores = [70, 90, 50];
// 1. 첫 값을 최고로
let best = scores[0];
// 2. 하나씩 꺼내 견주어 바꾸기
for (const score of scores) {
if (score > best) {
________
}
}
// 3. 끝나고 쓰기
console.log(best);
힌트
2번 하위 목표 '하나씩 꺼내 견주어 바꾸기'에서 바꾸는 줄입니다.
정답
best = score;입니다.
const scores = [70, 90, 50];
// 1. 첫 값을 최고로
let best = scores[0];
// 2. 하나씩 꺼내 견주어 바꾸기
for (const score of scores) {
if (score > best) {
best = score;
}
}
// 3. 끝나고 쓰기
console.log(best);90다르게 써도 되는 곳: best = Math.max(best, score);도 90이 찍힙니다. 빈칸에 best = 90;을 바로 써도 90이 찍히지만, 배열의 값을 바꾸면 따라 바뀌지 않습니다.
흔한 오답은 score = best;입니다. score는 5번 줄에서 const로 만든 이름이라 다시 붙일 수 없어, 둘째 바퀴의 7번 줄에서 TypeError로 멈춥니다(이름표 다시 붙이기와 const). =는 왼쪽 이름을 오른쪽 값에 붙입니다(규칙 2).
(나) 날마다 판 잔 수 가운데 가장 많은 값을 찾습니다. 2번 하위 목표를 통째로 채우세요. 다 채운 코드는 15를 찍어야 하고, 1번 줄을 const cups = [3, 8, 5];로 바꾸면 8을 찍어야 합니다.
const cups = [9, 15, 12];
// 1. 첫 값을 최고로
let best = cups[0];
// 2. 하나씩 꺼내 견주어 바꾸기
// 3. 끝나고 쓰기
console.log(best);
힌트
1절 풀이 예제의 2번 하위 목표와 같은 다섯 줄입니다. 꺼낸 값의 이름만 정하면 됩니다.
정답
const cups = [9, 15, 12];
// 1. 첫 값을 최고로
let best = cups[0];
// 2. 하나씩 꺼내 견주어 바꾸기
for (const cup of cups) {
if (cup > best) {
best = cup;
}
}
// 3. 끝나고 쓰기
console.log(best);15다르게 써도 되는 곳: 꺼낸 값의 이름은 cup 말고 n처럼 지어도 됩니다. 조건을 cup >= best로 써도 두 시험 값 모두 같은 값이 찍힙니다.
흔한 오답은 if 줄을 빼고 바퀴마다 best = cup;으로 붙이는 것입니다. 그러면 마지막 값 12가 찍히고, 둘째 시험 값에서도 8이 아니라 5가 찍힙니다.
3. 시험 점수에서 가장 높은 점수와, 가장 높은 점수가 바뀐 횟수를 찍습니다. 무엇이 찍힐까요?
const scores = [80, 95, 95, 70];
let best = scores[0];
let count = 0;
for (const score of scores) {
if (score > best) {
best = score;
count = count + 1;
}
}
console.log(best, count);
힌트
바퀴마다 5번 줄 조건이 true인지 false인지 추적 표에 적어 봅니다.
정답
95 180은 처음 best와 같고, 첫 95에서 한 번 바뀝니다. 둘째 95는 지금의 best와 같아서, 70은 작아서 바뀌지 않습니다.
흔한 오답은 95 3입니다. 같은 값이어도 바꾼다고 본 것입니다(>= 연산자로 읽은 것). > 연산자는 같으면 false입니다.
컴퓨터에서 해 보기(선택) — 점수에 들지 않습니다. 폰으로만 보고 있다면 건너뛰세요.
1절 코드(열한 줄)를 main.js에 저장해 돌린 뒤, 3번 줄을 let best = 0;으로, 6번 줄의 > 연산자를 < 연산자로 바꿔 가장 싼 값을 찾아 봅니다. 무엇이 찍힐지 먼저 적고, 바꿔 돌려서 맞춰 봅니다. 700이 아니면 자주 틀리는 곳 첫째를 다시 봅니다.
저장하고 돌리는 법은 내 컴퓨터에서 돌려 보기에 있습니다.