Skip to content
MinJeong Hong edited this page Jan 9, 2026 · 1 revision

코딩테스트 객체 다루기 룰북

1. 객체 초기화 패턴

기본 객체 초기화

class Manager {
  constructor() {
    this.record = {}; // 빈 객체로 시작
    this.data = []; // 빈 배열로 시작
  }
}

초기값을 가진 객체 생성

// reduce로 빈 객체부터 시작
const result = items.reduce((acc, item) => {
  // ... 로직
  return acc;
}, {});

2. 객체에 데이터 추가/업데이트 패턴

키 기반으로 배열 저장 (중요!)

가장 자주 사용되는 패턴

// 배열 데이터를 키별로 그룹화
const record = {};

rawData.forEach(([key, value]) => {
  if (!record[key]) {
    // 키가 없으면 배열 생성
    record[key] = [];
  }
  record[key].push(value); // 배열에 추가
});

// 예시: { '크루1': [데이터1, 데이터2], '크루2': [데이터3] }

객체 속성 직접 수정

const target = records[index];
target.checkIn = [hour, min];
target.isAttendance = true;

스프레드 연산자로 속성 추가/수정

// 기존 객체에 속성 추가
const newData = { ...data, worker: '김철수' };

// 여러 속성 한번에 추가
const updated = { ...prev, checkIn: time, isAttendance: true };

3. 객체 순회 및 변환 패턴

Object.keys() - 키 배열 얻기

const products = { 콜라: 1000, 사이다: 1500 };
const names = Object.keys(products); // ['콜라', '사이다']

Object.entries() - [키, 값] 배열 얻기

const products = { 콜라: 1000, 사이다: 1500 };

Object.entries(products).forEach(([name, price]) => {
  console.log(name, price);
});

// 객체를 배열로 변환
const entries = Object.entries(products);
// [['콜라', 1000], ['사이다', 1500]]

reduce로 객체 생성

// 배열 → 객체 변환
const result = items.reduce((acc, item) => {
  acc[item.name] = item.value;
  return acc;
}, {});

// 예시: reduce로 그룹화
const grouped = lines.reduce((acc, line) => {
  const [name, value] = line.split(',');
  if (!acc[name]) {
    acc[name] = [];
  }
  acc[name].push(value);
  return acc;
}, {});

Object.entries + reduce - 객체 변환

// 객체의 모든 값을 변환
const transformed = Object.entries(obj).reduce((acc, [key, value]) => {
  acc[key] = process(value);
  return acc;
}, {});

// 객체 필터링
const filtered = Object.entries(obj).reduce((acc, [key, value]) => {
  if (condition(value)) {
    acc[key] = value;
  }
  return acc;
}, {});

4. 객체 데이터 조회 패턴

find / findIndex - 객체 배열에서 검색

const records = [
  { day: 5, checkIn: [10, 30] },
  { day: 6, checkIn: [9, 0] },
];

// 특정 조건의 객체 찾기
const target = records.find((record) => record.day === 5);

// 인덱스 찾기
const index = records.findIndex((record) => record.day === 5);

filter - 조건에 맞는 객체 필터링

const filtered = records.filter((record) => record.isAttendance);

map - 객체 변환

const names = records.map((record) => record.name);
const transformed = records.map((record) => ({
  ...record,
  newProperty: value,
}));

5. 객체 복사 패턴

얕은 복사 (Shallow Copy)

// 스프레드 연산자
const copy = { ...original };

// 이전 상태 저장용
const prev = { ...records[index] };

객체 배열 복사

// 배열 전체 복사
const newArray = [...oldArray];

// 객체 배열에서 특정 객체만 복사
const copiedItem = { ...array[index] };

6. 객체 존재 확인 및 안전 접근

키 존재 확인

// 키가 있는지 확인
if (!obj[key]) {
  obj[key] = []; // 없으면 초기화
}

// 또는
if (key in obj) {
  // 키가 존재함
}

옵셔널 체이닝 및 기본값

// 안전한 접근
const records = this.record[nickname] || [];

// 중첩 객체 안전 접근
const value = obj?.level1?.level2 ?? 'default';

7. 객체 집계 및 계산 패턴

reduce로 집계 계산

