Platio式

Platioでミニアプリを作成すると、以下の様な簡単な計算をしたくなることがあります。

Platio式は、このような計算をするための仕組みです。なお、Platio式は、JavaScriptの式に良く似ていますが、JavaScriptの全ての機能を使えるわけではありません。

使用例

はじめに、いくつかの具体的な例を見てみましょう。

数値計算フィールド

数値計算フィールドの値プロパティには、Platio式を指定することができます。

例えば、単価フィールド(数値)、数量フィールド(数値)、合計フィールド(数値計算)があったとします。単価フィールドのカラムIDがc9dda012、数量フィールドのカラムIDがc48fdfa4のとき、合計フィールドの値プロパティに、c9dda012 * c48fdfa4を指定すると、単価と数量を掛け合わせた結果が合計フィールドに設定されることになります。

テキスト生成フィールド

同様にテキスト生成フィールドの値プロパティにも、Platio式を指定することができます。

例えば、姓フィールド(テキスト)、名フィールド(テキスト)と、名前フィールド(テキスト生成)があった場合に、名前フィールドの値プロパティに、c14d3611 + " " + c42b023cを指定すると、姓と名を空白文字で繋げた名前が名前フィールドに設定されることになります。ただし、c14d3611は姓フィールドのカラムID、c42b023cは名フィールドのカラムIDとします。

Webhookの変換

Webhookで外部のサーバーに通知を送るときには、Webhook プログラミングガイドで説明されている様に、決まったJSON形式でレコードがサーバーに送られます。しかし、他のサービスに直接接続する場合には、そのサービスが必要としている形式に変換する必要があります。Platio式を使用して、この変換を行うことができます。

例えば、以下の様な形式のJSONを受け取るサービスに接続することを考えます。

{
  "subject": "<ここに件名>",
  "body": "<ここに本文>"
}

subjectbodyに、件名フィールドや本文フィールドで指定された値を設定したい場合には、以下の様な変換用のPlatio式を指定します。cff8e7afc80c825eは、それぞれ件名フィールドと本文フィールドのカラムIDです。

{
  "subject": cff8e7af,
  "body": c80c825e
}

Webhookの条件

Webhookを使って、レコードが特定の条件を満たした時にのみ、サーバーに通知を送ることもできます。

例えば、温度フィールドの値が40度を超えたときにのみ通知を送りたい場合、条件としてc978eb40 > 40を指定します。c978eb40は、温度フィールドのカラムIDです。

複雑な数値計算フィールド

少し複雑な数値計算フィールドを考えてみましょう。商品名と値段を一緒に選択できる複数テキスト選択フィールドがあります。選択できる値は、商品A (¥1000)商品B (¥500)商品C (¥800)のようになっているとします。ここで、数値計算フィールドに選択された商品の合計金額を設定するには、以下の様なPlatio式を使用します。c0be75a4は、複数テキスト選択フィールドのカラムIDです。

sum(map(c0be75a4, value => number(replace(value, /.*\(¥(\d+)\)$/, "$1"))))

引数

Platio式を評価するときには、いくつかの引数が渡されますので、それらの引数を式の中で使用することができます。上の例では、カラムのIDを式の中で使用していましたが、これらが引数です。

フィールドの値を計算したり、Webhookの変換・条件にPlatio式を使うときには、カラムのIDを名前とする引数が渡されます。それぞれの引数の値は、そのカラムの値をPlatio式で扱いやすい様に変換した値になります。その他に、recordという名前の引数が渡されますが、この引数にはレコードのJSON表現がそのまま渡されます。

例として、テキストフィールドと複数数値選択フィールドを一つずつ持つレコードを考えます。それぞれのカラムのIDは、c63ae565c00bb867とします。この場合、以下の3つの引数が渡されます。

上記の様に、カラムのIDを使った引数を使うと、扱いやすい形でレコードの値を参照することができます。例えば、record.values.c00bb867.value[1].valueの代わりにc00bb867[1]を使用することができます。通常は、c63ae565c00bb867を使用しますが、それ以上の情報を使用したい場合には、recordを使用することができます。

この例の様に、ネストしているオブジェクトの引数を参照するときには、.を使用します。また、配列の要素を参照するには、0から始まるインデックスを、[]内に指定します。

