LoginSignup
8
1

More than 1 year has passed since last update.

Enum.sort_by/3で複雑な並び替え仕様を簡単に実装する(Elixir)

Last updated at Posted at 2022-03-05

つくばねの峰より落つるみなの川恋ぞ積りて淵となりぬる

Advent Calendar 2022 62日目1の記事です。
I'm looking forward to 12/25,2022 :santa::santa_tone1::santa_tone2::santa_tone3::santa_tone4::santa_tone5:
私のAdvent Calendar 2022 一覧


はじめに

Elixirを楽しんでいますか:bangbang::bangbang::bangbang:

今日は.md内のテーブルをソートしてみます。
Enum.sort_by/3を使います。

仕様

仕様は以下の通りとします。

  • README.mdの構造はかわらないものとする
    • 特にテーブルの列は変わらないものとする
  • テーブルの行は増えることがある
  • テーブルの並び順は基本的に以下の順番とする
    • Category順(UTF-8の文字列順)
    • Name順(UTF-8の文字列順)
    • LanguageはJapaneseを優先、他はUTF-8の文字列順
    • URL順(UTF-8の文字列順)
  • ただし、Category=Etcはテーブルの後方に置く

並び替えの仕様が複雑です。

サンプル

サンプルを示します。

Input

InputのREADME.mdはこういうものです。

README.md
# Awesome Twitter Communities for Engineers

