JavaScriptの多次元ハッシュ（連想配列）を一括でURIエンコードする

## 一括処理したいこと
HTTP通信において、次のような多次元のハッシュ（連想配列）を送信する場合、

```js:sampleHash.js
let hash = { 
  text: "こんにちは",
  user: {
    name: "山田太郎",
    email: "taro@yamada.com"
  }
};
```

サーバへのリクエストは、次のようにURIエンコードして送信することになります。

```:URIエンコード(UTF-8)
text=%E3%81%93%E3%82%93%E3%81%AB%E3%81%A1%E3%81%AF&user[name]=%E5%B1%B1%E7%94%B0%E5%A4%AA%E9%83%8E&user[email]=taro%40yamada.com
```

この処理を、関数を使って一括でURIエンコードすることがこの記事の目的です。
また、ネストの階層が深くても処理できるような工夫が必要です（これには、関数の再帰呼出しを使用します）。

なお、jQueryを使うと自動でURIエンコードをしてくれますし、JavaScriptでもJSON形式を使えば面倒な処理は不要になります。
そのため、この記事のような処理をすることは、あまり多くないかもしれません。

## データ加工のイメージ

多次元ハッシュをURIエンコードで送信する場合のルールは次のような感じです。

- ネストしている要素は１つずつ独立させる
- 下の階層のキーは、`[ ]`で括って、上の階層のキーと繋げて１つのキーとする

```:イメージ
text: こんにちは
user[name]: 山田太郎
user[email]: taro@yamada.com
```

なお、ネストが３階層以上になる場合のキーは`user[name][firstname]`のように角括弧で続けて記述します。

そして、URIの形式なので、次の加工も必要です。

- キーと値は `=` で関連付ける
- 要素と要素は `&` で接続する


```:イメージ
text=こんにちは&user[name]=山田太郎&user[email]=taro@yamada.com
```

最後に、URIエンコード(UTF-8)で変換します（再掲）。

```
text=%E3%81%93%E3%82%93%E3%81%AB%E3%81%A1%E3%81%AF&user[name]=%E5%B1%B1%E7%94%B0%E5%A4%AA%E9%83%8E&user[email]=taro%40yamada.com
```

## 一括変換する関数
一括変換する関数は、次のとおりです。

```js:sample.js
// テスト用のハッシュ（連想配列）
let hash = { 
  text: "こんにちは",
  user: { name: "山田太郎", email: "taro@yamada.com" }
};  

// 一括変換する関数
function encodeURIFromHash(data, stockKey="", prefix="&", suffix="") {
  let resultStr = "";
  if (typeof(data) === "object") {  // 対象データがオブジェクトである場合は再帰呼出し
    Object.keys(data).forEach(function(key) {
      resultStr += encodeURIFromHash(data[key], stockKey + prefix + encodeURIComponent(key) + suffix, "[", "]");
    });
  } else {  // 対象データがオブジェクトでなければ確定
    resultStr = stockKey + "=" + encodeURIComponent(data);
  }
  if (stockKey === "") { resultStr = resultStr.slice(1); }  //１文字目の不要な"&"又は"="を削除
  return resultStr;
}

// 処理結果の表示
console.log(encodeURIFromHash(hash));
// 出力結果： text=%E3%81%93%E3%82%93%E3%81%AB%E3%81%A1%E3%81%AF&user[name]=%E5%B1%B1%E7%94%B0%E5%A4%AA%E9%83%8E&user[email]=taro%40yamada.com
```

`encodeURIFromHash関数`の第１引数に、変換したいハッシュ形式のデータを指定すれば、戻り値としてURIエンコードされたデータが返されます。
ネストが続いている場合には、関数を再帰的に呼び出して、ネストが終わるまで処理を続けるようにしています。

## 再帰呼出しの例（階層が深い場合）
関数の再帰呼出しを使用しているので、次のような入り組んだハッシュ（連想配列）でも変換できます。

```js:sampleHash2.js
let hash = { 
  post: {
    text: "こんにちは",
    id: 348,
    other: {
      url: "https://www.yahoo.co.jp/",
      style: {
        layer1: "階層１",
        layer2: "階層２"
      }
    }
  },
  user: {
    name: "山田太郎",
    email: "taro@yamada.com",
  }
};
```

出力結果は、次のようになります。

```js:sample.js
let ans = encodeURIFromHash(hash);

console.log(ans);  // 変換結果を表示
// 出力結果： post[text]=%E3%81%93%E3%82%93%E3%81%AB%E3%81%A1%E3%81%AF&post[id]=348&post[other][url]=https%3A%2F%2Fwww.yahoo.co.jp%2F&post[other][style][layer1]=%E9%9A%8E%E5%B1%A4%EF%BC%91&post[other][style][layer2]=%E9%9A%8E%E5%B1%A4%EF%BC%92&user[name]=%E5%B1%B1%E7%94%B0%E5%A4%AA%E9%83%8E&user[email]=taro%40yamada.com

console.log(decodeURIComponent(ans));  // 変換結果をデコード
// 出力結果： post[text]=こんにちは&post[id]=348&post[other][url]=https://www.yahoo.co.jp/&post[other][style][layer1]=階層１&post[other][style][layer2]=階層２&user[name]=山田太郎&user[email]=taro@yamada.com
```

## 最後に
挙動を確認しているのは、文字列型、数値型、Boolean型までです。
何らかの参考となれば幸いです。