JavaScriptの連想配列の説明をしつつ、配列内文字列の出現回数を調べる

# 前段
A氏「数字が入っているリストがあって、そこから数字の出現回数を調べたいんですけど、連想配列使わない方法ってないですか？」
私　「連想配列じゃダメなのですか？」
A氏「いまいち連想配列が分からないので、可能なら避けて通りたい…」
私　「（他の方法かぁ。ん？　でも、これは連想配列を理解するにはいい題材なのでは？　避けて通らない方が！）」

その時に説明した内容を資料にまとめることになりました。

# 配列と連想配列

ともあれ、この記事をご覧の方の中には配列と連想配列の違いから、という方もいると思うので簡単に説明。

***配列は0から始まる数字(インデックス)を指定して作業を行う。***

```ts:sample.ts
const list: string[] = [
  "れもん",
  "みかん",
  "りんご"
];
console.log(list[0]); // れもん
console.log(list[1]); // みかん
console.log(list[2]); // りんご
```

***連想配列は数字(インデックス)ではなく文字列を指定して作業を行う。***

```ts:sample.ts
const map: {[key:string]:string} = {
  "lemon":"れもん",
  "orange":"みかん",
  "apple":"りんご"
};
console.log(map["lemon"]); // れもん
console.log(map["orange"]); // みかん
console.log(map["apple"]); // りんご
```

# ストーリー

買い物にいったお母さんが果物をたくさん買って帰ってきました。
スーパーの袋に手を突っ込み、無造作に机の上に並べます。
<img src="https://camo.qiitausercontent.com/5fb39f7975c045b0cc96a2f5f9c467bbd8c6beba/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f33333334372f36643461333961652d346433322d336364612d653635362d6538396462616565643366632e706e67" alt="連想配列_2.png" data-canonical-src="https://qiita-image-store.s3.amazonaws.com/0/33347/6d4a39ae-4d32-3cda-e656-e89dbaeed3fc.png" width="600px">


```ts:sample.ts
const fruitList: string[] = [
	"れもん",
	"みかん",
	"りんご",
	"みかん",
	"りんご",
	"れもん",
	"みかん",
	"りんご",
	"みかん",
	"みかん",
	"ぶどう"
];
```

そこに子供がやってきました。

子供「1番目の果物は"れもん"だね」

```ts:sample.ts
// 1番目（先頭）の要素にアクセスするための数字は0です。
console.log(fruitList[0]); // れもん
```

母親「そうね。それじゃあ、まず、各果物をまとめましょう」

```ts:sample.ts
// 連想配列
const note: { [key: string]: number } = {};

// フルーツリストの数だけ繰り返す
for ( let i = 0; i < fruitList.length; i++ ) {
	// フルーツ（の名前）取得
	const fruit = fruitList[ i ];

	// 連想配列に登録されているかどうか
	if ( note[ fruit ] == null ) {
		// 連想配列に登録されていなかったら1を設定
		note[ fruit ] = 1;
		continue;
	}

	// 連想配列に登録されていたら、登録内容をインクリメント
	note[ fruit ] = note[ fruit ] + 1;
}
```

子供「訳が分からないよ」
母親「順番に見ていけば分かるわ」
子供「がんばる」
母親「最初のフルーツ（`fruitList[0]`）は"れもん"だったわね。`note`には"れもん"というタイトルをつけたメモを貼り付けて、そこに「正」の字でカウントしましょうか。"れもん"が1つ」

<img src="https://camo.qiitausercontent.com/078ecb5ba89a83bc354fd1905be1cbe4199dd887/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f33333334372f39656437306634362d373963362d333938312d376132652d6337336637363164646632652e706e67" alt="連想配列_3.png" data-canonical-src="https://qiita-image-store.s3.amazonaws.com/0/33347/9ed70f46-79c6-3981-7a2e-c73f761ddf2e.png" width="400px">

母親「次のフルーツ（`fruitList[1]`）は"みかん"。`note`に"みかん"はないから、またメモを貼り付けるわね」

<img src="https://camo.qiitausercontent.com/b82bbf7fbf6c90b3ce41112221de45de2752ddf2/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f33333334372f39303833383834372d313962342d623930322d363437332d6163393061373261643636662e706e67" alt="連想配列_4.png" data-canonical-src="https://qiita-image-store.s3.amazonaws.com/0/33347/90838847-19b4-b902-6473-ac90a72ad66f.png" width="400px">

母親「次のフルーツ（`fruitList[2]`）は"りんご"。これも`note`にメモを貼り付ける」

<img src="https://camo.qiitausercontent.com/6359e614d561d9e5229e885bf362c121ac0453fd/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f33333334372f33663530616233342d396238632d643539322d393638322d3666306634313031373061372e706e67" alt="連想配列_5.png" data-canonical-src="https://qiita-image-store.s3.amazonaws.com/0/33347/3f50ab34-9b8c-d592-9682-6f0f410170a7.png" width="400px">

母親「次のフルーツ（`fruitList[3]`）は"みかん"。あ、`note`に"みかん"があるじゃないの。それなら「正」の字の縦線を書くわね。これを繰り返して…」

<img src="https://camo.qiitausercontent.com/a9cb0f26be512a53bd3d30eac0181e8626db94c2/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f33333334372f36336439333062352d616461312d666661632d636637352d6239346338636532633462342e706e67" alt="連想配列_6.png" data-canonical-src="https://qiita-image-store.s3.amazonaws.com/0/33347/63d930b5-ada1-ffac-cf75-b94c8ce2c4b4.png" width="400px">