元の値からカラムのIDを使った引数の値にするときに、どの様な変換が行われるかについては、型と値を参照してください。

また、record引数の形式は、Platio API リファレンスを参照してください。また、値の形式については、型と値を参照してください。

値の指定されていない引数

カラムのIDで引数を参照する場合に、そのカラムに値が指定されていない場合には、その型のデフォルトの値を取得します。例えば、テキストフィールドの値が指定されていない場合には空文字列を、数値フィールドの値が指定されていない場合には0を取得します。

各型のデフォルト値については、型と値を参照してください。

リファレンス

Platio式では以下の型が扱えます。各型の値が取れる値は、JavaScriptと同等です。

式に現れる値は、これらの型のいずれかの値か、nullまたはundefinedになります。

リテラル

以下の型にはリテラル表現があります。

文字列

JavaScriptの文字列リテラルと同様ですが、ダブルクオートで括る必要があります(シングルクオートはサポートしていません)。文字列中のエスケープの方法もJavaScriptと同様です。

数値

10進の小数点表記のみが使用できます。指数部を指定した1e10の様な表現は使用できません。

真偽値

JavaScriptの真偽値と同様です。

日時

リテラルはありません。

配列

JavaScriptの配列と同様です。スプレッド演算子を使用することができます。

オブジェクト

JavaScriptのオブジェクト同様ですが、キーがアルファベット以外を含むときにはダブルクオートで括る必要があります。スプレッド演算子を使用することができます。

正規表現

JavaScriptの正規表現と同様です。

nullundefined

nullundefinedをリテラルとして使用することができます。

演算子

以下の演算子が使用できます。

これらの演算子の動作および優先度は、以下の例外を除いてJavaScriptと同様です。

真偽値への変換

三項演算子の条件として使われたり、フィールドの条件付き表示に式を指定した場合など、値を真偽値に変換する場合には、以下のように行われます。

関数定義

mapfilterなどには、定義した関数を渡すことができます。

関数は、JavaScriptのアロー関数と同様の形式で定義します。例えば、map([1, 2, 3], n => n * 2)の、n => n * 2は引数の値を2倍する関数です。

定義済み関数

式の中では以下の関数を呼び出すことができます。関数を呼び出すには、floor(c3ef4f5d, 2)のように、関数名の後ろに()で括られ,で区切られた引数を指定します。

addDate

指定された日時・文字列・数値に、指定された時間を加えます。加える時間は、数値で2番目の引数として指定します。3番目の引数には、指定した時間の単位を指定します。指定できる単位は、 days, hours, minutes, seconds, milliseconds のいずれかで、それぞれ日数、時間、分、秒、ミリ秒を意味します。

日時を指定した場合、指定された時間を加え、日時を返します。単位を指定しなかった場合には、 milliseconds が指定されたものとして扱います。

数値を指定した場合、その数値を、1970年1月1日 00:00:00 (UTC)からのミリ秒として日時型に変換した上で、指定された時間を加え、1970年1月1日 00:00:00 (UTC)からのミリ秒を数値として返します。単位を指定しなかった場合には、 milliseconds が指定されたものとして扱います。

YYYY-MM-DD 形式の文字列を指定した場合、指定された日付に指定された時間を加え、 YYYY-MM-DD 形式の文字列を返します。単位を指定しなかった場合には、 days が指定されたものとして扱います。

HH:mm:ss 形式の文字列を指定した場合、指定された時間に指定された時間を加え、 HH:mm:ss 形式の文字列を返します。単位を指定しなかった場合には、 milliseconds が指定されたものとして扱います。24時間を超えた場合には、24時間で転回します。例えば、 "28:00:00" の代わりに "04:00:00" を返します。

addDate(parseDateTime("2021-12-08T10:34:45.000Z"), 10 * 60 * 60 * 1000)
// => 2021年12月8日 20:34:45 (UTC)を表す日時型の値

addDate(parseDateTime("2021-12-08T10:34:45.000Z"), 3, "days")
// => 2021年12月11日 10:34:45 (UTC)を表す日時型の値

addDate(1638959685000, 10 * 60 * 60 * 1000)
// => 1638995685000

addDate(1638959685000, 3, "days")
// => 1639218885000

addDate("2021-12-08", 3)
// => "2021-12-11"

