【JavaScript】 {} === {} はfalseになるので、簡単なnpmパッケージを作りました。

結構前に、JavaScriptで遊んでいたところ、以下のような場面に遭遇しました。

```
console.log({} === {})
```
出力結果は、 `true` でしょうか。 `false` でしょうか。

こんなの余裕で `true` でしょ！そう思っていました。

```:結果
false
```
でした。。。。

なんでだろう？？？

`true`でしょ！！そう思った方は、是非最後までご覧ください。

# 1. JavaScriptの比較・データ型について
### 等価演算子(==)と厳密等価演算子(===)

**等価演算子**は、2 つのオペランドが同じ型でないならばオペランドを変換して、それから厳密な比較を行います。
**厳密等価演算子**は、型変換なしでオペランド同士が等しければ真を返します。
参考: [比較演算子 |MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Comparison_Operators)

重要な点は、等価演算子(==)は、暗黙的に型の変換を行った上で比較を行うという点です。

###データ型

プリミティブ型とオブジェクト型の2種類があり、
プリミティブ型には、以下7種類のデータ型があります。
`Boolean` 
`Null` 
`Undefined` 
`Number` 
`BigInt` 
`String` 
`Symbol` 

プリミティブ型でないものは全てオブジェクト型になります。
**配列もオブジェクト型です。**

