본문으로 건너뛰기
기술스택 · stack/javascript/객체를 배열로, 다시 객체로: keys · values · entries · fromEntries

JavaScript · 5장 자료구조와 자료형

객체를 배열로, 다시 객체로: keys · values · entries · fromEntries

배열에는 map · filter · reduce가 있지만 객체에는 없어요. 그래서 객체의 모든 값에 할인을 적용하거나, 빈 칸만 빼거나, 가장 비싼 항목을 찾으려면 먼저 객체를 배열로 바꿔야 해요. 바꾸고, 다루고, 다시 객체로 돌리는 한 바퀴를 정리해요.

#5.8

01. 객체를 배열로: keys · values · entries

세 함수는 객체에서 각각 키 배열, 값 배열, [키, 값] 짝의 배열을 만들어요. 객체 자신의 속성만 담고, 심볼 키는 빠져요(심볼 키). 순서는 객체의 속성 순서 규칙 그대로예요. 정수 같은 키가 먼저 오름차순으로 오고, 나머지는 넣은 순서예요(속성 순서).

const prices = { 연필: 500, 공책: 2000, 지우개: 800 };

console.log(Object.keys(prices));    // ['연필', '공책', '지우개']
console.log(Object.values(prices));  // [500, 2000, 800]
console.log(Object.entries(prices)); // [['연필', 500], ['공책', 2000], ['지우개', 800]]

for (const [name, price] of Object.entries(prices)) {
  console.log(`${name}: ${price}원`); // 짝을 구조 분해해서 바로 받아요
}

for (const [name, price] of …)는 짝 배열 ['연필', 500]을 두 변수로 나눠 받는 구조 분해예요(구조 분해에서 자세히). 객체를 돌 때 for...in보다 이 모양을 더 많이 써요. 상속받은 속성이 섞이지 않고, 키와 값을 한 번에 받기 때문이에요.

02. 배열이 되면 배열 메서드를 쓸 수 있어요

배열로 바꾸고 나면 앞 글들의 메서드가 다 열려요. 합계는 values + reduce, 조건에 맞는 키 고르기는 entries + filter + map, 가장 큰 값 찾기는 entries + 정렬이에요. 짝을 받는 함수에서는 ([name, price]) => …처럼 매개변수 자리에서 바로 나눠요.

const prices = { 연필: 500, 공책: 2000, 지우개: 800 };

const total = Object.values(prices).reduce((sum, p) => sum + p, 0);
console.log(total);                                   // 3300

const over800 = Object.entries(prices)
  .filter(([, price]) => price >= 800)                // 키는 안 쓰니 비워 둬요
  .map(([name]) => name);
console.log(over800);                                 // ['공책', '지우개']

const [topName, topPrice] = Object.entries(prices).toSorted(([, a], [, b]) => b - a)[0];
console.log(topName, topPrice);                       // 공책 2000

console.log(Object.keys({}).length === 0);            // true — 빈 객체인지 확인

마지막 줄은 실무에서 자주 쓰는 "객체가 비었나?" 검사예요. 객체는 if (obj)로 확인하면 빈 객체도 참이라서, 키 개수를 세요.

03. 다시 객체로: Object.fromEntries

Object.fromEntries(짝들)은 entries의 반대예요. [키, 값] 짝의 목록을 받아 객체를 만들어요. 그래서 entries → 배열 메서드 → fromEntries가 "객체에 map · filter 하기"의 기본 모양이 돼요.

const prices = { 연필: 500, 공책: 2000, 지우개: 800 };

const sale = Object.fromEntries(
  Object.entries(prices).map(([name, price]) => [name, Math.round(price * 0.9)])
);
console.log(sale);    // { 연필: 450, 공책: 1800, 지우개: 720 }
console.log(prices);  // 원본은 그대로예요
①가격표 객체를 Object.entries로 [이름, 가격] 짝 배열로 바꾸고, map으로 가격마다 10%를 할인한 뒤, Object.fromEntries로 다시 객체로 묶어요. 원래 prices 객체는 바뀌지 않아요.

