なんとかしてカップルを別れさせたい[後編]

## 前回はこちら
前編を読んでもらっている前提で記事を書いているので、読んでない方はこちらをお先にお読みください。

https://qiita.com/sytkm/items/ca31ee392a9ab744c9ea

## カップルを別れさせる Phase.2
さて、前回最後に書いたとおり、カップルの絵文字はたくさんあり、ただ単純にZWJを置き換えるというだけではカップルを別れさせることはできません。

では、どうすればいいでしょうか。
絵文字をすべてそれぞれで抽出することができれば、それらから置き換える対象を選ぶということが難しくなさそうなので、やってみたかったのですが、そうは行きませんでした。

今度はZWJが邪魔をしてきます。
リンクの記事にもあるように、簡単にはできず、また、今回はJavaScriptでの置き換えを画策している以上、正規表現を使うのが望ましそうです。

https://zenn.dev/catnose99/scraps/79012f2617ffd9

どんな正規表現が望ましいでしょうか。

前回でまとめたパターンと下記Unicodeの絵文字リストをにらめっこした結果、
ZWJ[U+200D]+大人🧑[U+1F9D1]、ZWJ[U+200D]+男性👨[U+1F468]、女性👩[U+1F469]の2つの絵文字が続き、後ろにZWJがつかない組み合わせは、カップルに関わる絵文字しかないようです。

https://unicode.org/emoji/charts/full-emoji-list.html

ということで、上で挙げた2つのUnicodeが続いた3パターンに合致する場合にはZWJを消すことにしてみましょう。
後ろにZWJがつくと家族の絵文字に合致してしまうので、そのパターンは正規表現で除きます。

コードはこのようになりました。

```javascript:content.js
window.addEventListener("load",(eve)=>{
  const d = document;
  d.body.innerHTML=d.body.innerHTML.replaceAll(/\u{200d}[\u{1f9d1}\u{1f468}\u{1f469}][^\u{200d}]/ug,
    function(match){
      return match.replace(/\u{200d}/ug,"");
    });
})

```

JavaScriptの`replaceAll()`は2つ目の引数に一致すると呼び出される関数を指定することができます。
そこで、3つのパターンに合致したときに一致したテキストからZWJを消すようにすることで、ZWJのみを消すようにしました。

余談ですが、前回は`\u200d`という指定をしていました。
しかし、JavaScriptの正規表現でUnicodeを扱う場合には、uオプションをつけて`\u{200d}`とすると扱いやすくなるため、このコード以降はこちらを利用します。

これでカップルは無事分かれることができたのでしょうか？
カップルの絵文字がたくさんあるウェブサイトで確認してみましょう。

![スクリーンショット 2023-12-22 0.15.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104058/5bcce92c-7c8f-a98d-f079-950b2c53757c.png)
![スクリーンショット 2023-12-22 0.15.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104058/a5d6e4d6-d66c-a10d-0cd6-0a1809169528.png)

惜しいですね。
前回の記事で挙げた、1文字だけでそのものを表す絵文字には適用されません。

次はそれらのカップルが別れるように解決しましょう。

## カップルを別れさせる Phase.3
1文字だけでそのものを表す絵文字について。
これらの顔文字は数が限られているため個別に指定してなんとかしたいところですね。
しかし、ただ分けるだけではいけません。

肌の色が指定されていた場合、2人の肌の色がどちらも指定された色に変わるのですが、
ただ分けてしまうと後ろの絵文字にだけ肌の色がついてしまうことになります。

![スクリーンショット 2023-12-22 2.21.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104058/f0d657cd-a96e-2234-acb2-c9a25c14cf9f.png)
![スクリーンショット 2023-12-22 2.23.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104058/024eaeb3-e4b3-4024-f34c-c5d60c41d3d8.png)


