Notionは関数が使えます。さすがにスプレッドシートほどの自由度はないので以前は「使えない」と切り捨てていましたが、実は工夫次第でいろいろできるのではないかと感じました。
たとえば「今業務で使っているスプレッドシートを(全部は無理でも)Notionに移行できるのでは!?」と。安易ですね〜、安易ですけども。
Notionで使える関数は限られているので、その中で何ができて何ができないのか検討してみようじゃないか。
今回は私(@saosaoyamayama)がスプレッドシートで自作している「記事のレギュレーションチェックツール」の一部をNotionで再現できるかチャレンジ! ですよ。
※2023/02/21、リレーション・ロールアップについて新たな投稿を作成しています。
※2023/02/18、containsについて新たな投稿を作成しています。
こんな人の役に立つかも
Notionの関数って何ができるのか知りたい人。私の正規表現に対する理解度の低さを笑いたい人。
Contents
今回やること・使う関数
スプレッドシートで作っている「記事のレギュレーションチェックツール」の仕組みはとても単純なのですが、さすがに何年も使っていると関数入れ子増殖の違法建築だらけです。
今回はあくまでチャレンジってことにして欲張らず、わかりやすそうな項目を試してみました。
ツールに搭載する機能
WordPressのテキスト(HTMLタグ直書き)に対して以下のチェックができるようにします。
- 代替テキストを入れ忘れている画像があったらアラートを出す
- H2タグがいくつあるか数える
- 文章以外(HTMLタグ・ショートコード・引用文章)を削除する
ちなみにNotionでは正規表現が使えます。これだけでハードルがグンと下がりました。
使いたい関数と使える関数
スプシで使っている関数と同じ働きをする関数、あるいは代替関数がないとNotionでツールが作れません。そこで最低限必要な関数がNotionで手に入るか調べてみました。
- スプシのlen:length
- スプシのcountif:ない→containsで代用できるかも
- スプシのsubstitute:replaceAll
スプシの「len」は文字数を数える関数で、Notionには「length」という関数がありました。
続いてスプシの「countif」ですが、Notionにはこれに相当する関数がありません。結果として「length」と「replaceAll」で代用しました。
また「含まれているかどうか」のチェックにも「countif」が使えますが、Notionには「contains」というピッタリな関数がありました。
スプシの「substitute」は置換の関数です。Notionでは「replaceAll」(場合によっては「replace」)が使えそうです。
「substitute」は今回使わない予定でしたが「countif」の代替関数として「substitute」が必要になってしまいました。どう使うかは後述ってことで。
Notionデータベースにおける関数の基本的なお作法
Notion関数の基本的なお作法について書いておきます。さっさとツールを作って見せろって人はジャンプしてください。
スプレッドシートにあってNotion(のデータベースのテーブルビュー)にないもの。それが「セル」の概念です。Notionのテーブルビューはたくさんのページを「表」の形で表示しているだけで、実際はページの集合体、っていうのは以前の投稿で書きました。
- スプレッドシート:表計算
- Notion:ブロック形式のメモ
Notionをスプレッドシートと同じように使う、という考え方がナンセンスですけど、でもお作法さえ押さえておくと「あれができるのでは?」とアイデアが浮かぶと思います。
Notionの関数は別の行(データ)を参照できない
Notionのデータベース(図左)では、行を超えて数値が参照できません。スプレッドシート(図右)ではセルを指定すれば基本的にどこにある数値でも参照できます。
たとえばNotionテーブルの左端にIDが振ってあり、右端にも同じIDを表示させることはできます。「=左端」という数式を右端に入れればOK。これはスプシでも同じですね。
でも「1段上の数字に1を足して連番にする」みたいなことはできません。表にしているから「1段上」に見るだけで、実際は異なるNotionページにあるので、もはや他人だと思ってください。
私と吉沢亮は他人ですが、2人とも生年月日や血液型っていうプロパティを持っていますよってこと(はい?)。
Notionのデータベースで関数を動かすときは「データ=ページ」っていう基本を意識すると混乱しません。
※テーブルビューで縦方向の集計はできます。合計とか平均とか。
セル指定ではなく列指定
スプレッドシートで値の場所を指定するときは「A列の23行目=A23」のように、縦横の交差点をアルファベットと数字で表します。
Notionではそもそもセルの概念がなく、参照できるのは自データのプロパティ(列)です。指定するのはとても直感的で、プロパティ名(列見出し)がそのまま使えます。
たとえばプロパティ名が「生年月日」だったらこんな感じ。
prop("生年月日")
[prop]はpropertyのことですね。プロパティ名は必ず二重引用符で囲みます。たとえプロパティ名が「0123」という数字だとしても、それは数値ではなくテキスト扱いなので二重引用符が必要です。
Notionの関数は勝手にarrayformulaされる
Notionの関数は勝手に「arrayformula」してくれます。簡単にいうと「縦方向に勝手に計算してくれる関数」ですかね。
arrayformulaは今のところスプレッドシートでしか使えない関数で、深く知りたい人はChatGPTにでも聞いてみてください調べてください。
図の左、青い点線内に関数を入れているとしましょう。赤い枠の関数を修正したら、点線内すべての関数に修正が適用されます。たとえ縦に100行データがあっても、関数は1個所修正すればOKです。
※赤い枠、大幅にズレていますね……。
その代わり、1列の中に異なる関数を適用することはできません。
スプレッドシートの場合、データを蓄積するデータベース(図右)があって、それを集計する集計表(図左)があって……みたいな使い方をすることがあります。Notionではこれができない。
Notionは1つの列内に異なる数式を適用できないので、集計表が作れません。もしかしたらリレーションなんかを使えば作れるのかもしれませんが、今のところ手を出していません。
代替テキスト入れ忘れチェック
実際に関数を使ってみます。今回チェックに使う記事を適当に作りました。謎のインデントはスルーしてください。
導入文です。
導入文ですよ〜。
導入文です。
<h2>最初のH2見出し</h2>
<figure>
<img src="https://doit-myself.com/wp-content/uploads/2022/08/example.jpg" alt="" size-full /><figcaption>1枚目の画像</figcaption>
</figure>
最初の段落です。
最初の段落ですよ〜!
<h3>最初のH3見出しです</h3>
H3見出し登場。
もう1個入れておきましょう。
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">ほげほげです。<br>ほげほげですよ。</p>ほげほげです。 (@saosaoyamayama) <a href="https://twitter.com/hogehoge">January 01, 2023</a></blockquote>
<h3>2つ目のH3見出しです</h3>
2つ目のH3見出し登場。
ショートコードも入れますよ。
[table id=12575 column_widths="20%|15%|12%|25%|28%"/]
[table id=00000 /]
<div id="mokuji">[outline]</div>
<h2>2つ目のH2見出し</h2>
<figure>
<img src="https://doit-myself.com/wp-content/uploads/2022/08/example.jpg" alt="てすてす" size-full /><figcaption>2枚目の画像</figcaption>
</figure>
次はリストなど入れておきましょうか。
<ul>
<li>項目1</li>
<li>項目2</li>
<li>項目3</li>
</ul>
H2見出しが2つと2つとH3見出しが2つ、画像が2つとショートコードが3つ、リストがあって、Twitterの埋め込みもありますね。
何をやるか
記事内に「alt=””」があったらアラートを出すっていう仕組みを作っていきますよ。
WEB記事の画像には通常代替テキストを設定します。
- 目が不自由なかたが読み上げ機能を利用する場合に代替テキストも読み上げ対象
- 万が一画像が表示できなかったら「こんな画像があるはずなんだけど」と言い訳する
こういった用途。ですから「代替テキストは入れましょうね」っていうクライアントがほとんどです。
代替テキストを設定していない場合、WordPressでは自動的に「alt=””」と記述されます。設定してあれば二重引用符の間にテキストが入っているはずです。
<img src="https://doit-myself.com/〜example.jpg" alt="" size-full />
<img src="https://doit-myself.com〜example.jpg" alt="てすてす" size-full />
ということで、記事内に「alt=””」があったらアラートを出すっていう仕組みがほしい。
プロパティをセットする
左端の[テキスト]には記事を入れました。2列目[検出したいタグ]は[alt=””]ですね。
上のキャプチャでは既に[アラート]というプロパティを作っていますが、まだ作っていないふうを装ってください。
※二重引用符は半角です。選び間違えるとタグが検出できないのでご注意を。この記事では何か変な二重引用符になっているのでコピペするならコードブロックのものを使ってくださいませ。
関数プロパティをセットする
アラート用のプロパティを作っていきますよ!
データベース右側の[+]をクリックすると、右側にプロパティ選択画面がニュルッと出現します。
その中から[Σ関数]を選びましょう。関数のアイコンは[Σ]です。カワイイ。
[プロパティを編集]のボックスにはプロパティ名を入れます。私は「アラート」にしました。
名前を入れたら[編集>]を選択します。
関数用のボックスが出現します。[数式を入力してください]のところに関数やプロパティを記入していくわけです。
左の[プロパティ]枠内には追加済のプロパティが表示されます。プロパティ名をクリックすればプロパティがprop付きで設定されていくのは地味に便利。
他にも[定数]や[関数]などいろいろあるので、使いたいものをクリックしていきましょう。[数式を入力してください]のところに関数が入っていきます。もちろん手打ちでもOK。記述の間はカンマで区切ってくださいませ。
関数名にオンマウスすると右側に関数の説明が表示されるので、確認しつつ進めてみてください。
【contains】
1番目の引数の中に2番目の引数が見つかったら、trueを返します。
contains(1番目の引数, 2番目の引数)
contains(prop("テキスト"), prop("検出したいタグ"))
記事内に[alt=””]が含まれていたら[true]を返す、というのを[contains]関数で設定しました。この場合のTRUEは「頼んでいないのにNotionが勝手に作ったチェックボックスにレ点が入る」って意味です。
このキャプチャの「アラート」列です。チェックボックスは勝手に生えてきます。
チェック入り=代替テキストを入れ忘れている画像がありますよ〜ってことです。
【参考】二重引用符の罠
Notion関数で使う「引数」はプロパティで記述しなくてもいいんです。たとえば記事内に「img」という文字列が含まれているかどうか調べる場合、以下の記述でいけます。
contains(prop("テキスト"), "img")
スプレッドシートと同じですね。
では代替テキスト「alt=””」の場合はどうか。以下の形で関数を記述してみたのですが。
contains(prop("テキスト"), "alt=""")
これだとエラーを吐きます。エラーを吐くっていうか[完了]が押せないから関数が適用できない。
どうやらNotionの関数では、二重引用符をテキスト要素として使うとエラーを吐くみたいです。
そのため二重引用符を含む文字列を検出したい場合は、プロパティを作ってテキストを置いて、関数に代入しましょう。
※シングル引用符に変えたりいろいろやってみましたがダメでした。
【追記】containsでは正規表現が検出できないらしい
執筆時点では気付きませんでしたが、いろいろやっているうちに気付いてしまいました……。
以下の記事でご紹介しています。
H2見出しがいくつあるかチェック
よーし、これでもう関数のセッティング方法はバッチリっすね。
続いてはテキスト内に含まれる<h2></h2>タグセットを数えます。セットではなく<h2>単品でもいいんですけど、いろいろあって<h2></h2>をセットで数えています。
スプレッドシートの場合だと[countif]を使って数式1つで記述できます(<h2>を検出する場合)。
=countif(記事が入っているセル, "<h2>*")
「*」はワイルドカードと呼ばれていて「とりあえず何でもいいからテキストがある」を表します。「テスト<h2>」は検出できないけれど「<h2>テスト」は検出できる。
※Notionでワイルドカードは使えませんでした。ザンネーン。
でもNotionには[countif]がありません。今回は以下の2つの関数を使って泥臭く計算します。
- replaceAll:スプシのsubstituteに相当、指定文字列を別の文字列に置換する
- length:スプシのlenに相当、文字数を数える
【replaceAll】
テキスト内でAに一致する個所を、Bに置き換えます。
replaceAll(テキスト, A, B)
【length】
テキストの文字数をカウントします。
length(テキスト)
何をやるか
かなり泥臭いですが1工程ずつプロパティを作ってみました。テキストはちょっとシンプルにしてあります。
<h2>と</h2>が1つずつ含まれていたら「1」と数えます。上キャプチャの右端が[H:タグの数]が「1」になっていますね。
【テキスト】
朝顔咲いたよ<h2>あいうえお</h2>。
A:文字数
→length(prop("テキスト")) =21文字
B:正規表現
<\/?h2>
C:HTMLタグ
<h2></h2>
D:タグの文字数
→length(prop("C:HTMLタグ")) =9文字
E:正規表現を""に置換
→replaceAll(prop("テキスト"), prop("B:正規表現"), "")
朝顔咲いたよあいうえお。
※<\/?h2>は、h2の開始タグ・閉じタグいずれも一致する正規表現です。
F:置換後の文字数
→length(prop("E:正規表現を""に置換")) =12文字
G:削除された文字数
→prop("A:文字数") - prop("F:Eの文字数") =21-12 =9文字
H:タグの数(開始・閉じセット)
→prop("G:削除された文字数") / prop("D:タグ文字数") =9/9 =1セット
何をやっとるかというと、こんな感じ。
- <h2>・</h2>タグを「””」に置換(つまり削除)
- 削除前と削除後の文字数を引き算して何文字削除したのかを算出
- <h2></h2>何セット相当を削除したのか算出
関数って「一気に計算する方法は知らん。でもこれとこれを組み合わせたらできる」みたいなところから覚えていきませんか?
あ、私だけかな。こういう泥臭いことを今までずーっとやってきたので、私はとても楽しい。
数式を1つにまとめる
H2タグを数えるためにプロパティを8個も設定するのは割に合わないです、給料上げてください。ということで数式を1つにまとめます。
必要なプロパティは3つだけ(記事は除く)。正規表現と、タグの文字数を数えるためのHTMLタグ、おまとめ関数です。
以下の数式を「タグの数」にぶっ込みました。
(length(prop("テキスト")) - length(replaceAll(prop("テキスト"), prop("B:正規表現"), ""))) / length(prop("C:HTMLタグ"))
↓↓↓
([削除前のテキスト文字数] - [削除後のテキスト文字数])÷ タグセットの文字数
よし、1つにまとまった。
こうやってプロパティ参照型にしておけば、正規表現やHTMLタグを書き換えるだけで別のタグが検出できます。関数の書き換え不要で使い回せますわな。
HTMLタグ・ショートコード・引用文章を削除する
タグやショートコードなどを除いたテキストだけ、を取り出したい。これは別のツールに貼り付けたいからですね。たとえば校正ツールとか類似度チェックツールとか。
類似度チェックでは、引用タグに囲まれた引用文も削除しないと類似度が上がってしまいます。
やることはとてもシンプルで、正規表現を使って余計なものを「””」に置き換える関数を作るってことです。
先にお断りしておきます。私は正規表現を理解しきれていません。もしここに記載する胡散臭い正規表現がうまくマッチしなかったらご自身で試行錯誤してください。
- 引用タグ〜引用閉じタグまでを消す
- HTMLタグを消す
- ショートコードを消す
この3つを1つの数式にぶち込みます。うまく走らせるには、関数の入れ子の順番がキモでした。
引用タグ〜引用閉じタグまでを消す
以下の関数を使うと、引用タグ〜引用テキスト〜引用閉じタグの削除がうまくいきました。
replaceAll(prop("テキスト"), "<blockquote.+/blockquote>", "")
※正規表現は直書きじゃなくてプロパティでもいいですよ〜。
結果は以下のとおり。
この例ではTwitterの埋め込みを削除しています。Twitterって<blockquote>タグの中に<p>とか<a>なんかも入っているんですが、この正規表現でまとめて葬り去ります。
ただしTwitterの埋め込みを削除したあとに半角スペースが残るんですよね〜。まあそこは追々修正で。
HTMLタグを削除する
続いてはHTMLタグを一掃しますよ。
replaceAll(prop("テキスト"), "<[^>]+>", "")
「<」で始まり「>」で終わるHTMLタグを検出します。とりあえずこいつを単品で仕掛けると、こんな感じ。
右側のテキストをご覧ください。HTMLタグが全部消えてますよね! 素晴らしい、これですよこれ。ただし……引用タグが消えて引用テキストが残ってしまうんですよ。
これを避けるために、まずは引用タグ+引用テキストを削除して、それからHTMLタグを一掃する。この順番を守りましょうってことですね。
replaceAll(replaceAll(prop("テキスト"), "<blockquote.+/blockquote>", ""), "<[^>]+>", "")
入れ子にした結果が以下のとおり。
これでTwitterの引用テキストもHTMLタグも削除できましたね!
ちなみにスプレッドシートでHTMLタグを削除する際は別の正規表現を使っていたんです。
<("[^"]*"|'[^']*'|[^'">])*>
でもNotionではうまくいきませんでした。二重引用符とかワイルドカードのせいかな……。厄介だなぁ。
ショートコードを削除する
今度はショートコードです。相当試行錯誤しました。必要なテキストまで消える……。かなり調整しまくりました。
replaceAll(prop("テキスト"), "[^」^。^>^]+]", "")
この正規表現、なんかカワイイ。まあいいや。まずはショートコード単品を削除してみます。
はーい、HTMLタグに囲まれているショートコードまでさっくりサクサク削除できました。
では入れ子にしてみましょう。
replaceAll(replaceAll(replaceAll(prop("テキスト"), "<blockquote.+/blockquote>", ""), "<[^>]+>", ""), "[^」^。^>^]+]", "")
よしよし、全部消えたぞ。
このような形で、正規表現がしっかりマッチすればいろいろなものが削除できると思います。
うまくやればテキストの一部分だけを取り出すっていうのもできそうな気がします。
あとは空白行と残存スペースを削除して仕上げたい所存
ええ、このあたりはまだ途中です。
スペースや空白行(改行2回)を一掃するっていうのもやっていますが、まだ完璧ではありません。
もうちょっとがんばってみます。
検出したいワードが大量にある場合が課題かも
クライアントによってはNGワードや漢字の閉じ・開き指定がたくさんあるんですよね。
スプレッドシートだったら1つのセルにテキストを突っ込んで、NGワードをズラーっと縦に並べて、検出関数はオートフィルで伸ばせばあっという間です。
でもNotionの場合はそうもいきません。
上のキャプチャのようにテキストを縦方向に何度も貼り付けるか、あるいは右方向にチェック用プロパティをガンガン増やすしかありません。
1回手間を掛けて全NGワードを設定すればいい。だがそれが面倒くさい!
ここがクリアできれば、スプシのツールを完全再現できると思うんですけど……。
ひとまずは引用・タグ・ショートコード削除機能は実務で使い始めました。スプレッドシートよりいいかも。
引き続き、いろいろ工夫しながら話題を提供しようと思っておりまーす。
※【追記】リレーションとロールアップを使って割と効率良く遊べる方法が分かりました。