2021年6月27日

Object.keys、values、entries

個々のデータ構造から離れて、それらに対する反復処理について話しましょう。

前の章では、map.keys()map.values()map.entries()メソッドを見ました。

これらのメソッドは汎用的であり、データ構造に使用するという共通の合意があります。独自のデータ構造を作成する場合は、それらも実装する必要があります。

これらは以下のものに対してサポートされています。

  • Map
  • Set
  • Array

プレーンオブジェクトも同様のメソッドをサポートしますが、構文はやや異なります。

Object.keys、values、entries

プレーンオブジェクトでは、次のメソッドが使用できます。

違いに注意してください(例:mapとの比較)

Map オブジェクト
呼び出し構文 map.keys() Object.keys(obj)ですが、obj.keys()ではありません
戻り値 イテラブル 「実体のある」配列

最初の違いは、obj.keys()ではなくObject.keys(obj)を呼び出す必要があることです。

なぜそうなのか?主な理由は柔軟性です。オブジェクトはJavaScriptにおけるすべての複雑な構造の基礎であることを覚えておいてください。そのため、独自のdata.values()メソッドを実装する独自のオブジェクト(dataなど)を持つことができます。そして、それでもObject.values(data)を呼び出すことができます。

2番目の違いは、Object.*メソッドがイテラブルだけでなく、「実体のある」配列オブジェクトを返すことです。これは主に歴史的な理由によるものです。

例えば

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
};

// loop over values
for (let value of Object.values(user)) {
  alert(value); // John, then 30
}
Object.keys/values/entriesはシンボリックプロパティを無視します

for..inループと同様に、これらのメソッドはキーとしてSymbol(...)を使用するプロパティを無視します。

通常は便利です。しかし、シンボリックキーも必要であれば、シンボリックキーのみの配列を返す別のメソッドObject.getOwnPropertySymbolsがあります。また、すべてのキーを返すメソッドReflect.ownKeys(obj)も存在します。

オブジェクトの変換

オブジェクトには、配列に存在する多くのメソッド(例:mapfilterなど)がありません。

それらを適用したい場合は、Object.entriesに続いてObject.fromEntriesを使用できます。

  1. objからキー/値ペアの配列を取得するには、Object.entries(obj)を使用します。
  2. これらのキー/値ペアを変換するには、その配列で配列メソッド(例:map)を使用します。
  3. 結果の配列をオブジェクトに戻すには、Object.fromEntries(array)を使用します。

例えば、価格を含むオブジェクトがあり、それを2倍にしたいとします。

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

let doublePrices = Object.fromEntries(
  // convert prices to array, map each key/value pair into another pair
  // and then fromEntries gives back the object
  Object.entries(prices).map(entry => [entry[0], entry[1] * 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 loops over array of salaries,
// adding them up
// and returns the result
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;
}

テスト付きの解答をサンドボックスで開きます。

チュートリアルマップ

コメント

コメントする前にこれを読んでください…
  • 改善点に関するご提案がある場合は、コメントする代わりにGitHub issueまたはプルリクエストを送信してください。
  • 記事の内容が理解できない場合は、詳しく説明してください。
  • 短いコードを挿入するには<code>タグを使用し、複数行の場合は<pre>タグで囲み、10行を超える場合はサンドボックス(plnkrjsbincodepen…)を使用してください。