母親「ふぅ、全部で11個もフルーツを買っていたのね。でも、これで分類が終わったわ」

<img src="https://camo.qiitausercontent.com/72b1f67588ad965864c1f561634a0bc9db37af0a/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f33333334372f65313435353932322d343737342d633836662d373036632d6662363739333362323430332e706e67" alt="連想配列_7.png" data-canonical-src="https://qiita-image-store.s3.amazonaws.com/0/33347/e1455922-4774-c86f-706c-fb67933b2403.png" width="400px">

子供「一番たくさんあったのは、どれかな？」
母親「まず、タイトルを抜き出して並べてみましょうか」

<img src="https://camo.qiitausercontent.com/e7080ee4db055ce1a6d64f631524c170dac06141/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f33333334372f31653135653331652d383735312d393363642d396165332d6164626238303336356239342e706e67" alt="連想配列_8.png" data-canonical-src="https://qiita-image-store.s3.amazonaws.com/0/33347/1e15e31e-8751-93cd-9ae3-adbb80365b94.png" width="400px">

```ts:sample.ts
const memoTitleList = Object.keys( note );
```

母親「これを順番に比較していくわ。例えば、"れもん"と"みかん"ならどちらが大きいかしら？」
子供「"みかん"！」
母親「正解よ。それじゃ、その繰り返しで、一番たくさん入っていたのは何か調べましょう」
子供「がんばる」

```ts:sample.ts
// 初期値
let maxNum = 0;
let maxNumFruit = "";

// 連想配列noteの、key値の配列を取得
const memoTitleList = Object.keys( note );

// memoTitleListの数だけ繰り返す
for ( let i = 0; i < memoTitleList.length; i++ ) {
	const memoTitle = memoTitleList[ i ];

	// 連想配列noteにkey値であるmemoTitle（果物の名前）を与えて、value（正の字の数）を得る
	const fruitCount = note[ memoTitle ];

	// 保持しておいた最大数と比較
	if ( maxNum > fruitCount ) {
		// value（正の字で数えた数）が最大値より小さい場合はcontinue
		continue;
	}
	// 最大数とその果物名を保持
	maxNum = fruitCount;
	maxNumFruit = memoTitle;
}

console.log( maxNum ); // 5
console.log( maxNumFruit ); // みかん
```

子供「一番たくさん買ったのは、"みかん"だ！」
母親「うちの子、天才（涙）」

# コード

## ストーリーで使ったもの

```ts:sample.ts
const fruitList: string[] = [
	"れもん",
	"みかん",
	"りんご",
	"みかん",
	"りんご",
	"れもん",
	"みかん",
	"りんご",
	"みかん",
	"みかん",
	"ぶどう"
];

// 連想配列
const note: { [key: string]: number } = {};

// フルーツリストの数だけ繰り返す
for ( let i = 0; i < fruitList.length; i++ ) {
	// フルーツ（の名前）取得
	const fruit = fruitList[ i ];

	// 連想配列に登録されているかどうか
	if ( note[ fruit ] == null ) {
		// 連想配列に登録されていなかったら1を設定
		note[ fruit ] = 1;
		continue;
	}

	// 連想配列に登録されていたら、登録内容をインクリメント
	note[ fruit ] = note[ fruit ] + 1;
}

// 初期値
let maxNum = 0;
let maxNumFruit = "";

// 連想配列noteの、key値の配列を取得
const memoTitleList = Object.keys( note );

// memoTitleListの数だけ繰り返す
for ( let i = 0; i < memoTitleList.length; i++ ) {
	const memoTitle = memoTitleList[ i ];

	// 連想配列noteにkey値であるmemoTitle（果物の名前）を与えて、value（正の字の数）を得る
	const fruitCount = note[ memoTitle ];

	// 保持しておいた最大数と比較
	if ( maxNum > fruitCount ) {
		// value（正の字で数えた数）が最大値より小さい場合はcontinue
		continue;
	}
	// 最大数とその果物名を保持
	maxNum = fruitCount;
	maxNumFruit = memoTitle;
}

console.log( maxNum ); // 5
console.log( maxNumFruit ); // みかん
```

## 整理版

```ts:sample2.ts
const fruitList: string[] = [
	"れもん",
	"みかん",
	"りんご",
	"みかん",
	"りんご",
	"れもん",
	"みかん",
	"りんご",
	"みかん",
	"みかん",
	"ぶどう"
];

const note: { [key: string]: number } = {};

let maxNum = 0;
let maxNumFruit = "";

const len = fruitList.length;
for ( let i = 0; i < len; i++ ) {
	const fruit = fruitList[ i ];
	let fruitCount = note[ fruit ] == null ? 1 : note[ fruit ] + 1;
	note[ fruit ] = fruitCount;

	if ( maxNum > fruitCount ) {
		continue;
	}
	maxNum = fruitCount;
	maxNumFruit = fruit;
}

console.log( maxNum ); // 5
console.log( maxNumFruit ); // みかん
```

# 補足
* `Object.keys`でとれる順番は、画像のように「れもん-みかん-りんご-ぶどう」とは限りません。
* コードはTypeScriptで書いてますが、型部分を消せばjsとしても使える、と思います。（ただし、検証はしていない）

# 素材
[いらすとや](https://www.irasutoya.com/)さんの素材を使わせていただきました。