22일 8월 2020

Object.keys, values, entries

개별 자료 구조에서 한발 뒤로 물러나 순회에 관해 이야기 나누어봅시다.

이전 챕터에서 map.keys(), map.values(), map.entries()와 같은 메서드들에 대해 알아보았습니다.

이 메서드들은 포괄적인 용도로 만들어졌기 때문에 이 메서드들이 적용될 자료구조는 일련의 합의를 준수해야 합니다. 자료구조를 직접 만들어서 사용하려면 기존에 구현된 메서드를 쓰지 못하고 직접 커스텀 메서드를 구현해야 합니다.

keys(), values(), entries()는 아래와 같은 자료구조에 적용할 수 있습니다.

  • Map
  • Set
  • Array

일반 객체에도 유사한 메서드를 적용할 수 있는데, Map, Set, Array에 적용하는 메서드와는 문법이 약간 다릅니다.

Object.keys, values, entries

일반 객체엔 다음과 같은 메서드를 사용할 수 있습니다.

Map, Set, Array에 적용하는 메서드와 객체에 적용하는 메서드의 차이를 맵을 기준으로 비교하면 다음과 같습니다.

객체
호출 문법 map.keys() obj.keys()가 아닌 Object.keys(obj)
반환 값 iterable 객체 ‘진짜’ 배열

첫 번째 차이는 obj.keys()가 아닌 Object.keys(obj)를 호출한다는 점입니다.

이렇게 문법에 차이가 있는 이유는 유연성 때문입니다. 아시다시피 자바스크립트에선 복잡한 자료구조 전체가 객체에 기반합니다. 그러다 보니 객체 data에 자체적으로 메서드 data.values()를 구현해 사용하는 경우가 있을 수 있습니다. 이렇게 커스텀 메서드를 구현한 상태라도 Object.values(data)같이 다른 형태로 메서드를 호출할 수 있으면 커스텀 메서드와 내장 메서드 둘 다를 사용할 수 있습니다.

두 번째 차이는 메서드 Object.*를 호출하면 iterable 객체가 아닌 객체의 한 종류인 배열을 반환한다는 점입니다. ‘진짜’ 배열을 반환하는 이유는 하위 호환성 때문입니다.

예시:

let user = {
  name: "John",
  age: 30
};
  • Object.keys(user) = ["name", "age"]
  • Object.values(user) = ["John", 30]
  • Object.entries(user) = [ ["name","John"], ["age",30] ]

아래 예시처럼 Object.values를 사용하면 프로퍼티 값을 대상으로 원하는 작업을 할 수 있습니다.

let user = {
  name: "John",
  age: 30
};

// 값을 순회합니다.
for (let value of Object.values(user)) {
  alert(value); // John, 30
}
Object.keys, values, entries는 심볼형 프로퍼티를 무시합니다.

for..in 반복문처럼, Object.keys, Object.values, Object.entries는 키가 심볼형인 프로퍼티를 무시합니다.

대개는 심볼형 키를 연산 대상에 포함하지 않는 게 좋지만, 심볼형 키가 필요한 경우엔 심볼형 키만 배열 형태로 반환해주는 메서드, Object.getOwnPropertySymbols를 사용하면 됩니다. getOwnPropertySymbols 이외에도 모든 키를 배열 형태로 반환하는 메서드, Reflect.ownKeys(obj)를 사용할 수 있습니다.

객체 변환하기

객체엔 map, filter 같은 배열 전용 메서드를 사용할 수 없습니다.

하지만 Object.entriesObject.fromEntries를 순차적으로 적용하면 객체에도 배열 전용 메서드 사용할 수 있습니다. 적용 방법은 다음과 같습니다.

  1. Object.entries(obj)를 사용해 객체의 키-값 쌍을 요소로 갖는 배열을 얻습니다.
  2. 1.에서 만든 배열에 map 등의 배열 전용 메서드를 적용합니다.
  3. 2.에서 반환된 배열에 Object.fromEntries(array)를 적용해 배열을 다시 객체로 되돌립니다.

위 방법을 사용해 가격 정보가 저장된 객체 prices의 프로퍼티 값을 두 배로 늘려보도록 합시다.

let prices = {
  banana: 1,
  orange: 2,
  meat: 4,
};

let doublePrices = Object.fromEntries(
  // 객체를 배열로 변환해서 배열 전용 메서드인 map을 적용하고 fromEntries를 사용해 배열을 다시 객체로 되돌립니다.
  Object.entries(prices).map(([key, value]) => [key, value * 2])
);

alert(doublePrices.meat); // 8

지금 당장은 어렵게 느껴지겠지만 한두 번 위와 같은 방법을 적용해 보면 객체에 배열 전용 메서드를 손쉽게 적용할 수 있을 겁니다.

과제

중요도: 5

급여 정보가 저장되어있는 객체 salaries가 있습니다.

Object.valuesfor..of 반복문을 사용해 모든 급여의 합을 반환하는 함수 sumSalaries(salaries)를 만들어보세요.

salaries가 빈 객체라면, 0이 반환되어야 합니다.

예시:

let salaries = {
  "John": 100,
  "Pete": 300,
  "Mary": 250
};

alert( sumSalaries(salaries) ); // 650

테스트 코드가 담긴 샌드박스를 열어 정답을 작성해보세요.

function sumSalaries(salaries) {

  let sum = 0;
  for (let salary of Object.values(salaries)) {
    sum += salary;
  }

  return sum; // 650
}

let salaries = {
  "John": 100,
  "Pete": 300,
  "Mary": 250
};

alert( sumSalaries(salaries) ); // 650

Object.valuesreduce를 이용해 합을 구할 수도 있습니다.

// reduce는 급여 정보가 저장되어있는 배열을 순회해
// 급여의 총합을 만들고
// 그 결과를 반환합니다.
function sumSalaries(salaries) {
  return Object.values(salaries).reduce((a, b) => a + b, 0) // 650
}

테스트 코드가 담긴 샌드박스를 열어 정답을 확인해보세요.

중요도: 5

객체 프로퍼티 개수를 반환하는 함수 count(obj)를 만들어보세요.

let user = {
  name: 'John',
  age: 30
};

alert( count(user) ); // 2

가능한 짧게 코드를 작성해 보세요.

주의: 심볼형 프로퍼티는 무시하고 ‘일반’ 프로퍼티 개수만 세주세요.

테스트 코드가 담긴 샌드박스를 열어 정답을 작성해보세요.

function count(obj) {
  return Object.keys(obj).length;
}

테스트 코드가 담긴 샌드박스를 열어 정답을 확인해보세요.

튜토리얼 지도

댓글

댓글을 달기 전에 마우스를 올렸을 때 나타나는 글을 먼저 읽어주세요.
  • 추가 코멘트, 질문 및 답변을 자유롭게 남겨주세요. 개선해야 할 것이 있다면 댓글 대신 이슈를 만들어주세요.
  • 잘 이해되지 않는 부분은 구체적으로 언급해주세요.
  • 댓글에 한 줄짜리 코드를 삽입하고 싶다면 <code> 태그를, 여러 줄로 구성된 코드를 삽입하고 싶다면 <pre> 태그를 이용하세요. 10줄 이상의 코드는 plnkr, JSBin, codepen 등의 샌드박스를 사용하세요.