거르기는 빈 배열을 하나 두고, 배열에서 값을 하나씩 꺼내 조건에 맞는 것만 그 배열에 담는 틀입니다. 바꿔 담기는 꺼낸 값을 바꿔서 담는 같은 틀입니다.
가격 가운데 4000원 넘는 것만 고르거나, 모든 가격을 500원씩 올린 새 가격표를 만들 때가 있습니다. 원래 배열의 칸을 지우거나 고치면 처음 가격표가 사라집니다. 거르기와 바꿔 담기는 원래 배열을 그대로 두고, 고른 값이나 바꾼 값을 새 배열에 담습니다. 이 레슨은 거르기와 바꿔 담기 코드를 직접 쓰는 틀 레슨입니다. 풀이 예제를 하위 목표 셋으로 나눠 읽고, 빈칸을 채우며 쓰기로 넘어갑니다.
1거르기의 세 하위 목표
가격 셋 가운데 4000원보다 비싼 값만 고르는 풀이 예제입니다. 그림은 이 코드에서 주석 머리줄 셋을 뺀 여덟 줄이라 줄 번호가 다릅니다.
const prices = [900, 4500, 700];
// 1. 빈 배열
const picked = [];
// 2. 하나씩 꺼내 (맞으면) 담기
for (const price of prices) {
if (price > 4000) {
picked.push(price);
}
}
// 3. 끝나고 쓰기
console.log(picked);
[ 4500 ]- 1. 빈 배열: 아직 아무것도 고르지 않았으니 담긴 값이 없습니다. 3번 줄이
picked를 새 빈 배열 ②에 붙입니다. 이 이름은 다시 붙이지 않고 담기만 하니const로 만듭니다. - 2. 하나씩 꺼내 (맞으면) 담기: 5번 줄의
for...of가 칸 값을 하나씩 꺼내price를 붙입니다. 6번 줄 조건이true인 바퀴에만 7번 줄의push가 꺼낸 값을 ② 끝에 더합니다. - 3. 끝나고 쓰기: 반복이 다 끝난 뒤 11번 줄이 한 번 찍습니다.
추적 표로 따라가면 ②에 언제 담기는지 보입니다.
| 줄 | 바뀐 이름 | 값 | 출력 |
|---|---|---|---|
| 1 | prices | ① [ 900, 4500, 700 ] | |
| 3 | picked | ② [] | |
| 5 | price | 900 | |
| 6 | 조건 price > 4000 | false | |
| 5 | price | 4500 | |
| 6 | 조건 price > 4000 | true | |
| 7 | picked | ② [ 4500 ] | |
| 5 | price | 700 | |
| 6 | 조건 price > 4000 | false | |
| 5 | 꺼낼 수 없음 — 반복 끝 | ||
| 11 | [ 4500 ] |
7번 줄의 push는 이름을 다시 붙이지 않고 배열 ②를 제자리에서 바꿉니다. 이런 줄도 바뀐 이름 칸에 그 이름을, 값 칸에 바뀐 배열을 적습니다. 꼬리표가 ② 그대로라 새 배열이 아니라 같은 배열입니다.
picked처럼 고른 값을 모아 담는 이름을 담는 그릇이라고 부릅니다. price는 가장 큰 값 찾기에서처럼 꺼낸 값을 차례로 받는 걸음꾼입니다. 원래 배열 ①은 처음 그대로입니다.
2빈 배열은 반복 앞에 한 번
1절 풀이 예제에서 빈 배열을 만드는 줄을 반복 안으로 옮기고, 바퀴마다 picked를 찍게 했습니다. 기준도 800으로 낮췄습니다. 마지막 줄까지 무엇이 찍힐지 정한 뒤 펼쳐 보세요.
const prices = [900, 4500, 700];
for (const price of prices) {
const picked = [];
if (price > 800) {
picked.push(price);
}
console.log(picked);
}
실행 결과 보기
[ 900 ]
[ 4500 ]
[]예상과 다르면 그곳이 배울 곳입니다. 3번 줄은 중괄호 안에 있어 바퀴마다 실행되고, 그때마다 picked를 새 빈 배열에 붙입니다. 그래서 둘째 바퀴의 picked에는 첫 바퀴에 담은 900이 없고, 셋째 바퀴에는 700이 800보다 작아 빈 배열이 찍힙니다.
picked는 바퀴마다 새로 만들어지는 이름이라 const로 만들어도 오류는 아닙니다. 값을 모으려면 담는 그릇은 반복 앞에서 한 번만 만듭니다.
예제
모두 500원 올리기
바꿔 담기도 하위 목표는 같습니다. 2번 하위 목표에서 조건 없이, 꺼낸 값을 바꾼 결과를 담습니다.
const prices = [900, 4500, 700];
const raised = [];
for (const price of prices) {
raised.push(price + 500);
}
console.log(raised);
console.log(prices);
[ 1400, 5000, 1200 ]
[ 900, 4500, 700 ]4번 줄은 price + 500으로 계산한 새 값을 raised에 담을 뿐, prices의 칸은 건드리지 않습니다. 그래서 7번 줄이 찍은 prices는 처음 그대로입니다.
자주 틀리는 곳
틀린 생각: 빈 배열을 반복 안에서 만들어도 담은 값은 쌓인다.
담는 줄과 같은 반복에 두면 한곳에 모일 것 같지만, 중괄호 안의 줄은 바퀴마다 실행됩니다. 2절에서 본 것처럼 바퀴마다 새 빈 배열이 생겨 앞 바퀴에 담은 값이 남지 않습니다. 찍는 줄을 반복 뒤로 옮기면 가장 큰 값 찾기의 best처럼 picked가 이미 사라져 picked is not defined로 멈춥니다.
맞는 규칙: 담는 그릇은 반복 앞에 한 번 만듭니다. 반복 안에서 만든 이름은 그 중괄호 안에서만 삽니다(규칙 4).
틀린 생각: for...of는 반복을 시작할 때 있던 값만 꺼낸다.
반복이 처음에 칸 수를 세어 두고 그만큼만 돈다고 보기 쉽습니다. 1절 풀이 예제에서 담는 곳을 prices로 쓰면 아래 코드가 됩니다. 스스로 끝나지 않는 코드라서 실행 결과를 싣지 않습니다.
const prices = [900, 4500, 700];
for (const price of prices) {
if (price > 4000) {
prices.push(price);
}
}
console.log(prices);
둘째 바퀴의 4번 줄이 4500을 prices 끝에 더하면, 700 다음에 그 4500도 꺼내 또 담습니다. 담을 때마다 꺼낼 칸이 하나씩 늘어 반복이 스스로 끝나지 않습니다. 배열이 끝없이 길어지다 메모리가 모자라 오류로 멈추고, 7번 줄은 끝내 찍히지 않습니다.
돌렸다면 터미널에서 Ctrl+C를 눌러 바로 멈춥니다.
맞는 규칙: for...of는 바퀴마다 그때의 배열에 다음 칸이 있는지 봅니다. 도는 동안 끝에 더한 값도 꺼내니, 고른 값은 반복 앞에서 만든 새 배열에 담습니다.
틀린 생각: const로 만든 빈 배열에는 값을 담을 수 없다.
const가 값을 얼린다고 읽으면 빈 배열은 끝까지 비어 있어야 할 것 같습니다. 그런데 1절 풀이 예제의 picked는 const로 만들었는데도 7번 줄이 4500을 담았습니다.
맞는 규칙: push는 이름을 다시 붙이지 않고 같은 배열을 제자리에서 바꿉니다. const가 막는 것은 picked = [4500];처럼 이름을 다른 배열에 다시 붙이는 일뿐입니다(규칙 3, 배열과 이름 둘).
확인 문제
1. 1절 풀이 예제에서 6번 줄의 기준만 4500으로 바꿨습니다. 무엇이 찍힐까요?
const prices = [900, 4500, 700];
// 1. 빈 배열
const picked = [];
// 2. 하나씩 꺼내 (맞으면) 담기
for (const price of prices) {
if (price > 4500) {
picked.push(price);
}
}
// 3. 끝나고 쓰기
console.log(picked);
힌트
4500을 꺼낸 바퀴에서 6번 줄 조건의 값을 먼저 적어 봅니다.
정답
[]세 바퀴 모두 6번 줄 조건이 false라 7번 줄이 한 번도 실행되지 않습니다. 담는 그릇 ②가 처음 그대로 비어 있어 빈 배열이 찍힙니다.
흔한 오답은 [ 4500 ]입니다. 4500과 4500을 견주면 true라고 본 것입니다(>= 연산자로 읽은 것). > 연산자는 같으면 false입니다.
2. 빈칸을 채워 코드를 완성하세요.
(가) 날마다 판 잔 수 가운데 15잔 이상인 값만 담습니다. [ 20, 15 ]가 찍히도록 한 줄의 빈칸을 채우세요.
const cups = [12, 20, 9, 15];
// 1. 빈 배열
const busy = [];
// 2. 하나씩 꺼내 (맞으면) 담기
for (const cup of cups) {
if (cup >= 15) {
________
}
}
// 3. 끝나고 쓰기
console.log(busy);
힌트
2번 하위 목표 '하나씩 꺼내 (맞으면) 담기'에서 담는 줄입니다.
정답
busy.push(cup);입니다.
const cups = [12, 20, 9, 15];
// 1. 빈 배열
const busy = [];
// 2. 하나씩 꺼내 (맞으면) 담기
for (const cup of cups) {
if (cup >= 15) {
busy.push(cup);
}
}
// 3. 끝나고 쓰기
console.log(busy);[ 20, 15 ]흔한 오답은 busy = busy.push(cup);입니다. push는 바뀐 배열이 아니라 늘어난 길이를 돌려주고(배열 바꾸기와 복사), busy는 const로 만든 이름이라 다시 붙일 수 없습니다. 그래서 20을 꺼낸 둘째 바퀴의 7번 줄에서 TypeError로 멈춥니다.
(나) 시험 점수 가운데 60점 이상만 담습니다. 2번 하위 목표를 통째로 채우세요. 다 채운 코드는 [ 80, 90 ]을 찍어야 하고, 1번 줄을 const scores = [40, 70];으로 바꾸면 [ 70 ]을 찍어야 합니다.
const scores = [80, 55, 90];
// 1. 빈 배열
const passed = [];
// 2. 하나씩 꺼내 (맞으면) 담기
// 3. 끝나고 쓰기
console.log(passed);
힌트
1절 풀이 예제의 2번 하위 목표와 같은 다섯 줄입니다. 꺼낸 값의 이름과 조건을 정하면 됩니다.
정답
const scores = [80, 55, 90];
// 1. 빈 배열
const passed = [];
// 2. 하나씩 꺼내 (맞으면) 담기
for (const score of scores) {
if (score >= 60) {
passed.push(score);
}
}
// 3. 끝나고 쓰기
console.log(passed);[ 80, 90 ]다르게 써도 되는 곳: 꺼낸 값의 이름은 score 말고 s처럼 지어도 됩니다. 조건을 score > 59로 써도 두 시험 값 모두 같은 값이 찍힙니다.
흔한 오답은 if 줄을 빼고 바퀴마다 passed.push(score);로 담는 것입니다. 그러면 거르지 않고 다 담아 [ 80, 55, 90 ]이 찍히고, 둘째 시험 값에서도 [ 70 ]이 아니라 [ 40, 70 ]이 찍힙니다.
3. 가격을 모두 500원 올리려고 아래처럼 썼습니다. 무엇이 찍힐까요?
const prices = [900, 4500, 700];
for (const price of prices) {
price = price + 500;
}
console.log(prices);
힌트
3번 줄이 다시 붙이려는 이름 price를 2번 줄에서 무엇으로 만들었는지 봅니다.
정답
main.js:3 price = price + 500; ^ TypeError: Assignment to constant variable. at main.js:3:92번 줄이 price를 const로 만들어서, 첫 바퀴의 3번 줄이 price를 다시 붙이려는 순간 TypeError로 멈춥니다. for...of는 바퀴마다 price를 새로 만들지만, 한 바퀴 안에서 다시 붙이는 일은 막습니다(for...of로 배열 돌기). 5번 줄은 실행되지 않습니다.
흔한 오답은 [ 1400, 5000, 1200 ]입니다. 이름 price가 배열의 칸 자체라서 다시 붙이면 칸이 바뀐다고 본 것입니다(따라 옮겨 간다는 생각). 다시 붙이기는 이름만 옮깁니다(규칙 3). 올린 값이 필요하면 예제처럼 새 배열에 바꿔 담습니다.
컴퓨터에서 해 보기(선택) — 점수에 들지 않습니다. 폰으로만 보고 있다면 건너뛰세요.
1절 코드(열한 줄)를 main.js에 저장해 돌린 뒤, 6번 줄의 조건을 price > 800으로, 다음에는 price < 4000으로 바꿔 봅니다. 무엇이 찍힐지 먼저 적고, 바꿔 돌려서 맞춰 봅니다.
저장하고 돌리는 법은 내 컴퓨터에서 돌려 보기에 있습니다.