[javascript] 配列比較で覚えておきたい２つのデータ型

## この記事について

javascriptで配列の等値比較を行なった際につまずいたところがあったのでその備忘録として残しておきます。


## 参考
https://developer.mozilla.org/ja/docs/Web/JavaScript/Data_structures

https://ja.javascript.info/object-copy


## プリミティブ型とオブジェクト型
まず配列の等値比較の話をする前に頭に入れておきたいのが、javascriptのデータ型にはプリミティブ型とオブジェクト型があるということです。

|  データ型  |  具体例  |
| ---- | ---- |
|  プリミティブ型  |  文字列型 (string)、数値型 (number)、真偽型 (boolean)、特殊型 (null, undefined)、シンボル (symbol)  |
|  オブジェクト型  |  オブジェクト (object)、配列 (array)、関数 (function)、正規表現 (regular expression)  |

このデータ型の違いによって等値比較の挙動が大きく異なってきます。

## ２つのデータ型の等値比較時の挙動の違い

プリミティブ型とオブジェクト型では以下のように比較演算処理を行う際の対象が異なってきます。

|  データ型  |  比較対象  |
| ---- | ---- |
|  プリミティブ型  |  データそのもの  |
|  オブジェクト型  |  データの参照元 (メモリ上のどこから参照しているか)  |

例えば、文字列や数値といったプリミティブ型のデータを比較する場合は、データそのものの値を比較します。そのため同じ値を持つ変数同士を等価比較するとtrueを返します。

```javascript
let a = 'hoge'
let b = 'hoge'

a === b
>> true

let num1 = 1
let num2 = 1

num1 === num2
>> true
```

一方でオブジェクト型である変数同士を比較する場合は異なる結果となります。

```javascript
let array1 = ['hoge', 'fuga'];
let array2 = ['hoge', 'fuga'];

array1 === array2
>> false

let dict1 = {key1: 'hoge', key2: 'fuga'};
let dict2 = {key1: 'hoge', key2: 'fuga'};

dict1 === dict2
>> false
```

同じデータを持つ配列や連想配列同士を等値比較しても返ってくる値はfalseになってしまいます。
なぜこうなってしまうのかというと、オブジェクト型はデータそのものではなくデータの参照元の比較を行なっているからです。

ここでいう「データの参照元」とはメモリ上のどこにデータが置かれているのかを示すものです。

上記の配列の例でいうとarray1とarray2はデータの置き場所が異なるということです。

```javascript
let array1 = ['hoge', 'fuga'];
let array2 = ['hoge', 'fuga'];
```

例えで説明するとメモリというタンスの中に色々な引き出しがあってarray1とarray2がそれぞれ別の引き出しに入っているような状態です。

![オブジェクト型イメージ図.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151361/d27c4d2b-edf0-2841-ab7f-7be095ce359c.png)

逆にデータの参照元が同じであれば等価比較はtrueを返します。

```javascript
let array1 = ['hoge', 'fuga'];
let array2 = array１;

array1 === array2
>> true
```

このように、配列や連想配列はデータの参照元を比較してしまうので等値比較する際には注意が必要です。

## オブジェクト型の比較方法

では、どのようにすれば配列や連想配列といったオブジェクト型のデータは比較できるのでしょうか？

主に2つの方法があります

1. 文字列型に変換する
2. カスタム関数を作成する

### 1. 文字列型に変換する
データの中身そのものを比較できるのはプリミティブ型の場合のみでした。そこでオブジェクト型をプリミティブ型(文字列)に変換することでデータの参照元ではなくデータそのものの比較を実現します。
ここでは配列を文字列に変換できる`JSON.stringify()`を使用します。

```javascript
let array1 = ['hoge', 'fuga'];
let array2 = ['hoge', 'fuga'];

JSON.stringify(array1) === JSON.stringify(array2)
>> true
```
ただし、この文字列変換の方法は配列の要素の並び順は考慮しないため注意が必要です。

### 2. 関数を作成する

配列の長さが一致するか、そしてループ処理によって配列同士の要素が一致するかをチェックする関数を作成して配列同士が等価であるか確認できるようにします。

[配列の場合]

```javascript
let array1 = ['hoge', 'fuga'];
let array2 = ['hoge', 'fuga'];

const isEqualArray = function (array1, array2) {
   var i = array1.length;
   if (i != array2.length) return false;

   while (i--) {
     if (array1[i] !== array2[i]) return false;
   }
   return true;
 };

isEqualArray(array1, array２);
>> true

```

[連想配列の場合]

```javascript
let dict1 = {key1: 'hoge', key2: 'fuga'}
let dict2 = {key1: 'hoge', key2: 'fuga'}

const isEqualArray = function (dict1, dict2) {

   const dict1Keys = Object.keys(dict1).sort();
   const dict2Keys = Object.keys(dict2).sort();

   if(dict1Keys.toString() !== dict2Keys.toString()){
     return false;
   }

   const wrongValIndex = dict1Keys.findIndex(function(key){
     return dict1[key] !== dict2[key];
   });
   return wrongValIndex === -1;
};

isEqualArray(dict1, dict2);
>> true

```