본문으로 건너뛰기

거르기와 바꿔 담기

자바스크립트 목차배열레슨 37 / 76

목차

거르기는 빈 배열을 하나 두고, 배열에서 값을 하나씩 꺼내 조건에 맞는 것만 그 배열에 담는 틀입니다. 바꿔 담기는 꺼낸 값을 바꿔서 담는 같은 틀입니다.

가격 가운데 4000원 넘는 것만 고르거나, 모든 가격을 500원씩 올린 새 가격표를 만들 때가 있습니다. 원래 배열의 칸을 지우거나 고치면 처음 가격표가 사라집니다. 거르기와 바꿔 담기는 원래 배열을 그대로 두고, 고른 값이나 바꾼 값을 새 배열에 담습니다. 이 레슨은 거르기와 바꿔 담기 코드를 직접 쓰는 틀 레슨입니다. 풀이 예제를 하위 목표 셋으로 나눠 읽고, 빈칸을 채우며 쓰기로 넘어갑니다.

13단계 중 1단계. 가격 셋 가운데 4000원보다 비싼 값만 하위 목표 셋으로 골라 담습니다.

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번 줄이 한 번 찍습니다.

추적 표로 따라가면 ②에 언제 담기는지 보입니다.

줄바뀐 이름값출력
1prices① [ 900, 4500, 700 ]
3picked② []
5price900
6조건 price > 4000false
5price4500
6조건 price > 4000true
7picked② [ 4500 ]
5price700
6조건 price > 4000false
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:9

2번 줄이 price를 const로 만들어서, 첫 바퀴의 3번 줄이 price를 다시 붙이려는 순간 TypeError로 멈춥니다. for...of는 바퀴마다 price를 새로 만들지만, 한 바퀴 안에서 다시 붙이는 일은 막습니다(for...of로 배열 돌기). 5번 줄은 실행되지 않습니다.

흔한 오답은 [ 1400, 5000, 1200 ]입니다. 이름 price가 배열의 칸 자체라서 다시 붙이면 칸이 바뀐다고 본 것입니다(따라 옮겨 간다는 생각). 다시 붙이기는 이름만 옮깁니다(규칙 3). 올린 값이 필요하면 예제처럼 새 배열에 바꿔 담습니다.

컴퓨터에서 해 보기(선택) — 점수에 들지 않습니다. 폰으로만 보고 있다면 건너뛰세요. 1절 코드(열한 줄)를 main.js에 저장해 돌린 뒤, 6번 줄의 조건을 price > 800으로, 다음에는 price < 4000으로 바꿔 봅니다. 무엇이 찍힐지 먼저 적고, 바꿔 돌려서 맞춰 봅니다. 저장하고 돌리는 법은 내 컴퓨터에서 돌려 보기에 있습니다.

끝까지 내려오면 저절로 표시됩니다. 이 기기에만 저장합니다.

자바스크립트 목차로