Make組ブログ

Python、Webアプリや製品・サービス開発についてhirokikyが書きます。

JavaScriptのClipboard APIでリッチテキスト(書式付きテキスト)をコピーする

クリップボードにリッチテキストをコピーする方法を説明します。今回はとくにClipboard APIという現在推奨された方法で実装します。

「リッチテキストをコピー」というのは、ペーストしたときにWYSIWYGエディターへ書式が有効なまま入力されることを言っています。たとえばWordでコピーをすると、見出しや太字などを含めてコピーされるのと同じことです。

Clipboardを使ってリッチテキストをコピーする

標準のClipboard APIを使ってリッチテキストをコピーするには、以下のようにします。

const body = '<h1>見出し</h1><strong>太字</strong>'

const blob = new Blob([body], { type: 'text/html' })
const blobPlain = new Blob([body], { type: 'text/plain' })
const item = [new window.ClipboardItem({ 'text/html': blob, 'text/plain': blobPlain })]

await navigator.clipboard.write(item)

(たとえば「コピー」ボタンをクリックしたときなどに実行してください)

MIMEタイプに 'text/html' と指定して、Blobを渡すことでできます。こうすることでHTMLと認識させられるので、WordやWYSIWYGエディターへペーストしたときに書式が有効となります。Google Chromeの場合、コピーしたHTMLは自動でサニタイズされます。

実装するときは 'text/plain' としてもコピーしてください。そうしないと、メモ帳などにプレーンテキストとして貼り付けられるときに動作しません。

Firefoxは未対応

2022年1月26日時点ではFirefoxがHTMLのコピーに対応していません。

window.ClipboardItem が存在するかなどをチェックして、ない場合は navigator.clipboard.writeText を使ってプレーンテキストのみコピーするのが良いと思います(細かい実装の話はお任せしますが)。

Google Chromeで対応された履歴はこちらです。

chromestatus.com

Google ChromeSafariではできるので、そこまで困らないのが事実です(僕はFirefoxユーザーですが)。

document.execCommandは使わない

以前まではClipboard API経由でリッチテキストをコピーできませんでした。少し前の記事ですと document.execCommand('copy') とイベントリスナーを使って何とかする方法も書かれていますが、現在は避けたほうが良いかと思います。

document.execCommand 自体が非推奨になっていますので、できれば Clipboard を使いましょう。

developer.mozilla.org

まとめ

Clipboard APIを使ってリッチテキスト(書式付きテキスト)のコピーはできます! Firefoxは執筆時点で未対応ですが、そこは割り切れる範囲かなという気もします。

document.execCommand('copy') はなるべく使わないようにしましょう。

今後、対応されていくと良いなと思っています。

caniuse.com


執筆:Kiyohara Hiroki (@hirokiky)Shodoで執筆されました

Pythonで`{文字列: 数値}`の辞書から数値が最大のキーと値を取る

{文字列: 数値} のような辞書(Dict[str, int])があるときに、数値が最大・最小のキーと値を取得する方法です。

たとえば文字の出現回数をカウントしているときなどに使えます。

>>> d = {"a": 3, "b": 2, "c": 1}
>>> k, v = max(d.items(), key=lambda x: x[1])
>>> k
'a'
>>> v
3

他にも sorted()min() でも使えます。

collections.Counter とも併せて使えるので便利です。

>>> from collections import Counter
>>> d = Counter("aaaaabbbcc")
>>> k, v = max(d.items(), key=lambda x: x[1])
>>> k
'a'
>>> v
5

タプルのリストが便利に使える

タプルのリストから最大値、最小値を取ると便利に使えます。

たとえば辞書のキーにできないオブジェクトの場合は、第一要素に数値を持つタプルを使うと似たことができます。 この例では辞書がひも付いているとしました。

>>> l = []
>>> for i in range(5):
...     l.append((i, {}))
... 
>>> l
[(0, {}), (1, {}), (2, {}), ...]
>>> max(l, key=lambda x: x[0])
(4, {})