[![Build Status](https://github.com/mattn/awesome-twitter-communities/actions/workflows/lint.yaml/badge.svg?branch=main)](https://github.com/mattn/awesome-twitter-communities/actions/workflows/lint.yaml?query=branch%3Amain)

|Name|Category|Language|URL|
|-|-|-|-|
|AWS|Cloud Infrastructure|English|<https://twitter.com/i/communities/1471503983839567878>|
|WebRTC|Communication technology|English|<https://twitter.com/i/communities/1498133315164860419>|
|Kubernetes|Container|English|<https://twitter.com/i/communities/1444745802383953921>|
|Kubernetes & コンテナ技術|Container|Japanese|<https://twitter.com/i/communities/1498974495989956614>|
|MySQL-JP|Database|Japanese|<https://twitter.com/i/communities/1496795585982382084>|
|NewSQL_jp|Database|Japanese|<https://twitter.com/i/communities/1498603867285581824>|
|自作DBMS友の会|Database|Japanese|<https://twitter.com/i/communities/1498114917672505344>|
|OSS 日本語 L10n|Localization|Japanese|<https://twitter.com/i/communities/1498961283546157061>|
|Linuxカーネル|OS|Japanese|<https://twitter.com/i/communities/1499543572189618176>|
|vyosjp|OS|Japanese|<https://twitter.com/i/communities/1498101007733370880>|
|.NET_Japan|Programming Language|Japanese|<https://twitter.com/i/communities/1496977662640009217>|
|C++|Programming Language|Japanese|<https://twitter.com/i/communities/1499396722514345984>|
|Deno-ja|Programming Language|Japanese|<https://twitter.com/i/communities/1498174921562013698>|
|F# on Twitter|Programming Language|English|<https://twitter.com/i/communities/1493280005589196801>|
|Java|Programming Language|Japanese|<https://twitter.com/i/communities/1497219281079398402>|
|Java|Programming Language|English|<https://twitter.com/i/communities/1471178821906821122>|
|Julia Programming Language|Programming Language|English|<https://twitter.com/i/communities/1441046367514755082>|
|Julia言語|Programming Language|Japanese|<https://twitter.com/i/communities/1499390501467811845>|
|PHP-ja|Programming Language|Japanese|<https://twitter.com/i/communities/1497741277789835264>|
|Pythonジャパン|Programming Language|Japanese|<https://twitter.com/i/communities/1498184748728205315>|
|The Go Programming Language|Programming Language|English|<https://twitter.com/i/communities/1493637136502960134>|
|TypeScript|Programming Language|Japanese|<https://twitter.com/i/communities/1499329858178289664>|
|WebAssembly|Programming Language|English|<https://twitter.com/i/communities/1497545442023944192>|
|clojure|Programming Language|English|<https://twitter.com/i/communities/1494013093059432451>|
|dotnet|Programming Language|English|<https://twitter.com/i/communities/1488624124817666051>|
|elixir|Programming Language|English|<https://twitter.com/i/communities/1493287155942232066>|
|elixir-jp|Programming Language|Japanese|<https://twitter.com/i/communities/1498232167864082435>|
|ruby-jp|Programming Language|Japanese|<https://twitter.com/i/communities/1496768365683408900>|
|サバイバルTypeScript|Programming Language|Japanese|<https://twitter.com/i/communities/1499723154956390401>|
|プログラミング言語Go|Programming Language|Japanese|<https://twitter.com/i/communities/1498095077222400000>|
|プログラミング言語Rust|Programming Language|Japanese|<https://twitter.com/i/communities/1498496039401451522>|
|プログラミング言語処理系|Programming Language|Japanese|<https://twitter.com/i/communities/1499381283864342530>|
|Security Engineering|Security|Japanese|<https://twitter.com/i/communities/1498138021723467781>|
|Webセキュリティ|Security|Japanese|<https://twitter.com/i/communities/1498053973097730048>|
|VS Codeの会|Text Editor|Japanese|<https://twitter.com/i/communities/1498865559353511941>|
|emacs-jp|Text Editor|Japanese|<https://twitter.com/i/communities/1498276712034947072>|
|vim-jp|Text Editor|Japanese|<https://twitter.com/i/communities/1497961032404594691>|
|Ebiten|Tools/Libraries|Japanese|<https://twitter.com/i/communities/1498350105346600960>|
|Flutter|Tools/Libraries|English|<https://twitter.com/i/communities/1472249315724771329>|
|Jotai Friends JP|Tools/Libraries|Japanese|<https://twitter.com/i/communities/1497150937806213120>|
|Nuxt|Tools/Libraries|English|<https://twitter.com/i/communities/1498235047194808320>|
|SATySFi|Tools/Libraries|Japanese|<https://twitter.com/i/communities/1498074334619123712>|
|Spring Developers|Tools/Libraries|English|<https://twitter.com/i/communities/1496544801533091844>|
|Svelte日本|Tools/Libraries|Japanese|<https://twitter.com/i/communities/1499182207491260424>|
|DATA Saber|Etc|Japanese|<https://twitter.com/i/communities/1498660583415361536>|
|IE11を偲ぶ会|Etc|Japanese|<https://twitter.com/i/communities/1498870842784043009>|
|ITエンジニアを褒める会|Etc|Japanese|<https://twitter.com/i/communities/1494319842585083906>|
|JMUG|Etc|Japanese|<https://twitter.com/i/communities/1498585537527320577>|
|Japan Okta User Group|Etc|Japanese|<https://twitter.com/i/communities/1498432461839036418>|
|SREとかObservabilityとか|Etc|Japanese|<https://twitter.com/i/communities/1498088713670172675>|
|Tech Twitter|Etc|English|<https://twitter.com/i/communities/1472105760389668865>|
|Tech Twitter - Japan|Etc|Japanese|<https://twitter.com/i/communities/1494649689215737856>|
|Twitter Developers Japan|Etc|Japanese|<https://twitter.com/i/communities/1493041080077795328>|
|UdonCreatorCommunity|Etc|Japanese|<https://twitter.com/i/communities/1497068375553765378>|
|nlp-jp|Etc|Japanese|<https://twitter.com/i/communities/1498287599928365062>|
|sudo-jp|Etc|Japanese|<https://twitter.com/i/communities/1499227044982374401>|
|ウェブパフォーマンス|Etc|Japanese|<https://twitter.com/i/communities/1498089387422515202>|
|ガジェット|Etc|Japanese|<https://twitter.com/i/communities/1498224086652121099>|
|バンクーバー日本人ソフトウェアエンジニア|Etc|Japanese|<https://twitter.com/i/communities/1498574247895056384>|
|吉祥寺.pm|Etc|Japanese|<https://twitter.com/i/communities/1498106494989967363>|
|日本のウェブフォームを良くする開発者の会|Etc|Japanese|<https://twitter.com/i/communities/1498168121336614916>|
|自作キーボード|Etc|Japanese|<https://twitter.com/i/communities/1495468692813287425>|
|鹿児島ITエンジニア|Etc|Japanese|<https://twitter.com/i/communities/1496763936682954752>|
|ネットワーク自動化|Etc|Japanese|<https://twitter.com/i/communities/1498540422154821633>|

Output

これを正解とします。

README.md
# Awesome Twitter Communities for Engineers

[![Build Status](https://github.com/mattn/awesome-twitter-communities/actions/workflows/lint.yaml/badge.svg?branch=main)](https://github.com/mattn/awesome-twitter-communities/actions/workflows/lint.yaml?query=branch%3Amain)

|Name|Category|Language|URL|
|-|-|-|-|
|AWS|Cloud Infrastructure|English|<https://twitter.com/i/communities/1471503983839567878>|
|WebRTC|Communication technology|English|<https://twitter.com/i/communities/1498133315164860419>|
|Kubernetes|Container|English|<https://twitter.com/i/communities/1444745802383953921>|
|Kubernetes & コンテナ技術|Container|Japanese|<https://twitter.com/i/communities/1498974495989956614>|
|MySQL-JP|Database|Japanese|<https://twitter.com/i/communities/1496795585982382084>|
|NewSQL_jp|Database|Japanese|<https://twitter.com/i/communities/1498603867285581824>|
|自作DBMS友の会|Database|Japanese|<https://twitter.com/i/communities/1498114917672505344>|
|OSS 日本語 L10n|Localization|Japanese|<https://twitter.com/i/communities/1498961283546157061>|
|Linuxカーネル|OS|Japanese|<https://twitter.com/i/communities/1499543572189618176>|
|vyosjp|OS|Japanese|<https://twitter.com/i/communities/1498101007733370880>|
|.NET_Japan|Programming Language|Japanese|<https://twitter.com/i/communities/1496977662640009217>|
|C++|Programming Language|Japanese|<https://twitter.com/i/communities/1499396722514345984>|
|Deno-ja|Programming Language|Japanese|<https://twitter.com/i/communities/1498174921562013698>|
|F# on Twitter|Programming Language|English|<https://twitter.com/i/communities/1493280005589196801>|
|Java|Programming Language|Japanese|<https://twitter.com/i/communities/1497219281079398402>|
|Java|Programming Language|English|<https://twitter.com/i/communities/1471178821906821122>|
|Julia Programming Language|Programming Language|English|<https://twitter.com/i/communities/1441046367514755082>|
|Julia言語|Programming Language|Japanese|<https://twitter.com/i/communities/1499390501467811845>|
|PHP-ja|Programming Language|Japanese|<https://twitter.com/i/communities/1497741277789835264>|
|Pythonジャパン|Programming Language|Japanese|<https://twitter.com/i/communities/1498184748728205315>|
|The Go Programming Language|Programming Language|English|<https://twitter.com/i/communities/1493637136502960134>|
|TypeScript|Programming Language|Japanese|<https://twitter.com/i/communities/1499329858178289664>|
|WebAssembly|Programming Language|English|<https://twitter.com/i/communities/1497545442023944192>|
|clojure|Programming Language|English|<https://twitter.com/i/communities/1494013093059432451>|
|dotnet|Programming Language|English|<https://twitter.com/i/communities/1488624124817666051>|
|elixir|Programming Language|English|<https://twitter.com/i/communities/1493287155942232066>|
|elixir-jp|Programming Language|Japanese|<https://twitter.com/i/communities/1498232167864082435>|
|ruby-jp|Programming Language|Japanese|<https://twitter.com/i/communities/1496768365683408900>|
|サバイバルTypeScript|Programming Language|Japanese|<https://twitter.com/i/communities/1499723154956390401>|
|プログラミング言語Go|Programming Language|Japanese|<https://twitter.com/i/communities/1498095077222400000>|
|プログラミング言語Rust|Programming Language|Japanese|<https://twitter.com/i/communities/1498496039401451522>|
|プログラミング言語処理系|Programming Language|Japanese|<https://twitter.com/i/communities/1499381283864342530>|
|Security Engineering|Security|Japanese|<https://twitter.com/i/communities/1498138021723467781>|
|Webセキュリティ|Security|Japanese|<https://twitter.com/i/communities/1498053973097730048>|
|VS Codeの会|Text Editor|Japanese|<https://twitter.com/i/communities/1498865559353511941>|
|emacs-jp|Text Editor|Japanese|<https://twitter.com/i/communities/1498276712034947072>|
|vim-jp|Text Editor|Japanese|<https://twitter.com/i/communities/1497961032404594691>|
|Ebiten|Tools/Libraries|Japanese|<https://twitter.com/i/communities/1498350105346600960>|
|Flutter|Tools/Libraries|English|<https://twitter.com/i/communities/1472249315724771329>|
|Jotai Friends JP|Tools/Libraries|Japanese|<https://twitter.com/i/communities/1497150937806213120>|
|Nuxt|Tools/Libraries|English|<https://twitter.com/i/communities/1498235047194808320>|
|SATySFi|Tools/Libraries|Japanese|<https://twitter.com/i/communities/1498074334619123712>|
|Spring Developers|Tools/Libraries|English|<https://twitter.com/i/communities/1496544801533091844>|
|Svelte日本|Tools/Libraries|Japanese|<https://twitter.com/i/communities/1499182207491260424>|
|DATA Saber|Etc|Japanese|<https://twitter.com/i/communities/1498660583415361536>|
|IE11を偲ぶ会|Etc|Japanese|<https://twitter.com/i/communities/1498870842784043009>|
|ITエンジニアを褒める会|Etc|Japanese|<https://twitter.com/i/communities/1494319842585083906>|
|JMUG|Etc|Japanese|<https://twitter.com/i/communities/1498585537527320577>|
|Japan Okta User Group|Etc|Japanese|<https://twitter.com/i/communities/1498432461839036418>|
|SREとかObservabilityとか|Etc|Japanese|<https://twitter.com/i/communities/1498088713670172675>|
|Tech Twitter|Etc|English|<https://twitter.com/i/communities/1472105760389668865>|
|Tech Twitter - Japan|Etc|Japanese|<https://twitter.com/i/communities/1494649689215737856>|
|Twitter Developers Japan|Etc|Japanese|<https://twitter.com/i/communities/1493041080077795328>|
|UdonCreatorCommunity|Etc|Japanese|<https://twitter.com/i/communities/1497068375553765378>|
|nlp-jp|Etc|Japanese|<https://twitter.com/i/communities/1498287599928365062>|
|sudo-jp|Etc|Japanese|<https://twitter.com/i/communities/1499227044982374401>|
|ウェブパフォーマンス|Etc|Japanese|<https://twitter.com/i/communities/1498089387422515202>|
|ガジェット|Etc|Japanese|<https://twitter.com/i/communities/1498224086652121099>|
|ネットワーク自動化|Etc|Japanese|<https://twitter.com/i/communities/1498540422154821633>|
|バンクーバー日本人ソフトウェアエンジニア|Etc|Japanese|<https://twitter.com/i/communities/1498574247895056384>|
|吉祥寺.pm|Etc|Japanese|<https://twitter.com/i/communities/1498106494989967363>|
|日本のウェブフォームを良くする開発者の会|Etc|Japanese|<https://twitter.com/i/communities/1498168121336614916>|
|自作キーボード|Etc|Japanese|<https://twitter.com/i/communities/1495468692813287425>|
|鹿児島ITエンジニア|Etc|Japanese|<https://twitter.com/i/communities/1496763936682954752>|

Elixirのプログラム

sort.exs
lines =
  File.read!("README.md")
  |> String.split("\n")

fixed_contents_cnt =
  lines
  |> Enum.find_index(& &1 == "|-|-|-|-|")
  |> Kernel.+(1)

table_contents =
  lines
  |> Enum.drop(fixed_contents_cnt)
  |> Enum.reject(& &1 == "")

mapper = fn name, category, language, url ->
  language = if language == "Japanese", do: "\u0000", else: language

  if category == "Etc" do
    {category, name, language, url, :etc}
  else
    {category, name, language, url}
  end
end

sorted_table_contents =
  table_contents
  |> Enum.map(& {&1, String.split(&1, "|")})
  |> Enum.sort_by(fn {_, ["", name, category, language, url, ""]} ->
    mapper.(name, category, language, url)
  end)
  |> Enum.map(& elem(&1, 0))

Enum.take(lines, fixed_contents_cnt)
|> Kernel.++(sorted_table_contents)
|> Kernel.++([""])
|> Enum.join("\n")
|> then(&File.write!("README.md", &1))
Dockerfile
FROM elixir:1.13.3-slim

WORKDIR /app

COPY . .

CMD ["elixir", "sort.exs"]

解説

少し解説をします。
Dockerfileは、Elixirを未インストールでも動かせるように用意しました。
Elixirをインストール済の方は気にしないでください。

このプログラムの肝はソートです。
Enum.sort_by/3を使いました。
この関数に指定するmapper関数が肝です。
mapper関数は、各要素どうしを何で比較するのかを返します。

もう一度仕様を記載します。

  • テーブルの並び順は基本的に以下の順番とする
    • Category順(UTF-8の文字列順)
    • Name順(UTF-8の文字列順)
    • LanguageはJapaneseを優先、他はUTF-8の文字列順
    • URL順(UTF-8の文字列順)
  • ただし、Category=Etcはテーブルの後方に置く

タプルを使って、比較したい順に要素を並べればよいです。

mapper = fn name, category, language, url ->
  language = if language == "Japanese", do: "\u0000", else: language

  if category == "Etc" do
    {category, name, language, url, :etc}
  else
    {category, name, language, url}
  end
end

例外部分については以下のように細工をしました。
「LanguageはJapaneseを優先、他はUTF-8の文字列順」については、Japaneseの場合にはNULL文字だけの文字列で置き換えることとしました。
NULL文字だけの文字列は、Elixirは以下のように書きます。

  • "\u0000" もしくは
  • <<0>>

(追記) 単純に空文字列 "" でよいです。

「Category=Etcはテーブルの後方に置く」についてはタプルの要素を増やしておくことで後ろにまわすことにしました。

Elixirは、ソートをすっきり書けます。

動かし方

InputのREADME.mdを同じフォルダに置いておいてください。

docker build -t awesome-twitter-communities-sort .                
docker run --rm -v "$PWD":/app awesome-twitter-communities-sort

もちろんElixirをインストール済の場合は以下のように実行できます。

elixir sort.exs

mattn/awesome-twitter-communities

Twitterのコミュニティのうち、技術系のコミュニティを @mattn さんがまとめてくださっています。
ありがとうございます!

たまに @mattn さんがソートコミットを入れられていたので、Elixirで書いてみたらどうなるのだろう? とおもって書いてみました。
きっとすでにツールはお持ちだったり、GitHub Actionsに自動ソートが組み込まれていいるのじゃないかとおもったりもしました。
せっかく作ったので、プルリクエストを送ってみました。
rejectされてもいいや〜 という気持ちです。
その旨、コメントに I don't mind と書きました。

プルリクエストを送ったあとにすでに、@mattn さんのほうでGoのプログラムが作られていることに気づきました。

それで、やっぱりプルリクエストはrejectでした :sweat_smile:
けれでも、Enum.sort_by/3に詳しくなれたし、Elixirは文字列操作がしやすいなあと感じられたので良かったです。
何よりも私は、Elixirを楽しめました。

何気に順番も大事で、
https://github.com/mattn/sort-awesome-twitter-communities/ リポジトリを先に見つけていたらElixirで作ることはしなかったかもしれません。

今回はソートがポイントだったのでやはり
$\huge{順番は大事💜}$
です。


mattn/awesome-twitter-communitiesにプルリクエストを送る際には、@mattn さんのsort-awesome-twitter-communities/でテーブルの行の並び替えを
$\huge{そ〜っと💜}$
しておくと良いとおもいます。

ソート

mattn/awesome-twitter-communitiesformat-readme.cmdformat-readme.shが追加されていました。
macOSで試しました。

./format-readme.sh

2022/03/05 12:12 adc0682では、Javaのところで差分がでました。
Languageのところをadc0682ではJapaneseが前にきていますが、スクリプトを実行するとEnglishが前にきます。
これはこんなところで言う話ではなくて、GitHubで言う話でしょうが、正解は作者の方の最新が正解ですので、特にアクションは起こしません。

この記事は架空のREADME.mdを題材とした話で、LanguageはJapaneseを優先ということをそのままにしておきます。
そのほうがプログラムに妙味がでるので。


git clone https://github.com/mattn/sort-awesome-twitter-communities
cd sort-awesome-twitter-communities

README.mdを同じフォルダに置いておいて

go main.go

Wrapping up :lgtm::lgtm::lgtm::lgtm::lgtm:

Enjoy Elixir:bangbang::bangbang::bangbang:
$\huge{Enjoy\ Elixir🚀}$

今回は、Enum.sort_by/3に指定するmapper関数にて、並び替えたい優先度順に要素を並べておくだけで、複雑な並び替え仕様を簡単に実装できることを示しました。

mattn/awesome-twitter-communitiesにプルリクエストを送る際には、@mattn さんのsort-awesome-twitter-communities/でテーブルの行の並び替えを
$\huge{そ〜っと💜}$
しておくと良いとおもいます。
format-readme.sh or format-readme.cmdの実行を〜!!!

以上です。

  1. @kaizen_nagoya さんの「「@e99h2121 アドベントカレンダーではありますまいか Advent Calendar 2020」の改訂版ではありますまいか Advent Calendar 2022 1日目 Most Breakthrough Generator」から着想を得て、模倣いたしました。

8
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
1