addDate("2021-12-08", 4 * 24 * 60 * 60 * 1000, "milliseconds")
// => "2021-12-12"

addDate("10:34:45", 10 * 1000)
// => "10:34:55"

addDate("10:34:45", 15, "hours")
// => "01:34:55"

ceil

指定した精度で切り上げた値を返します。

ceil(10.5)
// => 11

ceil(10.513, 2)
// => 10.52

contains

文字列または配列・オブジェクトが指定された値を含んでいるかどうかを返します。

contains("abcdefg", "cde")
// => true

contains("abcdefg", "x")
// => false

contains([1, 2, 3], 3)
// => true

contains([1, 2, 3], [2, 3])
// => false

contains({ x: 1, y: 2 }, 2)
// => true

count

文字列の長さ、または、配列、オブジェクトの要素数を取得します。

count("abcdefg")
// => 7

count([1, 2, 3])
// => 3

count({ x: 1, y: 2 })
// => 2

encodeURIComponent

文字列をURIに埋め込むためにエンコード処理を行います。

encodeURIComponent("100% ABC")
// => "100%25%20ABC"

encodeURIComponent("こんにちは")
// => "%E3%81%93%E3%82%93%E3%81%AB%E3%81%A1%E3%81%AF"

escapeHtml

文字列をHTMLとしてエスケープ処理します。以下の変換が行われます。

escapeHtml("<A & B>")
// => "&lt;A &amp; B&gt;"

filter

配列またはオブジェクトの各値に関数を適用し、真となる値を返した値のみを含む配列またはオブジェクトを返します。

filter([1, 2, 3], n => n % 2 == 1)
// => [1, 3]

filter({ x: 1, y: "foo", z: 300 }, v => count(string(v)) == 3)
// => { y: "foo", z: 300 }

floor

指定した精度で切り捨てた値を返します。

floor(10.5)
// => 10

floor(10.513, 2)
// => 10.51

formatDateTime

指定された日時型の値をフォーマットして文字列に変換します。フォーマットを指定しない場合には、YYYY-MM-DD'T'HH:mm:ss.SSS'Z'でフォーマットされます。使用できるフォーマットは、Formatを参照してください。

フォーマットを指定した場合、タイムゾーンをAsia/Tokyoのように指定することができます。タイムゾーンが指定されない場合、実行されている環境のタイムゾーンでフォーマットされます(サーバー上で実行された場合にはアプリケーションのタイムゾーン、アプリ内で実行された場合にはデバイスのタイムゾーンの指定を使用します)。

formatDateTime(parseDateTime("2018-06-30T12:30:00.000Z"))
// => "2018-06-30T12:30:00.000Z"

formatDateTime(parseDateTime("2018-06-30T12:30:00.000Z"), "YYYY/MM/DD HH:mm:ss")
// => "2018/06/30 12:30:00"

formatDateTime(parseDateTime("2018-06-30T12:30:00.000Z"), "YYYY/MM/DD HH:mm:ss", "Asia/Tokyo")
// => "2018/06/30 21:30:00"

formatNumber

指定された数値型の値をフォーマットして文字列に変換します。

二番目の引数として、以下のオプションの組み合わせを指定することができます。

指定可能な値の範囲などの詳細は、Intl.NumberFormatを参照してください。

formatNumber(1234.56)
// => "1,234.56"

formatNumber(1234.56, { useGrouping: false })
// => "1234.56"

formatNumber(1234.56, { minimumIntegerDigits: 5 })
// => "01,234.56"

formatNumber(1234.56, { minimumFractionDigits: 3 })
// => "1,234.560"

formatNumber(1234.56, { maximumFractionDigits: 1 })
// => "1,234.5"

formatTime

数値、または文字列で指定された時間を、指定されたフォーマットで文字列に変換します。時間は、1日の初めからの経過時間をミリ秒単位の数値で、もしくは、HH:mm:ss形式の文字列で指定します。使用できるフォーマットは、Formatを参照してください。

formatTime((9 * 60 + 30) * 60 * 1000, "HH:mm:ss")
// => "09:30:00"

formatTime("08:34:00", "H:mm")
// => "8:34"

formatTime("13:30:00", "Ah時mm分")
// => "午後1時30分"

map

配列またはオブジェクトの各値に関数を適用し、配列またはオブジェクトを返します。

