Sibainu Relax Room

愛犬の柴犬とともに過ごす部屋

JavaScript JSON.stringify()について2

何かいいものがないか一生懸命に水仙を覗いています。

概要

Map オブジェクトを JSON.stringify() メソッドで変換すると

このようになってしまいます。

前回の「JavaScript JSON.stringify()について1」では Object オブジェクトで書く方法があると記載しましたが、次のような入れ子になってくるとはやり手間がかかるので方法を考えてみました。

Object.fromEntries()を使ってみる

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object/fromEntries

Object.fromEntries() メソッドは、引数の iterable からキーと値のリストを受け取り、キーから作成されたプロパティを持つ新しいオブジェクトを返します。

引数は iterable となっていますが、iterable は @@iterator メソッドを実装したものです。

そして、オブジェクトのように2つの要素を持ち、最初の要素がプロパティキーとして使われる値であり、次の要素がプロパティのキーに関連付けられる値であるようなオブジェクトであることが求められますとあります。

実行してみると次のようになりました。

意図する結果になりました。

もう少し突っ込んでみたいので、再度JSON.stringify() を調べてみます。

JSON.stringify()

mdn web docs にJSON.stringify()の解説があります。

このHPの中ほどに「replacer として関数を用いる例」があります。この例から value が Map オブジェクトなら Object.fromEntries() で変換する replacer を考えてみます。

次のような関数になると思います。

copy

function replacer(key, value) {
    if (value instanceof Map) {
        return Object.fromEntries(value);
    } else {
        return value;
    }
}

実行の前に replacer をいじってみます。

replacer として関数を用いる例

その中ほどにreplacer として関数を用いる例があります。

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#replacer_として関数を用いる例

これによると、key と文字列化される value の 2 つの引数を取ります。キーをもつオブジェクトが replacer のコンテキストで this として提供されるので、結果mapオブジェクトが対象になります。

copy

function replacer(key, value) {
  // プロパティをフィルターする
  if (typeof value === "string") {
    return undefined;
  }
  return value;
}

var foo = {
  country: "Japan",
  zip: 1234567,
  ken: "aichi",
  city: "nagoya",
  street: "nishiki",
  address: 77,
};

console.log(JSON.stringify(foo, replacer));
// undefined は無視され値が文字列なら古い落し、結果値が数値が残ります
// '{"zip":1234567,"address":77}'

replacer として配列を用いる例

replacer が配列である場合、その配列の値はJSON 文字列に含めるプロパティの名前を示し、配列の値で抽出します。

JSON.stringify(foo, ["country", "city", "street"]);
// '{"country":"japan", "city":"nagoya", "street":"nishiki"}'

JSON.stringify(value, replcer)を使ってみる

上の「Object.fromEntries()を使ってみる」を基に書き直して実行してみます。ずいぶんすっきりし、見通しがよくなりました。

結果は上の「Object.fromEntries()を使ってみる」と同じでした。

Map オブジェクトが入れ子になっても大丈夫なようです。

copy

const cMap1 = new Map();
cMap1.set("c1","aaaaa");
cMap1.set("c2","bbbbb");
cMap1.set("c3","ccccc");

const cMap2 = new Map();
cMap2.set("C1","AAAAA");
cMap2.set("C2","BBBBB");
cMap2.set("C3","CCCCC");

const pMap = new Map();
pMap.set("p11",[cMap1,cMap2]);
pMap.set("p12",[cMap1,cMap2]);
pMap.set("p13",[cMap1,cMap2]);

const ppMap = new Map();
ppMap.set("ppp1",pMap);
ppMap.set("ppp2",pMap);
ppMap.set("ppp3",pMap);


function replacer(key, value) {
    if (value instanceof Map) {
        return Object.fromEntries(value);
    } else {
        return value;
    }
}

console.log(JSON.stringify(ppMap, replacer));

正規表現で抽出してみる

Object.fromEntires() と同時に正規表現で抽出ができるかどうか確認してみました。

条件として値に c が含まれているなら JSON 文字列から削除というようなコードで実行してみました。

結果は次のとおりで綺麗に削除されています。使えそうです。

コードは次のとおりです。

copy

const cMap1 = new Map();
cMap1.set("c1","aaaaa");
cMap1.set("c2","bbbbb");
cMap1.set("c3","ccccc");

const cMap2 = new Map();
cMap2.set("C1","AAAAA");
cMap2.set("C2","BBBBB");
cMap2.set("C3","CCCCC");

const pMap = new Map();
pMap.set("p11",[cMap1,cMap2]);
pMap.set("p12",[cMap1,cMap2]);
pMap.set("p13",[cMap1,cMap2]);

const ppMap = new Map();
ppMap.set("ppp1",pMap);
ppMap.set("ppp2",pMap);
ppMap.set("ppp3",pMap);
ppMap.set("ppp4","cccc");
ppMap.set("ppp5","dddd");

const re = /c+/ 

function replacer(key, value) {
    if (value instanceof Map) {
        return Object.fromEntries(value);
    } else {
        if (typeof value === "string" && re.test(value)) {
          return undefined;
        } else {
          return value;
        }
    }
}

console.log(JSON.stringify(ppMap, replacer));

JSON.stringify(value, [])を使ってみる

replacer 関数か配列かの二者択一だからreplacer 関数は使えないので、Map オブジェクトは Object.stringify()メソッドで前もって Object オブジェクトに変換します。

そして、 ppp1 関係、ppp4 関係を得ようと配列 [“ppp1”, “ppp4”] を渡して実行してみました。

結果は次のとおりとなりました。ppp1 の関係も全部出てくると思ったのですが違っていました。

私の感覚からするとちょっと残念に思います。

今回はここまでとします。