key= を指定しない場合、第1要素の数値が同値の場合に第2要素が評価されますので注意してください。

タプルのリストや key= 引数をうまく使えば、何らかの値に数値がひも付いているときに最大値や最小値を取得できます。

執筆:Kiyohara Hiroki (@hirokiky)Shodoで執筆されました

社会って論理的じゃないよね。僕らの弱さ、そして小野マトペ氏の裁判に感じること

皆さんのコメントを読んで、反省しました

皆さん、たくさんのコメントありがとうございます。 僕自身の考えや文が稚拙であったことを反省しました。

まず、僕は「社会は論理的でないところがある」ように書きましたが、これは良くなかったなと考えを改めました。 僕の間違いは、自分は論理的に考えていると思っていたものの、社会の中にある論理や合理性を軽視していたということです。 自分に理解できないものを、論理ではないものだと考える、感情的なものでした。 さらに文自体も感情的で良くなかったと反省しております。

皆さんのコメントのおかげで気づけました。ありがとうございます。 僕は、自分以外の世界というのに少し感覚が弱かったというか、軽視していたのだと思いました。

この記事について、削除してしまおうかとも思いましたが、僕自身が反省して学びこれからに活かすため残しておこうと思います。 内容はそのままにしておりますので、それを前提に読んでいただけると幸いです。

改めて、教えていただきありがとうございました。 そしていつも読んでいただき、ありがとうございます。

本文

社会って案外、論理的じゃないよね、と感じた話をさせてください。

いえ、僕こそホントは論理的でなかったと勝手に反省した 。オチを言うとそういう話です。

小野マトペ氏の裁判が終わり、個人的に「うわぁ」と思ったので感想です。

平たくまとめてるとこういう裁判です:

  • Twitterで「俺はコロナ」とネタをツイート
  • 居酒屋に行って写真をツイート
  • 刑事事件(偽計業務妨害)になる

もとの話はこちらを: note.com

何が怖いのか

まさに自分のことだったからです。

この裁判は控訴棄却で有罪判決となりました。 「ネタであり業務妨害をする気なんてなかった(故意でない)」という点が裁判において認められなかったということですね。

でも僕自身にも、すごく心当たりがあるんですよね。 まるで自分のことかと思いました。

Twitterでその日盛り上がった話題やニュースに、ちょっぴり過激な面白いことを言いたいことってあるじゃないですか。 友だちと飲み会をして「濃厚接触!」なんて冗談を飛ばしたいときもあります(ネットには書かないネットリテラシーを発動)。

もちろん、褒められたことではありません。 でもそんな、なくもないことが刑事事件になってしまう んだなということです。 僕も反省するところです。

たしかに前後関係を繋げていけば「業務妨害になっても構わないと思ってますよね?(未必の故意である)」と追い詰められる状況になります。

社会は論理的じゃない、そして僕こそ

僕が感じる「ロジック」とは違う論理があると言うべきでしょうか。そして自分が思っているほど、自分が論理的ではなかったということです。

裁判を見ると、小野マトペ氏が「故意に業務妨害をやったぞ!」と言える決定的な証拠はないわけです。 たとえば「店を名指しした犯罪行為を感じさせるツイート」など明らかなものはありません。

さらに、民事的ないざこざではないので、刑事的な「故意に犯罪をやっているかどうか」が争点なわけですね。

ただ、社会には「社会的に見てどうか?」という倫理による力があって、(情勢も鑑みて)納得感のある正義が勝ったわけです。 僕の学びとしては「ロジカルに言えば問題ないでしょう、とは言えない」、「納得感や社会的な倫理が重要な世界なんだ」という社会の掟に気づいたことです。

でも、そう考えれば、当たり前のことを言ってしまっていますね。

僕の弱点:論理的思考だと思ってしまう

論理的思考こそ至高なのではないのですか? そうなのですが、その思考が危ういんですね。