map([1, 2, 3], n => n * 2)
// => [2, 4, 6]

map({ x: 10, y: "foo" }, v => string(v))
// => { x: "10", y: "foo" }

match

指定された文字列を正規表現にマッチさせて結果を返します。結果は、文字列の配列で返されます。最初の要素は、正規表現全体にマッチした文字列が、それ以降の要素には、正規表現中のグループにマッチした文字列が入ります。ただし、正規表現にgフラグが指定されている場合には、正規表現全体にマッチした文字列が配列として返されます。マッチしない場合には、nullが返されます。

match("ABC", /B/)
// => ["B"]

match("ABC", /(.)B/)
// => ["AB", "A"]

match("ABCBA", /(.)B/g)
// => ["AB", "CB"]

match("ABC", /X/g)
// => null

number

引数を数値に変換します。

number("10.5")
// => 10.5

number(true)
// => 1

number(parseDateTime("2018-06-30T12:30:00.000Z"))
// => 1530361800000

number("abc")
// => NaN

parseDate

文字列を渡すと、YYYY-MM-DD形式でフォーマットされた文字列を、指定されたタイムゾーンで日時型に変換します。タイムゾーンが指定されない場合、実行されている環境のタイムゾーンで変換されます(サーバー上で実行された場合にはアプリケーションのタイムゾーン、アプリ内で実行された場合にはデバイスのタイムゾーンの指定を使用します)。

parseDate("2018-06-30")
// => 2018年6月30日 00:00:00 (UTC)を表す日時型の値

parseDate("2018-06-30", "Asia/Tokyo")
// => 2018年6月29日 15:00:00 (UTC)を表す日時方の値

parseDate("2018/06/30")
// => undefined

parseDateTime

文字列を渡すと、YYYY-MM-DD'T'HH:mm:ss.SSS'Z'形式でフォーマットされた文字列を日時型に変換します。数値を渡すと、1970年1月1日 00:00:00 (UTC)からのミリ秒として日時型に変換します。

parseDateTime("2018-06-30T12:30:00.000Z")
// => 2018年6月30日 12:30:00 (UTC)を表す日時型の値

parseDateTime(1530361800000)
// => 2018年6月30日 12:30:00 (UTC)を表す日時型の値

parseDateTime("2018/06/30 12:30:00")
// => undefined

parseTime

文字列を渡すと、HH:mm:ss形式でフォーマットされた時間をミリ秒単位で数値に変換します。

parseTime("09:00:00")
// => 32400000

parseTime("9:00:00")
// => undefined

quote

引数を""で括ります。"\\でエスケープされます。引数が文字列以外の場合には、文字列に変換されます。

quote("abc")
// => "\"abc\""

quote("a\"b\\c")
// => "\"a\\\"b\\\\c\""

quote(10.5)
// => "10.5"

quote(true)
// => "true"

quote([1, 2, 3])
// => "1,2,3"

quote({ x: 1 })
// => "[object Object]"

reduce

配列またはオブジェクトの各値に関数を適用して畳み込みます。

reduce([1, 2, 3], (a, n) => a + n, 0)
// => 6

reduce({ x: 10, y: "foo" }, (a, v) => a + v, "")
// => "10foo"

replace

指定された文字列の中の、指定された正規表現にマッチした部分を、指定された文字列で置き換えます。正規表現にマッチしない場合には、元の文字列が返されます。

正規表現にgフラグが指定されていない場合には、最初にマッチした文字列が置き換えられます。gフラグが指定されている場合には、マッチした全ての文字列が置き換えられます。

replace("ABC", /B/, "XYZ")
// => "AXYZC"

replace("ABC", /X/, "PQR")
// => "ABC"

replace("ABC", /^(.)/, "$1X")
// => "AXBC"

replace("A B C", / /g, "")
// => "ABC"

round

指定した精度で四捨五入した値を返します。

round(10.5)
// => 11

round(10.513, 2)
// => 10.51

sort

配列をソートします。配列以外を渡すと、undefinedを返します。

比較関数を渡さなかった場合、配列の全ての要素が数値の場合には数値として、それ以外の場合には要素を文字列に変換した上で大小が比較され、ソートされます。