参考: [JavaScript のデータ型とデータ構造](https://developer.mozilla.org/ja/docs/Web/JavaScript/Data_structures)

###プリミティブとは
JavaScript において、プリミティブ (primitive、プリミティブ値、プリミティブデータ型) はオブジェクトでなく、メソッドを持たないデータのことです。 すべてのプリミティブ値は、イミュータブル、つまり変更できません。変数には新しい値を再割り当てすることができますが、既存の値については、オブジェクト、配列、関数が変更できるのに対して、プリミティブ値は変更することができません。

参考: [プリミティブ |MDN](https://developer.mozilla.org/ja/docs/Glossary/Primitive)

**つまり、プリミティブな型は、値を貸すことはできるが、定義された値に対して新しい値を代入することができません。**

具体的に例で示します。

プリミティブな型の場合。

```javascript

const number = 10
numberChange(number)
console.log(number) // => 10

function numberChange(number){
	number += 10000
	console.log(number) // => 10010
}
```
値を渡すことはできますが、定義した`const number = 10`の値自体が変更されることはありません。

オブジェクト型(プリミティブ型でない)場合。

```javascript

const array = [1,2,3]
arrayChange(array)
console.log(array) // => [1,2,3,10000]

function arrayChange(array){
	array.push(10000)
	console.log(array) // => [1,2,3,10000]
}
```
値ではなく、アドレスを渡している(参照渡し)ため、`const array = [1,2,3]`の値が変更されます。

# 1. 上記疑問を解決する
ドキュメントに答えが書かれていました。
>等価演算子は、2 つのオペランドが同じ型でないならばオペランドを変換して、それから厳密な比較を行います。**両方のオペランドがオブジェクトならば、 JavaScript は内部参照を比較するので、オペランドがメモリ内の同じオブジェクトを参照するときに等しくなります。**

具体的に例で示します。

```javascript
object  = {name: "aoki"}
object2 = {name: "aoki"}
object3 = object

console.log(object === object2) // => false 参照が異なるため  
console.log(object === object3) // => true 参照が同じため
```
`object`と`object2`は、プロパティとその値の組み合わせこそ同じなのですが、参照が異なるので`false`となります。
`object`と`object3`は、参照が同じなので`true`となります。

以上のことから、

```javascript
console.log({} === {}) // => false
```

となるわけです。なるほどっ！

でもやっぱり違和感を感じてしまいます。
直感的には、
`{name: "aoki"}`と`{name: "aoki"}`は同じなんだから、`true`を返してほしい。

そう思い、npmパッケージを作ることにしました。

# 2. npmパッケージを作って公開する
今回、作成するnpmパッケージが満たすべき要件は、以下のように設定しました。

- 二つのオブジェクトを比較した際に、プロパティと値の組み合わせが同じなら、プロパティの順番が異なっていても、trueを返す

そこで、必要な機能をまとめました。

(1)それぞれのオブジェクトのプロパティをソートする
(2)それぞれソートされたオブジェクトをjson形式にして比較する

まず、(1)です。以下のように実装しました。

```javascript:(1)の実装

function objectSort(object) {
  var newObject = {};
  var keyArray = [];
  for (key in object) {
    keyArray.push(key);
  }
  keyArray.sort()
  for (var i = 0; i < keyArray.length; i++) {
    newObject[keyArray[i]] = object[keyArray[i]];
  }
  return newObject;
}
```
新しいオブジェクト・配列を用意し、引数として与えられたオブジェクトのプロパティを配列に格納します。
格納された配列をsortメソッドを使ってソートし、ソートされた配列に対して、対応する値とともに新しいオブジェクトに格納します。
格納されたオブジェクトを返します。

***⚠︎追記　
JavaScriptのsortメソッドは、不安定なソートであり、必ずしも同じ序列を持つ値の順番が保証されません。
参考: [Array.prototype.sort() |MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)***

chromeだと安定ぽい...!

`安定なソート`として実現させる場合、以下のような処理を行います。

```JavaScript:安定
function objectSort(object) {
  var newObject = {};
  var keyArray = [];
  for (key in object) {
    keyArray.push(key);
  }
  stableSort(keyArray)
  for (var i = 0; i < keyArray.length; i++) {
    newObject[keyArray[i]] = object[keyArray[i]];
 
  console.log(newObject)
  return newObject;
}

function compare (a, b) {
  if (a === b) {
    return 0;
  } else if (a > b) {
    return 1;
  } else {
    return -1;
  }
}

function stableSort (array, fn) {
  if (fn == null) {
    fn = compare;
  }

  var i, len = array.length;

  if (len === 0) return array;

  // 値とインデックスのペアにする
  for (i = 0; i < len; i++) {
    array[i] = [array[i], i];
  }

  array.sort(function (p1, p2) {
    // ペアの0番目同士が等しくない場合はその比較結果を返す
    // 等しい場合は、インデックスを比較した結果を返す
    return fn(p1[0], p2[0]) || (p1[1] - p2[1]);
  });

  // ペアの0番目を取り出す
  for (i = 0; i < len; i++) {
    array[i] = array[i][0];
  }

  return array;
}
```
参考: [JavaScript で安定ソート](https://qiita.com/odiak/items/00ed21c5874d1bec176a)

**今回のnpmパッケージ関しては、不安定なソートのまま実装しています。**

次に、(2)です。
JSON.stringify()というメソッドが用意されているので、それを使います。

(1)と(2)を組み合わせて、関数を作ります。

```javascript:index.js
function objectMatch(obj1,obj2){
  const obj1Sorted = objectSort(obj1)
  const obj2Sorted = objectSort(obj2)
  obj1Json = JSON.stringify(obj1Sorted)
  obj2Lson = JSON.stringify(obj2Sorted)
  if(obj1Json === obj2Lson){
    return true
  }else{
    return false
  }
}

function objectSort(object) {
  var newObject = {};
  var keyArray = [];
  for (key in object) {
    keyArray.push(key);
  }
  keyArray.sort()
  for (var i = 0; i < keyArray.length; i++) {
    newObject[keyArray[i]] = object[keyArray[i]];
  }
  return newObject;
}
```
できました！！
いくつかテストをしてみます。

```javascript:index_test.js
const objectMatch = require('./index');

const object1 = {
  name: "aoki",gender: 1,height: 168.5
}

const object2 = {
  gender: 1,name: "aoki",height: 168.5
}

const object3 = {
  gender: 1,name: "aoki",height: 168.6
}

const object4 = {
  gender: 1,name: "aoki",profile: {hobby: {sports: ["soccer","baseball","basketball"]}}
}

const object5 = {
  gender: 1,profile: {hobby: {sports: ["soccer","baseball","basketball"]}},name: "aoki"
}

//プロパティとその値の組み合わせは同じだが、順番が異なる時、trueを返す。
test('object1 and object2 is equal', () => {
  expect(objectMatch(object1,object2)).toBe(true);
});

//プロパティに対して、その値が異なる時、falseを返す。
test('object1 and object3 is not equal', () => {
  expect(objectMatch(object1,object3)).toBe(false);
});

//プロパティに対する値が複雑でも、同じならtrueを返す。
test('object4 and object5 is equal', () => {
  expect(objectMatch(object4,object5)).toBe(true);
});
```
```
 PASS  ./index.test.js
  ✓ object1 and object2 is equal (2ms)
  ✓ object1 and object3 is not equal (1ms)
  ✓ object4 and object5 is equal (1ms)

Test Suites: 1 passed, 1 total
Tests:       3 passed, 3 total
Snapshots:   0 total
Time:        2.969s
```
通りました。

では、この関数をnpmパッケージにして公開します。
[こちらの記事](https://qiita.com/TsutomuNakamura/items/f943e0490d509f128ae2)がとても分かりやすく、とても簡単に公開することができました！

公開したnpmパッケージについては[こちら](https://www.npmjs.com/package/object_matching)から
githubについては[こちら](https://github.com/katsuomi/object_matching)から

<img width="1437" alt="スクリーンショット 2019-12-02 19.25.56.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/270696/24ae5171-945e-9cf0-497f-119d7b61d093.png">

以上になります！