社会や法における「論理」と僕が感じている「論理」や合理性は違うものなんだなと分かりました。 いえ、むしろ本当はエンジニアの世界にもロジックだけじゃないものが大事なはずなんです。

そうでない、社会のもつ「納得感」が、一般的な世界の枠組みにおいて重要視されるわけです。 実際に小野マトペ氏の記事を読むと、論理や「自分の中にある正義」を大切にされてると分かります。

社会的な倫理観や、情勢、思惑や納得感を考える(おもんばかる)のも重要なんですね。 人の気持ちや常識、そしてそれらを動かす修辞や弁論も重要なわけです。

論理的思考は僕たちの世界においては絶対の理!と考えるのも、むしろいびつなんでしょうね。 正直、僕自身も上手に生きられてるとは思いません。

僕が学ぶこと

僕が学ぶこととしては、論理的思考だけで物事は決まらないと受け入れるだと思います。 実際に世の中は社会的な倫理観や納得感、そしてそれらを動かすレトリック(修辞)や弁論術やリテラシーが重要なようです。

飛躍してしまえば「有罪かどうかも修辞や弁論に左右される」ということですよね。 「いやいや、そのために弁護士がいるんでしょ!」と思った方は、そのとおりです。 小野マトペ氏のまとめ記事にも、弁護士を選ぶことが相当重要であったと反省が書かれています。

むしろそれこそが学びです。

「己の潔白と事実を説明して共感してもらえば分かってもらえる」と思っていませんか? そんなことはなかったんですね。 そもそも、社会的な通念を重んじるのも大切なんですね(当たり前のことですが)。

僕は、自分の「論理が正しい」思考を改める必要があると思いました。 そして合理的な時代の今だからこそ、修辞学(レトリック)や弁論術、そして法学や倫理こそ学ばないといけないのかな、ということです。

おわりに

ぜひ小野マトペ氏のまとめ記事を前半から読んでください。

あと、これに関したおすすめの本です。 このリンクから買ってくれると嬉しいです:


執筆:Kiyohara Hiroki (@hirokiky)Shodoで執筆されました

DjangoのQuerySetでグループごとに最大・最小のデータのみ取得する

DjangoのQuerySetでグループごとに最大・最小のデータのみ抽出する方法を紹介します。 この記事はDjango Advent Calendar 2021 3日目の記事です。

グループごとの最大・最小のデータとは何でしょうか?

たとえば以下のような場合に必要となります。

  • ブログ記事ごとに最新のコメントのみ取得
  • ユーザーごとに金額が最大の購入履歴のみ取得
  • ページごとにリビジョン番号が最大の差分データのみ取得

実際に仕事をしているとたまに欲しくなりますよね。

今回は Parent モデルというグループごとに、 Child モデルの number が最大になる Child の一覧を取得します。

class Parent(models.Model):
    pass
    
class Child(models.Model):
    parent = models.ForeignKey(Parent)
    number = models.IntegerField()

Meta__str__ は表記上省略)

QuerySetでグループごとに最大・最小のみ取得する

最大のみ取得する処理は以下のようになります(解説は後述します)。

children = Child.objects.all()
sub_qs = children.filter(
    parent=models.OuterRef("parent"),
    number__gt=models.OuterRef("number"),
)
qs = children.filter(~models.Exists(sub_qs))

実際に動作を確認してみましょう。 p1p2というグループごとに最大の値を持つ c1_4c2_2 のデータを作りました。

>>> p1 = Parent.objects.create()
>>> p2 = Parent.objects.create()
>>> c1_1 = Child.objects.create(parent=p1, number=1)
>>> c1_2 = Child.objects.create(parent=p1, number=2)
>>> c1_3 = Child.objects.create(parent=p1, number=3)
>>> c1_4 = Child.objects.create(parent=p1, number=4)
>>> c2_1 = Child.objects.create(parent=p2, number=1)
>>> c2_2 = Child.objects.create(parent=p2, number=2)
>>> children.filter(~models.Exists(sub_qs))
<QuerySet [<Child: 1-4>, <Child: 2-2>]>

