LoginSignup
77
58

More than 3 years have passed since last update.

JavaScriptでオブジェクトをマージ(結合)する方法、JSONのマージをする方法

Last updated at Posted at 2020-01-28

概要

JavaScriptで以下のようなオブジェクトをマージする方法について書きます。

本題は深いマージ(deep merge,deep copy)です

※functionが存在しないプロパティのみのオブジェクトを想定

const a = { familyName: '織田',
  firstName: '吉法師',
  address: '尾張',
  sex: '',
  details: 
   { character: 
      { favoriteTactics: '奇襲',
        favoriteWord: '天下布武',
        favoritePlace: '' },
     ownedCastle: [ '清洲城' ] } };

const b = { familyName: '織田',
  firstName: '信長',
  details: 
   { character: { favoriteTactics: '鉄砲活用' },
     ownedCastle: [ '岐阜城', '安土城' ] } };

Object.assignで浅いマージ(shallow merge,shallow copy)

Object.assignをつかうと以下のようになる。

Object.assingを使う例

const a = {
    familyName: '織田',
    firstName: '吉法師', 
    address: '尾張', 
    sex: '',
    details: {
        character: {favoriteTactics: '奇襲', favoriteWord: '天下布武', favoritePlace: ''},
        ownedCastle: ['清洲城']
    }
};

const b = {
    familyName: '織田',
    firstName: '信長',
    details: {
        character: {favoriteTactics: '鉄砲活用'},
        ownedCastle: ['岐阜城', '安土城']
    }
};

const result1 = Object.assign(a, b);

console.log('result1=');
console.log(result1);

ちなみに、スプレッド構文をつかうと、Object.assignよりも短い記述で書ける

スプレッド構文をつかった例
const result1= { ...a, ...b };
結果
result1=
{ familyName: '織田',
  firstName: '信長',
  address: '尾張',
  sex: '',
  details: 
   { character: { favoriteTactics: '鉄砲活用' },
     ownedCastle: [ '岐阜城', '安土城' ] } }

Object.assignにあるとおり、

Object.assign() はプロパティの値をコピーするため、深い複製を行うには別な方法を使用する必要があります。元の値がオブジェクトへの参照である場合、参照の値のみをコピーするからです。

変数aに存在するプロパティは、変数bの同プロパティのものでコピー(上書き)される。
だが、変数aにあるa.detailsb.detailsの参照でコピーされてしまうため、当初のa.detailsの情報が失われる。

たとえば、a.details.character.favoriteWord(='天下布武')の情報は失われる。

そこで、プロパティの値がオブジェクトへの参照である場合にも、
そのオブジェクト以下のプロパティまで含めてコピーするために深いマージについて考える

深いマージ(deep merge,deep copy)

以下のような関数mergeDeeplyをつくって、オブジェクト以下のプロパティを再帰的にマージする深いマージ(deep merge,deep copy)をできるようにする。

function mergeDeeply(target, source, opts) {
    const isObject = obj => obj && typeof obj === 'object' && !Array.isArray(obj);
    const isConcatArray = opts && opts.concatArray;
    let result = Object.assign({}, target);
    if (isObject(target) && isObject(source)) {
        for (const [sourceKey, sourceValue] of Object.entries(source)) {
            const targetValue = target[sourceKey];
            if (isConcatArray && Array.isArray(sourceValue) && Array.isArray(targetValue)) {
                result[sourceKey] = targetValue.concat(...sourceValue);
            }
            else if (isObject(sourceValue) && target.hasOwnProperty(sourceKey)) {
                result[sourceKey] = mergeDeeply(targetValue, sourceValue, opts);
            }
            else {
                Object.assign(result, {[sourceKey]: sourceValue});
            }
        }
    }
    return result;
}


const a = {
    familyName: '織田',
    firstName: '吉法師', 
    address: '尾張', 
    sex: '',
    details: {
        character: {favoriteTactics: '奇襲', favoriteWord: '天下布武', favoritePlace: ''},
        ownedCastle: ['清洲城']
    }
};

const b = {
    familyName: '織田',
    firstName: '信長',
    details: {
        character: {favoriteTactics: '鉄砲活用'},
        ownedCastle: ['岐阜城', '安土城']
    }
};


const result2 = mergeDeeply(a, b);

console.log('result2=');
console.log(result2);

これを実行すると、以下のようになる。

結果
result2=
{ familyName: '織田',
  firstName: '信長',
  address: '尾張',
  sex: '',
  details: 
   { character: 
      { favoriteTactics: '鉄砲活用',
        favoriteWord: '天下布武',
        favoritePlace: '' },
     ownedCastle: [ '岐阜城', '安土城' ] } }

今度は変数aの内容に、変数bの内容がオブジェクト以下まで含めてマージされ、深いマージができた。

配列の取り扱い

プロパティの値が配列だった場合の挙動をどうするかを考える。

配列の要素(中身)は結合(concat)しない場合

↑の例では、
a.details.ownedCastleは配列(=['清洲城'])であるが、
マージするときにこの配列とb.details.ownedCastle(=['岐阜城','安土城'])は結合されない

配列の要素(中身)は結合(concat)したい場合

以下のようにして、さっきの関数を呼び出してやればOK

配列の中身を結合(concat)する
const result3 = mergeDeeply(a, b, {concatArray: true});

console.log('result3=');
console.log(result3);
結果
result3=
{ familyName: '織田',
  firstName: '信長',
  address: '尾張',
  sex: '',
  details: 
   { character: 
      { favoriteTactics: '鉄砲活用',
        favoriteWord: '天下布武',
        favoritePlace: '' },
     ownedCastle: [ '清洲城', '岐阜城', '安土城' ] } }

JSONどうしをマージする方法

JSONフォーマットのファイルや文字列として存在するJSONどうしをマージする方法も基本かわらない。いったんオブジェクトにしてから、戻すだけ。

const jsonA = `{"familyName":"織田","firstName":"吉法師","address":"尾張","sex":"男",
"details":{"character":{"favoriteTactics":"奇襲","favoriteWord":"天下布武","favoritePlace":"京"},
"ownedCastle":["清洲城"]}
}`;

const jsonB = `{"familyName":"織田","firstName":"信長",
"details":{"character":{"favoriteTactics":"鉄砲活用"},
"ownedCastle":["岐阜城","安土城"]}
}`;

const jsonResult = JSON.stringify(
    mergeDeeply(JSON.parse(jsonA), JSON.parse(jsonB), {concatArray: true})
);

console.log('jsonResult=');
console.log(jsonResult);
結果
jsonResult=
{"familyName":"織田","firstName":"信長","address":"尾張","sex":"","details":{"character":{"favoriteTactics":"鉄砲活用","favoriteWord":"天下布武","favoritePlace":""},"ownedCastle":["清洲城","岐阜城","安土城"]}}

ライブラリを使う方法

深いマージ(deep merge)をするためのメジャーなライブラリも存在する

●インストール方法

npm install deepmerge

●使用方法

const merge = require('deepmerge');
merge(a, b)

まとめ

  • Javascriptでオブジェクトをマージする方法について説明しました
  • Object.assignをつかった浅いマージ、簡単な関数をつかった深いマージ、JSONどうしのマージ、ライブラリを使った方法などにふれました

参考・関連サイト

77
58
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
77
58