そこで、肌の色が指定された絵文字などはまず分解し、両方の肌の色を指定するという方法で対応することにします。
肌の色は、明るい肌色[U+1f3fb],やや明るい肌色[U+1f3fc],肌色[U+1f3fd],やや濃い肌色[U+1f3fe],濃い肌色[U+1f3ff]の5種類です。
これらの肌色が1つのUnicodeでカップルを表す絵文字にくっついていた場合に分けてみます。

```javascript
document.body.innerHTML = document.body.innerHTML.replaceAll(/\u{1f46b}[\u{1f3fb}\u{1f3fc}\u{1f3fd}\u{1f3fe}\u{1f3ff}]/ug,function(match){let skin = match.replace(/\u{1f46b}/ug,"");return "\u{1f469}"+skin+"\u{1f91d}"+"\u{1f468}"+skin});
```
![スクリーンショット 2023-12-22 2.24.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104058/fe4c2633-f76b-3416-10d9-2bf43bd3e913.png)
![スクリーンショット 2023-12-22 2.24.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104058/a39eba3e-94be-f921-c3e6-1a004eb5e886.png)

上手く行ったようです。

もちろん、こちらでは1つの絵文字👫[U+1f46B]にしか対応していません。
前回挙げた他の絵文字👬👭💑💏でも別れるようにしたいところ。
すべて同じコードを書くのは少しばかり面倒なので変数を使いましょう。

注意点として、JavaScriptの正規表現はそのまま変数を利用することはできません。
まず、正規表現オブジェクトを作り、そちらで変数を入れることで、利用することができます。


それらを組み合わせて、最終的に出来上がったコードがこちらになります。

```javascript:content.js
window.addEventListener("load",(eve)=>{
  const d = document;
  //単体の絵文字用
  let replace_emojis =
[['\u{1f46b}','\u{1f469}','\u{1f91d}','\u{1f468}'], //手をつなぐ男女
['\u{1f46c}','\u{1f468}','\u{1f91d}','\u{1f468}'], //手をつなぐ男性
['\u{1f46d}','\u{1f469}','\u{1f91d}','\u{1f469}'], //手をつなぐ女性
['\u{1f491}','\u{1f9d1}','\u{200d}\u{2764}\u{fe0f}','\u{1f9d1}'], //カップルとハート
['\u{1f48f}','\u{1f9d1}','\u{200d}\u{2764}\u{fe0f}\u{200d}\u{1f48b}','\u{1f9d1}']]; //2人でキス
  for(const property of replace_emojis){
      let regexp1 = new RegExp(property[0]+'[\u{1f3fb}\u{1f3fc}\u{1f3fd}\u{1f3fe}\u{1f3ff}]','ug');
      let regexp2 = new RegExp(property[0],'ug');
      d.body.innerHTML=d.body.innerHTML.replaceAll(regexp1,function(match){const skin = match.replace(regexp2,'');return property[1]+skin+property[2]+property[3]+skin});
      // 肌色が指定されていない場合
      d.body.innerHTML=d.body.innerHTML.replaceAll(regexp2,function(match){return property[1]+property[2]+property[3]});
  };

  //ZWJを利用した絵文字用
  d.body.innerHTML=d.body.innerHTML.replaceAll(/\u{200d}[\u{1f9d1}\u{1f468}\u{1f469}][^\u{200d}]/ug,function(match){return match.replace(/\u{200d}/ug,'');
  });
})
```

![スクリーンショット 2023-12-22 0.15.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104058/8420d350-96b7-576f-782b-0f90b3f49b8b.png)
![スクリーンショット 2023-12-22 2.15.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104058/f4fede1e-f6d9-f3de-0c58-5ec60bcd9434.png)

ついにカップルを別れさせることができたようです。
やりました！！

## 終わりに
ここまで、2回と長きに渡り、カップルを別れさせてきました。
いかがだったでしょうか？

ただの絵文字を分けるのですら結構大変なので、カップルが別れることなんてない気がしてきましたね。
自分の策略は失敗に終わってしまうようです。
残念です。