Child: 1-4c1_4)とChild: 2-2c2_2)の取得を確認できました。

SQLは以下のようになりました(読みやすいよう改行を追加しています)。

SELECT "child"."id", "child"."parent_id", "child"."number"
FROM "child"
WHERE NOT EXISTS(
    SELECT (1) AS "a"
    FROM "child" U0
    WHERE (
        U0."number" > "child"."number" AND
        U0."parent_id" = "child"."parent_id"
    ) LIMIT 1
)

この処理では「グループごとに最大・最小の値が複数ある場合に両方とも取得される」点に注意してください。

解説

今回のSQLでは「よりnumberが高い値のある行を除外する」という方法で、グループごとの最大値を取得しています。直感的にはGROUP BYで最大値を計算するサブクエリーを書きたいところですが、実行速度が速い方法を紹介しました。

Djangoではこのサブクエリーを書くために、 OuterRefExists を使っています。OuterRef を使えば外側のクエリー中のフィールドを参照できますので、サブクエリーを簡単に書けます。 今回の場合は OuterRef("parent") でグループにする Parent を指定し、 OuterRef("number") で除外対象の number を指定しています。

実際の環境で高速に動作するかどうかは、データベースの実行計画を確認してください。 データベースやデータの内容によって変わってくると思いますので、仕事の環境で使う場合は実行計画を見ましょう。

GROUP BYを使った書き方も紹介しておきます。

他の書き方

参考として、GROUP BYとサブクエリーを使った方法も紹介します。

children = Child.objects.all()

sub_qs = children\
    .filter(parent=models.OuterRef("parent")) \
    .values("parent") \
    .annotate(max_number=models.Max("number")) \
    .values("max_number")
qs = children.filter(number=models.Subquery(sub_qs))

SQLは以下のようになります。

SELECT "child"."id", "child"."parent_id", "child"."number"
FROM "child"
WHERE "child"."number" = (
    SELECT MAX(U0."number") AS "max_number"
    FROM "child" U0
    WHERE U0."parent_id" = "child"."parent_id"
    GROUP BY U0."parent_id"
)

この処理はサブクエリー内で number の最大値を求めたうえで、その値を条件として Child の一覧を取得しています。こちらのほうが NOT EXISTS を使った書き方よりも理解しやすいですが、処理としては遅くなると思います(実際のデータや環境でお試しください)。

おわりに

DjangoOuterRefSubqueryExists を使ってグループごとに最大・最小の値のみを取得しました。 昔は「Djangoで凝ったSQLを書けない」という印象もありましたが、今のDjangoではクエリーの表現が多彩ですので、今回のようなサブクエリーも直感的に書けます。

ぜひ、業務の中でも活用していただけると嬉しいです (実行計画の確認はしてください)。

ちょっと聞いてください

