0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

[TypeScript] reduce を使って新しいオブジェクトを生成する

Last updated at Posted at 2023-04-21

はじめに

やりたいことはタイトルのとおりです。この記事では次の3つの要件に対する実装案を提示します。

  • 要件-1
    • 既存のオブジェクトから特定のフィールドを取り出して新しいオブジェクトを生成する
  • 要件-2
    • 既存のオブジェクトから全く同じフィールドをもつ新しいオブジェクトを生成する
  • 要件-3
    • 要件-2 の内容を関数化してみる

要件-1: 特定のフィールドを取り出して新しいオブジェクトを生成する

下記のような objectA があって、ここからフィールド a, b だけを抽出した新しい objectB を生成したいケースを想定します。

interface ModelType {
    [key: string]: string    
}

const objectA: ModelType = {
    'a': 'AAAA',
    'b': 'BBBB',
    'c': 'CCCC'
};

あらかじめ抽出したいフィールドのキー値を指定した配列を用意します。
そのキー値配列に対して reduce を使って objectA からデータを設定していきます。

const keys: string[] = [
    'a',
    'b'    
];

// キー値の配列にあるフィールドだけを取り出して objectB を作る
// このやり方だと if 文 や filter を使ったフィルタリングがいらない
const objectB = keys.reduce(
    (obj, key) => {
        obj[key] = objectA[key];
        return obj;
    },
    // 型指定をしないと TS7053 で下記が発生する
    // Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{}'.
    // No index signature with a parameter of type 'string' was found on type '{}'.
    {} as ModelType
);

console.log(objectB); // { 'a': 'AAAA', 'b': 'BBBB' } が出力される

要件-2: 全く同じフィールドをもつ新しいオブジェクトを生成する

前掲の objectA から同じフィールドを持つ objectC を作りたいケースです。
( 要は ObjectA のコピーです, Lodash の cloneDeep を使いたくないときとかの代替としての実装案です )

objectC が持ちたいフィールドは objectA と同じなので、objectA からキー値のフィールドを生成します。
あとは 要件-1 と同じです。生成したキー値配列に対して reduce を使って objectA からデータを設定していきます。

// キー値の配列を objectA から作る
const keys2 = Object.keys(objectA) as (keyof typeof objectA)[];
console.log(keys); // ["a", "b", "c"]  が出力される

// 作成したキー値配列から reduce を使って objectA から objectC を作る
const objectC = keys2.reduce(
    (obj, key) => {
        obj[key] = objectA[key];
        return obj;
    },
    {} as ModelType
);

console.log(objectC); // { 'a': 'AAAA', 'b': 'BBBB', 'c': 'CCCC' } が出力される 

上記はキー値配列の生成と object の生成をつなげて一つの処理にまとめることも出来ます。

// objectA から キー値配列を作り、reduce を使って新しい objectD を作る処理をひとまとめに行う
const objectD = (
    Object.keys(objectA) as (keyof typeof objectA)[]
).reduce(
    (obj, key) => {
        obj[key] = objectA[key];
        return obj;
    },
    {} as ModelType
);

console.log(objectD); // { 'a': 'AAAA', 'b': 'BBBB', 'c': 'CCCC' } が出力される 

要件-3: 関数化してみる

ここまで上げたやり方ですと、オブジェクトの型が固定されているので汎用性にかけます。
引数に与えられたオブジェクトの情報をもとに、返却するオブジェクトのキー値と型を設定する ことで関数化できるか試してみます。

/**
 * ベースとなるオブジェクトを引数に受取り、ベースオブジェクトと同じオブジェクトを返却する
 */
const cloneObject = <T extends object>(
  baseObject: T
): { [key in keyof T]: T[key] } => {
  return (Object.keys(baseObject) as (keyof T)[]).reduce((obj, key) => {
    obj[key] = baseObject[key];
    return obj;
  },
  {} as {
    // [key in keyof T]: T[key] で baseObject のキーに設定されている値の型を、そのまま本関数が返すオブジェクトの型として設定できる
    [key in keyof T]: T[key]
  });
};

次のコードはこの関数を使った例です。 baseObject と同じ情報が生成されていることがわかります。
( 要件-1, 要件-2 とは異なり、ここでは ModelType のフィールドの型も string で統一ではなく、string, number, boolean と型を分けています )

interface ModelType {
  a: string;
  b: number;
  c: boolean;
}

const baseObject: ModelType = {
  'a': 'AAAA',
  'b': 1,
  'c': false
};

const objectE = cloneObject(baseObject);

// ここで objectD は次の型で返却されている
//
// [objectE] の型
// const objectE: {
//   a: string;
//   b: number;
//   c: boolean;
// }

console.log(objectE); // {a: "AAAA", b: 1, c: false} が出力される

補足

オブジェクトの比較をしたい場合は Lodash の isEqual を使えば簡単にできます。

import * as _ from 'lodash';

console.log(_.isEqual(objectA, objectB)); // fase が出力される
console.log(_.isEqual(objectA, objectC)); // true が出力される
console.log(_.isEqual(objectA, objectD)); // true が出力される

参考

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?