// 객체 배열을 순회하며 집계
const total = items.reduce((sum, item) => {
  return sum + item.price * item.quantity;
}, 0);

// 복잡한 집계 객체 생성
const summary = items.reduce(
  (acc, item) => {
    if (item.type === 'promotion') {
      acc.promotionCount += item.quantity;
    } else {
      acc.regularCount += item.quantity;
    }
    acc.total += item.quantity;
    return acc;
  },
  { promotionCount: 0, regularCount: 0, total: 0 }
);

Object.entries로 객체 값 계산

// 객체의 모든 값 합산
const total = Object.entries(obj).reduce((sum, [key, value]) => {
  return sum + value;
}, 0);

// 예시: 등수별 당첨 횟수 계산
const tally = { 1: 0, 2: 0, 3: 0 };
const totalPrize = Object.entries(tally).reduce((total, [rank, count]) => {
  return total + PRICE_BY_RANK[rank] * count;
}, 0);

8. 자주 사용되는 조합 패턴

패턴 1: 배열 → 객체 변환 (키별 그룹화)

// 가장 많이 사용!
const data = [
  ['크루1', '2024-12-01', '10:00'],
  ['크루2', '2024-12-01', '09:30'],
  ['크루1', '2024-12-02', '08:00'],
];

const grouped = {};
data.forEach(([nickname, date, time]) => {
  if (!grouped[nickname]) {
    grouped[nickname] = [];
  }
  grouped[nickname].push({ date, time });
});
// 결과: { '크루1': [{date, time}, ...], '크루2': [...] }

패턴 2: 객체 → 배열 변환 후 처리

// 객체의 값들을 배열로 변환
const crews = Object.keys(this.record); // ['크루1', '크루2']

// 객체를 배열로 변환하여 순회
Object.entries(this.record).forEach(([nickname, records]) => {
  // 처리 로직
});

패턴 3: 날짜/키별 인덱싱

// 날짜를 키로 사용하는 인덱스 생성
const recordsByDay = {};
records.forEach((record) => {
  recordsByDay[record.day] = record;
});

// 빠른 조회
const day5Record = recordsByDay[5];

9. 실전 체크리스트

✅ 객체에 데이터 추가할 때

  • 키가 없으면 먼저 초기화: if (!obj[key]) obj[key] = []
  • 배열이면 push, 단일 값이면 직접 할당

✅ 객체 순회할 때

  • 키만 필요: Object.keys()
  • 키와 값 모두: Object.entries()
  • 값만 필요: Object.values()

✅ 객체 변환할 때

  • reduce로 새 객체 생성
  • Object.entries().reduce() 패턴 기억하기

✅ 안전하게 접근할 때

  • obj[key] || [] 기본값 사용
  • if (!obj[key]) 존재 확인

✅ 복사가 필요할 때

  • 스프레드 연산자: { ...obj }
  • 이전 상태 저장: const prev = { ...current }

10. 자주 하는 실수

❌ 객체 초기화 안 하고 바로 push

// 잘못된 예
record[nickname].push(item); // undefined에 push 불가

// 올바른 예
if (!record[nickname]) {
  record[nickname] = [];
}
record[nickname].push(item);

❌ 원본 객체 직접 수정

// 주의: 원본이 변경됨
const target = records[index];
target.property = newValue; // records도 변경됨

// 필요시 복사 후 수정
const copy = { ...records[index] };
copy.property = newValue;

11. if(acc[key]) 검사 vs 바로 넣기 - 핵심 차이점

🔍 누적식 (Accumulative) - 검사 필요 ✅

여러 번 같은 키에 값을 추가/누적해야 할 때

// ✅ 검사 후 초기화 (누적식)
const grouped = {};
data.forEach(([name, value]) => {
  if (!grouped[name]) {
    // 키가 없으면 초기화
    grouped[name] = [];
  }
  grouped[name].push(value); // 계속 추가
});

// 예시: 같은 이름의 데이터가 여러 번 나올 수 있음
// 입력: [['크루1', '데이터1'], ['크루2', '데이터2'], ['크루1', '데이터3']]
// 결과: { '크루1': ['데이터1', '데이터3'], '크루2': ['데이터2'] }