比較関数を渡す場合には、比較関数に渡された一番目の引数の値が二番目の引数よりも前に位置する場合には負の値を、一番目の引数の値が二番目の引数よりも後ろに位置する場合には正の値を、元の順序を保持する場合には0を返すようにします。

sort([51, 9, 10, 9])
// => [9, 9, 10, 51]

sort(["51", "9", "10", "9"])
// => ["10", "51", "9", "9"]

sort(["abc", "d", "ef", "ghi"], (lhs, rhs) => count(lhs) - count(rhs))
// => ["d", "ef", "abc", "ghi"]

split

指定された文字列を、指定された文字列または正規表現にマッチした文字列で分割し、文字列の配列を返します。

split("A,B", ",")
// => ["A", "B"]

split("AB", ",")
// => ["AB"]

split(",A,B,", ",")
// => ["", "A", "B", ""]

split("A1B2C3", /[A-Z]/)
// => ["", "1", "2", "3"]

split("1a2b3", /([a-z])/)
// => ["1", "a", "2", "b", "3"]

string

引数を文字列に変換します。

string("abc")
// => "abc"

string(10.5)
// => "10.5"

string(true)
// => "true"

string([1, 2, 3])
// => "1,2,3"

string({ x: 1 })
// => "[object Object]"

substring

文字列から、指定された位置、長さの文字列を取得します。位置や長さは文字単位で指定します。

位置は、0ベースのインデックスで、0以上の整数を指定する事ができます。整数以外や負の整数を指定するとundefinedを、文字列の長さより大きい値を指定すると、空文字列を返します。

長さは、0以上の整数を指定する事ができます。整数以外や負の整数を指定するとundefinedを返します。位置と長さの合計が文字列の長さより大きい場合には、文字列の最後までを返します。

substring("abc123", 0, 2)
// => "ab"

substring("abc123", 2, 10)
// => "c123"

substring("abc123", 10, 2)
// => ""

sum

配列内の数値の合計を計算します。

sum([1, 2, 3, 4, 5])
// => 15

toLowerCase

文字列を小文字に変換します。

toLowerCase("ABCabc123")
// => "abcabc123"
toLowerCase("ABCabc123")
// => "abcabc123"

toUpperCase

文字列を大文字に変換します。

toUpperCase("ABCabc123")
// => "ABCABC123"
toUpperCase("ABCabc123")
// => "ABCABC123"

JavaScriptとの違い

Platio式は、JavaScriptと似ていますが、一部異なる部分があります。また、JavaScriptの全ての機能が使えるわけではありません。ここでは、JavaScriptと異なる部分のいくつかを挙げます。ここで挙げた以外にも違いはありますので注意してください。

Platio式は、式のみが使用でき、文は使用できません。例えば、10 + 5; "test"はPlatio式では不正な式になります。また、文を必要とする、for, if, whileなどの制御構文は使用できません。

文字列リテラル

Platio式では、文字列のリテラルは、ダブルクオートで(")で括ります。シングルクオート(')は使用できません。例えば、"test"は正しい式ですが、'test'は不正な式です。

オブジェクトや配列の要素の参照

オブジェクトや配列の要素を.で区切った名前で参照できるのは、引数を参照する場合のみです。例えば、recordが引数の時に、record.values.c7faa419.valueは正しい式ですが、{ key: "value" }.keyは不正な式です。引数以外のオブジェクトの値を参照するには、{ key: "value" }["key"]の様にキーを[]内に指定します。

undefinednullのプロパティの参照

JavaScriptでは、undefinednullのプロパティを参照しようとするとエラーになりますが、Platio式ではundefinedになります。例えば、cb3bbfb0というカラムIDのフィールドに値が設定されていない時、record.values.cb3bbfb0.valueundefinedになります。

等号の意味

Platio式の==!=は厳密な比較です。JavaScriptの===!==にあたります。

メソッド呼び出し

Platio式では、オブジェクトのメソッドを呼び出すことはできません。例えば、date.getTime()は不正な式です。

関数の定義

Platio式では、関数は全てアロー関数の構文を使用して定義します。例えば、map([1, 2], n => n * 2)は正しい式ですが、map([1, 2], function(n) { return n * 2 })は不正な式です。

NaNInfinite

Platio式を評価した結果がNaN, Infiniteまたは-Infiniteになった場合、undefinedに変換されます。