fromEntries는 짝을 하나씩 꺼낼 수 있는 이터러블이면 무엇이든 받아요. Map이나 주소의 쿼리 문자열(URLSearchParams)을 보통 객체로 바꿀 때도 써요. 쿼리 값은 늘 문자열이라는 점만 기억해요.

console.log(Object.fromEntries(new Map([['a', 1], ['b', 2]]))); // { a: 1, b: 2 }
console.log(Object.fromEntries(new URLSearchParams('q=js&page=2'))); // { q: 'js', page: '2' }

회원 정보 수정 화면에서, 비워 둔 칸('')과 값이 없는 칸(null)은 빼고 서버로 보내려고 해요. 그런데 나이 0까지 빠졌어요(갓 태어난 아기 회원이라고 해요). 0은 남기도록 고쳐서 { name: '하늘', city: '서울', age: 0 }이 나오게 해 보세요.

const form = { name: '하늘', email: '', phone: null, city: '서울', age: 0 };

const payload = Object.fromEntries(
  Object.entries(form).filter(([, value]) => value)
);
console.log(payload);
해답 보기

filter(([, value]) => value)는 "참 같은 값"만 남겨서, 거짓 같은 값인 0도 함께 빠졌어요(참 같은 값). 빼고 싶은 값을 정확히 적어요. value != null은 null과 undefined를 한 번에 걸러요.

const payload = Object.fromEntries(
  Object.entries(form).filter(([, value]) => value !== '' && value != null)
);
// { name: '하늘', city: '서울', age: 0 }

04. 실전 포인트

  • 언제 쓰나요? 객체를 돌 때는 Object.entries + for...of, 객체의 값 바꾸기 · 걸러 내기는 entries → map · filter → fromEntries, 빈 객체 확인은 Object.keys(o).length === 0, Map · 쿼리를 객체로 바꿀 땐 fromEntries를 써요.
  • 대신 쓰는 것: 키가 계속 늘고 줄어서 자주 돌고 세야 한다면 처음부터 Map(객체와 Map)이 나아요. 속성 하나만 바꿀 때는 { ...obj, price: 900 }처럼 펼치기가 더 짧아요.
  • 흔한 실수: 객체에 바로 .map을 부르는 것, 참 · 거짓으로 걸러서 0 · false · 빈 문자열까지 빠지는 것, 숫자처럼 생긴 키의 순서가 바뀌는 걸 모르는 것, 쿼리 값을 숫자로 바꾸지 않고 계산하는 것이에요.

정리

  • Object.keys · values · entries는 객체를 키 · 값 · [키, 값] 짝 배열로 바꿔요.
  • 배열이 되면 map · filter · reduce · toSorted를 그대로 쓸 수 있어요.
  • Object.fromEntries는 짝 목록을 다시 객체로 묶어요. entries → 배열 메서드 → fromEntries가 기본 모양이에요.
Object.entries({ a: 1, b: 2 })의 결과는?

[['a', 1], ['b', 2]]예요. [키, 값] 짝을 항목으로 하는 배열이라 for (const [k, v] of …)로 바로 나눠 받을 수 있어요.

객체의 모든 값을 두 배로 만든 새 객체는 어떻게 만들까요?

Object.fromEntries(Object.entries(obj).map(([k, v]) => [k, v * 2]))예요. 짝 배열로 바꿔 값만 바꾼 뒤 다시 객체로 묶어요. 원래 객체는 그대로예요.

빈 객체인지 확인할 때 if (!obj)를 쓰면 안 되는 이유는?

객체는 비어 있어도 참 같은 값이라 !{}는 false예요. Object.keys(obj).length === 0처럼 키 개수로 확인해요.

SIMPLE · 기술스택 / JavaScript — 5장 자료구조와 자료형