最後にたくさんの絵文字を置いておきます。

💑🏻	🧑🏻‍❤️‍🧑🏼	🧑🏻‍❤️‍🧑🏽	🧑🏻‍❤️‍🧑🏾	🧑🏻‍❤️‍🧑🏿
🧑🏼‍❤️‍🧑🏻	💑🏼	🧑🏼‍❤️‍🧑🏽	🧑🏼‍❤️‍🧑🏾	🧑🏼‍❤️‍🧑🏿
🧑🏽‍❤️‍🧑🏻	🧑🏽‍❤️‍🧑🏼	💑🏽	🧑🏽‍❤️‍🧑🏾	🧑🏽‍❤️‍🧑🏿
🧑🏾‍❤️‍🧑🏻	🧑🏾‍❤️‍🧑🏼	🧑🏾‍❤️‍🧑🏽	💑🏾	🧑🏾‍❤️‍🧑🏿
🧑🏿‍❤️‍🧑🏻	🧑🏿‍❤️‍🧑🏼	🧑🏿‍❤️‍🧑🏽	🧑🏿‍❤️‍🧑🏾	💑🏿
👩‍❤️‍💋‍👨🏻	👩‍❤️‍💋‍👨🏼	👩‍❤️‍💋‍👨🏽	👩‍❤️‍💋‍👨🏾	👩‍❤️‍💋‍👨🏿
👩🏻‍❤️‍💋‍👨🏻	👩🏻‍❤️‍💋‍👨🏼	👩🏻‍❤️‍💋‍👨🏽	👩🏻‍❤️‍💋‍👨🏾	👩🏻‍❤️‍💋‍👨🏿
👩🏼‍❤️‍💋‍👨🏻	👩🏼‍❤️‍💋‍👨🏼	👩🏼‍❤️‍💋‍👨🏽	👩🏼‍❤️‍💋‍👨🏾	👩🏼‍❤️‍💋‍👨🏿
👩🏽‍❤️‍💋‍👨🏻	👩🏽‍❤️‍💋‍👨🏼	👩🏽‍❤️‍💋‍👨🏽	👩🏽‍❤️‍💋‍👨🏾	👩🏽‍❤️‍💋‍👨🏿
👩🏾‍❤️‍💋‍👨🏻	👩🏾‍❤️‍💋‍👨🏼	👩🏾‍❤️‍💋‍👨🏽	👩🏾‍❤️‍💋‍👨🏾	👩🏾‍❤️‍💋‍👨🏿
👩🏿‍❤️‍💋‍👨🏻	👩🏿‍❤️‍💋‍👨🏼	👩🏿‍❤️‍💋‍👨🏼	👩🏿‍❤️‍💋‍👨🏾	👩🏿‍❤️‍💋‍👨🏿

今回はカップルを別れさせるというテーマのもとに絵文字の仕組みや扱い方について見ていきました。
絵文字というのは、次から次へと新しいのが作られ、すぐ使われるという、文字ですが文字じゃないような使われ方をするものです。
ここで作ったコードも1年後には使えなくなる可能性があります。
進歩というものは偉大ですね。

また、自分としては、絵文字があったからこそ様々なシステムでの多言語対応が進んだと思っているので日々絵文字に感謝しながら過ごしております。

ぜひ皆様も普段利用している絵文字について思いを馳せてはいかがでしょうか。
それではまた。

## 聞かれそうなこと
* ZWJを利用した絵文字の分解には前方にZWJが残ってしまっていると思いますが、そのままでも動くので消さずに残しています。そのあたり絵文字はルーズで助かりますね。
* 最終的なコードの7-8行目でZWJが入っているのはそれを入れないとハートが黒くなってしまったからです。`\u{fe0f}`というバリエーションセレクターつけてるはずなのになぜなんだろう。。
* 前編でなんとかして別れさせたいと書いていますが、特段カップルに対して思い入れはありません。