왜 검사가 필요한가?

  • 같은 키가 여러 번 나타날 수 있음
  • 배열이나 값이 누적되어야 함
  • 첫 번째 등장 시 초기화 필요

🎯 덮어쓰기 (Overwrite) - 검사 불필요 ❌

같은 키에 한 번만 값을 넣거나, 덮어써도 되는 경우

// ✅ 바로 넣기 (덮어쓰기 방식)
const mapped = {};
data.forEach(([id, name]) => {
  mapped[id] = name; // 바로 할당, 이미 있으면 덮어씀
});

// 또는 reduce에서도
const result = items.reduce((acc, item) => {
  acc[item.id] = item.value; // 검사 없이 바로 할당
  return acc;
}, {});

// 예시: ID는 유일하므로 덮어쓸 일 없음
// 입력: [[1, '크루1'], [2, '크루2']]
// 결과: { 1: '크루1', 2: '크루2' }

왜 검사가 불필요한가?

  • 각 키가 한 번만 나타남 (유일한 키)
  • 덮어써도 되는 경우
  • 배열이 아닌 단일 값 할당

📊 비교 예시

케이스 1: 누적식 (검사 필요)

// 출석 기록: 같은 크루가 여러 날 출석
const attendance = [
  ['크루1', '2024-12-01'],
  ['크루2', '2024-12-01'],
  ['크루1', '2024-12-02'], // 크루1이 또 나옴!
  ['크루1', '2024-12-03'], // 크루1이 또 나옴!
];

const record = {};
attendance.forEach(([crew, date]) => {
  if (!record[crew]) {
    // ✅ 검사 필수!
    record[crew] = [];
  }
  record[crew].push(date);
});

// 결과: { '크루1': ['2024-12-01', '2024-12-02', '2024-12-03'], '크루2': ['2024-12-01'] }

케이스 2: 덮어쓰기 (검사 불필요)

// 상품 정보: 각 상품명은 한 번만 나옴
const products = [
  ['콜라', 1000],
  ['사이다', 1500],
  ['콜라', 1000], // 같은 상품이 다시 나와도 덮어써도 됨
];

const priceMap = {};
products.forEach(([name, price]) => {
  priceMap[name] = price; // ✅ 검사 불필요, 바로 할당
});

// 결과: { '콜라': 1000, '사이다': 1500 }
// 마지막 값으로 덮어씀

케이스 3: 배열이 아닌 단일 객체 할당

// 날짜별 정보: 각 날짜는 한 번만 나타남
const dates = [
  { day: 1, weekday: '월' },
  { day: 2, weekday: '화' },
  { day: 3, weekday: '수' },
];

const byDay = {};
dates.forEach((date) => {
  byDay[date.day] = date; // ✅ 검사 불필요, 객체 바로 할당
});

// 결과: { 1: {day: 1, weekday: '월'}, 2: {...}, 3: {...} }

🎯 결정 기준 체크리스트

검사가 필요한 경우 (누적식)

  • ✅ 같은 키가 여러 번 나타날 수 있음
  • ✅ 배열에 값을 계속 추가해야 함 (push)
  • ✅ 값이 누적되어야 함 (+=, push)
  • ✅ 키별로 목록을 만들어야 함

검사가 불필요한 경우 (덮어쓰기)

  • ✅ 각 키가 한 번만 나타남 (유일성 보장)
  • ✅ 단일 값만 할당하면 됨
  • ✅ 덮어써도 되는 경우
  • ✅ 맵핑/변환 목적 (1:1 관계)

💡 실전 패턴 정리

// 패턴 A: 누적식 (검사 + push)
if (!acc[key]) {
  acc[key] = [];
}
acc[key].push(value);

// 패턴 B: 누적식 (검사 + 초기값)
if (!acc[key]) {
  acc[key] = 0;
}
acc[key] += value;

// 패턴 C: 덮어쓰기 (바로 할당)
acc[key] = value;

// 패턴 D: 덮어쓰기 (객체 할당)
acc[key] = { ...item };

기억하자:

  • 배열(push)이나 누적(+=) → 검사 필요
  • 단일 할당(=) → 검사 불필요

Clone this wiki locally