このブログ記事は私の会社で開発しているShodo(https://shodo.ink/)を使って執筆されました。 Shodoは書いた文章をAIがリアルタイムにチェックしてくれるWebサービスです。

ただのAI校正ツールではありません。 エンジニアの皆さまにも嬉しい特徴がたくさんあります。

と、お勧めできます。

さらにGitHubのようなワークフローで記事を書けるのも特徴です:

  • 記事を書く日程やステータスを管理したり
  • 複数人で記事のレビューコメントやレビュー依頼をしたり
  • 共有リンクからたくさんの人にコメントをもらったり

そんな便利なWebサービスです。

今ならなんと!技術アドベントカレンダーの期間中、Shodoを無料で使えるキャンペーンを実施しています。 これから技術ブログを書く皆さん、ぜひShodoで技術記事を書いて知識やノウハウを共有しませんか?

クーポンコードはこちらから。 ぜひお試しください。

blog.shodo.ink

執筆:Kiyohara Hiroki (@hirokiky)Shodoで執筆されました

「若者エンジニア」が30歳になりました。老いが急激に加速するのはなぜ?

こんにちは。hirokikyです。

僕の個人的な話をしますが、4分ほどで誰でも気軽に読めるようまとめました ので、ぜひお付き合いください (5分の動画より短いです)。

今日の 2021年11月5日で、30歳になりました。 そしてこのブログも、もう10年以上やっていることになります。

僕は20歳のころからプログラミング(Python)界隈と関わりを持っていました。 当時は「若者Pythonエンジニア」と受け入れられたように覚えています。2011年ごろはまだRubyの全盛期で、わざわざPythonを選ぶのは少し変わった人たちでした(ChefやCoffeeScriptが流行り始める時代感です)。 そこに現れた20歳の僕や、同年代のPythonエンジニアはさらに珍しかったのです。

2012年のPyCon JPで併設カンファレンスを開催したとき

(2012年の併設カンファレンスでの懇親会)

「若者エンジニア」という名札は便利でした (正直に言ってしまうと)。

あれこれと技術について質問をしても、多少過激なことを言っても、周りは温かく受け入れてくれます。むしろ、その少し攻めた姿勢も良かったのでしょうか。臆せず質問や登壇、イベントの主催ができたように思います。親切な皆さんに助けていただいたり、一緒に仕事をしたり、イベントをやったりと楽しい日々でした。他にも自社で企画開発したWebサービスをお客様に買っていただいたり、そして起業したりと良い20代だったように思います。

DjangoCongress JP 2019の集合写真

主催したDjangoCongress JP 2019での写真(また皆んなで集まりたいね)

でもそんな若者エンジニアも、今日で30歳になります。

つまり、若さという免罪符が失効してしまったのです。

これからは大人の生き方を考えなければいけません。

...

と、文が続くと思ったのではないでしょうか。

いや、そんなことはありません。 若者らしさがなくなるのは、僕としてはどうでも良いのです(実を言えば27歳くらいから若者ではないですし)。

僕の中で困ったことは他にあるのです。

重鎮感 です。

何か「年上や先輩として気を遣われている感」、「界隈で歴の長い人として大切にされている感」です。 ときおりヌルリと感じるこの「圧」を、ここ数年感じていました。

たとえば軽い気持ちで「よしやってみよう」と言ったつもりで、周りの人がガッツリと作業に入っていた、などです。 他にもTwitterで「ああいうことやっても意味ないよ」とツイートすれば、知らないところで誰かが傷ついていたとか。

どちらにしろ僕が悪いだけです。

あと、少しうぬぼれすぎかもしれません。 僕はそこまで影響力のある人間ではないです(すいません)。

ですが、皆さんも何か心当たりがあるのではないでしょうか?

  • 「軽い気持ちで言ったのに、思ったより大事な発言になっていた」
  • 「いつの間にか、会議で発言が待たれている」

規模の大小はあれ、年を重ねたり、同じ業界でずっと頑張っていると誰しもが重鎮らしくなるわけです。 より一般化して言うと 「他人はあなたを、ロールに収めたがっている」 ということです(たとえば「決めてくれる人」)。 僕自身も他人にそれを期待してしまっています。

正直、若さを失うことはどうでも良いです。

ただ、 実際の現実は若さがなくなるだけでなく「老けさせようという圧力」がある んです。 より具体的に言うと 「他人が期待するあなたの姿へ、あなた自身を押し込む圧力」ということ です。

30歳くらいになった人が、急に老けてくる現象ってありますよね。 あれは、一度確立した自分の姿や「先輩としての自分」で留まると起こるのかな?と最近思います。 昔は尖ってカッコよかったのに、今は時事ネタのご意見番になった知識人などもそのタイプだと思います。

この力はおそらく人間心理的なものなのかなと思います。 責任や発言力、信頼や立場などというものが絡み合って、周囲が求める「あなた」という像が固定化されたのでしょう。あなた(そして僕)自身も誰かにそれを期待しているわけです。

でも僕は、 周囲が期待する僕に飲み込まれるのはカッコよくないな と思うわけです。 「適度に周りが気持ちいいことを言っていればいい」と迎合したり、「あの人が動いてくれるだろう」と無思慮に他人へ期待することです。そうすると、何も考えなくても案外生きられてしまうんです(現状への過学習というか、局所最適化というか)。

その他人が求める役割に浸っていると、人は急速に老いるわけです。 それがおそらく、30歳前後にある1つの分岐点なのでしょう。

若さがなくなるのは別に構わないんです。気遣いを勘違いしたり、自分が確立した自分に収められる力へ負けると、急激に老いる気がします。 努力した分だけ「先輩の威厳」や「重鎮感」まで追加されます。でもそれは相手の優しさであって、あなた(僕)のすごさではないんです。

なので、僕はなるべく初めて会う人や話してくれる人に、なるべく相手の立場を考えて話そうと思います。 そして、他の人にもロールや名札に落とし込んだような話し方はしないでおこうと思います。

世の中に、接しやすい先輩や、イジりやすい年上の人っていますよね?あの人たちは謙虚なんじゃなくて生存戦略としてああやってるんです。自分を重鎮扱いして固定化しないでくれよー!老いさせないでくれよー!というアピールなんですね。役割を捨てたピュアな関わりを、あなたと持ち続けたいんです(僕はどうにも絡みにくい人間になりがちなので、尊敬します)。

若さを失うことは怖くないですが、老けさせようという圧力は恐ろしいものです。 そして僕はこれからも抗っていきたいと思います。

そんなことを感じる、元、若者エンジニアでした。

ハッピーバースデー僕!

これからも頑張って良いものを作りますので、応援いただけると嬉しいです。

そんなわけで

そんなわけで、老いに抗っている僕が作っている新しいWebサービスがこちらです。

ブログ記事やプレスリリースを執筆する 「当たり前」のやり方を変えたい と思っています! まだまだ至らないところはありますが、頑張ってどんどん良くしていきます。

shodo.ink

以上です!

ここまで読んでくれてありがとうございます。


執筆:Kiyohara Hiroki (@hirokiky)Shodoで執筆されました

JavaScriptのグローバルマッチする正規表現でtest()、exec()すると状態が残る

今日はちょっとしたメモ書きです。

JavaScriptでグローバルマッチの(gオプションを付けた)正規表現で、 正規表現.test() をすると実行した状態が残ります。

> const FOO_REGEX = new RegExp('fo+', 'g')
> FOO_REGEX.test('fooooo is foo')
true
> FOO_REGEX.test('fooooo is foo')
true
> FOO_REGEX.test('fooooo is foo')
false

正規表現.exec() でも同様です。

> FOO_REGEX.exec('foooo is foo')
[ 'foooo', index: 0, input: 'foooo is foo', groups: undefined ]
> FOO_REGEX.exec('foooo is foo')
[ 'foo', index: 9, input: 'foooo is foo', groups: undefined ]
> FOO_REGEX.exec('foooo is foo')
null
>

グローバルマッチする正規表現.lastIndex というプロパティに、「今現在どこまで処理したか」を記録します。 .test().exec() ではその場所から次にマッチするまで処理されるので、このような挙動になります。

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/test#using_test_on_a_regex_with_the_global_flag

ローカル変数の場合に問題はありませんが、 グローバル変数にグローバルマッチする正規表現を置いている場合には状態が残るので注意しましょう。主に 正規表現.replace() のために使うのでグローバルマッチが良いけど、該当の文字が存在するかのチェックだけで使いたい場合などが考えられます。

対処法の考察

ここは単純にグローバルマッチをやめるのが良いと思います。

> const FOO_REGEX = new RegExp('fo+')

FOO_REGEX.lastIndex = 0 のようにすると状態を初期化できますが、グローバル変数の状態を更新するのは美しくない印象です。

「でも主に .replace() を利用したいのでグローバルマッチを付けておきたい!」ということもありますが、この場合は、うーーーん??!!! 正規表現の文字列をグローバル変数に定義しつつ、2種類の正規表現も定義するとか?ちょっと他のアイディアもあれば教えてくれると嬉しいです(すいません)。

他には 文字列.match(正規表現) を使う方法もあります。文字列全体を検証した結果が返されるので、存在のチェックだけをしたい場合に無駄な処理が多くなります。短い文字列しかない前提であれば、これでも良いかと思います。

> 'foooo is foo'.match(FOO_REGEX)
['fooo', 'foo']

まとめ

グローバルマッチする正規表現.test().exec()正規表現に状態が残るという話をしました。

執筆:Kiyohara Hiroki (@hirokiky)Shodoで執筆されました

村社会はどこにでも

ふと思ったのですが資本主義も一歩引いて考えると村社会と言えるのではないでしょうか。

資本家が多くの力や発言力を持っているというのは、周知の事実だと思います。 この資本主義も多くの人が参加しているようで、実は狭い範囲の人がそのルールを握っています。

「村社会」 集落に基づいて形成される地域社会。特に、有力者を中心に厳しい秩序を保ち、しきたりを守りながら、よそ者を受け入れようとしない排他的な社会をいう。しきたりに背くと村八分などの制裁がある。 同類が集まって序列をつくり、頂点に立つ者の指示や判断に従って行動したり、利益の分配を図ったりするような閉鎖的な組織・社会を1にたとえた語。談合組織・学界・政界・企業などに用いる。

goo国語辞書「村社会」 https://dictionary.goo.ne.jp/word/村社会/

たしかに資本主義はよく機能するシステムですし、僕は社会主義共産主義が良いと話を展開する気はありません。 資本主義の問題について論じるのも、マイケル・サンデルマルクス・ガブリエルに譲ります。

でも僕が怖いなと思うのは、皆が実はジョーンズタウンで生活しているのでは?という疑念です。 規模が大きいだけの村やエコーチェンバーの中にいるのでは?という仮説です。

ジョーンズタウンは、アメリカ合衆国キリスト教新宗教(カルト)・人民寺院によってガイアナ北部に開拓・設立された町(コミューン)。(中略) 1978年11月18日、この人里離れたコミューンで計918人の集団自殺を決行したことで世界的に著名になった。

Wikipedia「ジョーンズタウン」 https://ja.wikipedia.org/wiki/ジョーンズタウン

もちろんこれは少し悲観的すぎる見解です。ですが「僕たちは案外狭い社会的価値観の中で生きているのでは?」と疑うことはできます。そして多くの人が言う 「お金が欲しい」というのは実のところ資本主義という村から脱出したい という願望にさえ思えます。

というよりも資本主義にしろ、日本社会、Twitter、コミュニティ、スタートアップ界隈にしろ、規模が違えど 村社会らしさはどんな集団も持っている ということです。

たとえば資本主義は一部の人(資本家)の声が大きくなりやすいシステムです。実際に力も持っていて人も動かせます。 TwitterなどのSNSも、それを作る人やレコメンドのAI、バズりやすい世俗的な記事の価値観が支配的になり得ます。 オープンさを大切にするコミュニティでも実は「中心となる人に好かれないと居心地が悪い思いをする」ような閉鎖性を持っていたりします(常にオープンな心で人と接するのは誰しも難しいものです)。

日本の閉鎖性を批判するひろゆきが、ファンに囲われた閉鎖的な村を形成していったように、どこにもそれは現れます。 極端な例ではオウム真理教サティアンや、連合赤軍の山岳ベースがありますが、その異常な状況も歴史を見れば徐々に歪んでいったことが分かります。

僕たちは案外、排他的で小さな村の中に生きています。資本主義という村、日本という村も、大きなようで実は小さく狭い価値観に左右されています。

なので、少し過激なくらいオープンで反発的なほうが良いのかもしれません。 人間の心には、閉鎖的になりたい欲求が常にあるからです。

執筆:Kiyohara Hiroki (@hirokiky)Shodoで執筆されました