FileMaker ソートボタン

FileMakerでファイルを作るときに使い回す定番の小ネタ・小技があります。今回はソートボタンのスクリプト。

FileMakerはときどき「えっ。なんでこれができないの?」っていう機能の欠落を感じることがあります。ソートの関数が用意されていないこともその一つ。

計算式でソートを制御することができません。ソート関数はありませんし、ソート項目を名前で指定できるスクリプトステップもありません。ソートはすべてソートダイアログで個別具体的に指示し保存するしかありません。これが不自由でみんな困ってます。多分困ってますよね?困ってませんか?そうですか。

何がしたいかというとリストのタイトル部分をクリックしたらソートされるようにしたいだけです。大概のリスト表示があるプログラムでよく見かけます。クリックでソートするだけでなくトグルで昇順降順が入れ替わります。

わざわざムービーにする必要もないけど。

 

ポイントは二点あります。

  1. ソートを実行するスクリプト
  2. ボタンクリックごとに昇順・降順を切り替える仕組み

ということでソートのスクリプトについてです。

その1は汎用スクリプト1個 + 個別にソートのスクリプトを作りまくる方法、
その2はスクリプト一個(か2個)のみで済む代わりにフィールドを2個追加する二つの方法を記しました。

その1 個別にソートスクリプトを作りまくる方法

その1は、汎用的なソートスクリプトを一個作り、さらに個別ソートのスクリプトを作りまくる方法です。以前この方法を好んで使っていましたが今は使わなくなりました。これをすっ飛ばして その2 に行くも良しです。

動作の流れ

流れというか仕組みというか、必要な手続きは以下です。

  • 汎用的なソートスクリプトを一個作成します。
  • 個別に、ソートのスクリプトを昇順降順セットでガシガシ作成します。
  • 個別ソートのスクリプトの命名にルールを設け、汎用スクリプトが判断する仕組みです。
  • レイアウト上でボタンを作って割り当てます。ボタンにスクリプト引数を与えます。

個別のソートスクリプト

個別にソートスクリプトを昇順降順2個セットで作ります。中身はコピペの書き換えで良いのですが、スクリプトの名前にルールがあります。ルールそのものが重要ってわけではなく、ルールに厳格なことが重要ってだけです。

個別ソートスクリプトの命名のルール

個別にせこせこと作るソートスクリプトです。スクリプト名の付け方に決まりを設けます。

ラベル+セパレータ+ソート順

ここではこうします。

ラベル

ソートスクリプトを作るときに付けたくなる普通の名前です。これが「ラベル」部分です。ラベルにルールはありません。ただし、次の「セパレータ」を含むことだけは避けます。

セパレータ

セパレータなしでもいいんですがここではセパレータがあるということで話を進めます。セパレータは「 – 」「 : 」「 _ 」「 | 」などです。ラベルに含まれる可能性がある文字は避けます。何にせよ、セパレータを決めてそれを守り抜きます。

ソート順

個別ソートスクリプトは二個セットで作ります。昇順と降順の二つです。命名の最後はソート順がどっちなのかを示す言葉をくっつけます。昇順、降順でもいいしASC、DESCでも↑↓でもいいでしょう。それを決めて、必ず付けます。スクリプト作りまくりで鬱陶しいですが一個作ればコピペ改編で済むのでさほどの手間ではないです。

てなわけで個別スクリプトの命名ルールでした。例えば こんな感じです。

スクリプト名

ボタンにスクリプト引数

レイアウト上でボタンにソートスクリプトを割り当てるとき、スクリプト引数をセットします。そこには「ラベル」を書書きます。上図のIDなら「ID」、ファイル名なら「ファイル名」です。スクリプト名の、セパレータより前の部分をセットします。

ボタン設定 スクリプト引数
スクリプト引数に「ラベル」を書く

個別ソートスクリプト作成のルール

スクリプトの中身はスクリプトステップが2行。簡単です。

1行目はスクリプト名に相応しいソートを「レコードのソート」スクリプトステップのダイアログで作って保存します。

2行目はグローバル変数をセットします。ここでは「$$sort」という名前にしておきましょう。$$sort に値をセットしますが、書き方にまたしてもルールがあります。ルールというか、これは決め打ちなので個別に改編もせずこのまま全部同じです。

変数を設定 [ $$sort = Get ( スクリプト名 ) ] です。ソートしたらスクリプト名がそのまま入るだけです。スクリプト名とは?「ラベル – ソート順」ですね。そのままグローバル変数になって、現在のソート状況が保存されるという案配です。

個別ソートスクリプト

「ファイル名-ASC」という名前のスクリプトを実行させると、ファイル名で昇順ソートして、グローバル変数$$sortに「ファイル名-ASC」と記録します。これが個別ソートスクリプトの全貌です。

※ 後に、グローバル変数とスクリプト内の if を利用して2個セットのスクリプトを1個にすることができました。こんな記事を鵜呑みにせず、各々、知恵と工夫で良い正解を見つけてくださいませ。

ソートボタンスクリプト

さて肝心のソートスクリプト行きます。ソートボタンスクリプトはすべてのソートボタンにセットする汎用的なソートスクリプトです。このスクリプトに書くステップを順に書きます。

まず変数を設定します。

変数を設定 [ $sep ; 値: "-" ]

命名ルールで定めたセパレータを変数に保存しました。念のための措置。

変数を設定 [ $label ; Get(スクリプト引数) ]

次にスクリプト引数を取得しました。ルール上、それは必ずラベルになっているはずですね。

次は条件分岐です。グローバル変数 $$sort の値の中に、ラベルがあるかどうかを調べて処理を分けます。

if ( PatternCount ( $$sort ; $label ) )
  #あるとき〜
else
  #ないとき〜
end if

$$sort に自分と同じラベルが含まれていれば、次はソート順を見ます。

あるとき〜

$$sortには必ず ASC か DESC が入っているので、ASCなら逆であるDESCスクリプトを、DESC なら逆であるASCスクリプトを発動させます。

if [ patternCount ( $$sort ; "ASC" ) ]
   スクリプト実行 [名前で ; $label & $sep & "DESC" ]
else
   スクリプト実行 [名前で ; $label & $sep & "ASC" ]
end if

個別ソートのスクリプトを名前で実行することで、全てのソートボタン対応の汎用性を手に入れています。

ないとき〜

$$sort に自分と同じラベルがないときは未ソートであるからして、ASCかDESCのどちらか好きなほうをデフォルトとして発動させます。

ということで汎用ソートスクリプトは以上です。一応まとめときますか。

if ( PatternCount ( $$sort ; $label ) )
   #あるとき〜
   if [ patternCount ( $$sort ; "ASC" ) ]
      スクリプト実行 [名前で ; $label & $sep & "DESC" ]
   else
      スクリプト実行 [名前で ; $label & $sep & "ASC" ]
   end if 
else
   #ないとき〜
   スクリプト実行 [名前で ; $label & $sep & "ASC" ]
end if

たったこれだけのシンプルな話でした。

レイアウトでボタンを使う

ソートボタンスクリプトは書いた。はい。個別ソートのスクリプトも作った。はい。あとはレイアウトで実装するだけ。ボタンにソートボタンスクリプトの実行をセットして引数を書きます。

リスト的な表示(リストやポータル)のヘッダにタイトルのラベルを置きたくなります。このラベルをボタンにするんです。

ボタンバー

ボタンバーを使うと計算でボタンラベルを変更できるから昇順降順を示せます($$sortに応じてテキストを変える)

図は↑ですが下の説明では▲になってますね。すいません💦

ボタンラベルの計算式はこんな感じです。

Let ( [
 ラベル = "作成日" ;
 label = If ( PatternCount ( $$sort ; "ASC" ) and PatternCount ( $$sort ; ラベル ) ; ラベル & " ▲" ;
 If ( PatternCount ( $$sort ; "DESC" ) and PatternCount ( $$sort ; ラベル ) ; ラベル & " ▼"; ラベル & " " ) )
 ]; label )

最初の「ラベル = “”」にボタンスクリプトの Label を指定するだけです。

表示上の項目名とラベル(スクリプト名のラベル)を分けたいときは、表示用ラベルを追加しておくとより良いですね。その場合こうなりますか。ラベルと表示ラベルだけ、ボタンごとに書き換えます。

Let ( [
 ラベル = "作成日" ;
 表示ラベル = "日付" ;
 label = If ( PatternCount ( $$sort ; "ASC" ) and PatternCount ( $$sort ; ラベル ) ; 表示ラベル & " ▲" ;
 If ( PatternCount ( $$sort ; "DESC" ) and PatternCount ( $$sort ; ラベル ) ; ラベル & " ▼"; 表示ラベル & " " ) )
 ]; label )

その2 専用フィールドを作成する方法

その2のやり方は、個別ソートのスクリプトを作りまくったりしません。スクリプトは1個または2個で済みます。こっちのやりかたはファイルメーカーのヘルプにも載っているやり方で、専用フィールドを作成して利用する方法です。

個別ソートのスクリプトを作らなくても良い代わりに、ソート専用のフィールドを作っておく必要があります。ヘルプに載ってるまともで簡素なやり方ですが、そのままではちょっと実用に耐えないので、少し工夫が必要な部分もあります。

材料

グローバルフィールドを一つ、計算フィールドを一つ追加します。
例としてグローバルフィールドを「GLBfieldName」、計算フィールドを「sortContent」と名付けてみます。

あとはソートのスクリプトが一つあれば良いです。実際の運用では、このソートスクリプトを発動させるための「ソートボタンのスクリプト」をもう一つ追加すれば良いでしょう。

理屈

グローバルフィールド GLBfieldName は、ソートする「フィールド名」を記入する入れ物です。ここにフィールド名を入れることで「おいお前、このフィールド名の内容でソートするんだぞ」と命じることになります。

計算フィールド sortContent は「あいよ、おまいさん。こうかい?」と、グローバルフィールドで指定したフィールド名の「フィールド内容」をそっくりそのまま転記してクローンと化します。

例えばグローバルフィールドに「氏名フィールド」と書いたら、計算フィールドは実際の「氏名フィールド」の内容をレコード分転記して「氏名フォールド」と同じになります。

スクリプトは、常に計算フィールド sortContent でソートするように仕込みます。個別にソートスクリプトなど作る必要ありません。sortContentのソート指定一個ですみます。sortContent の中身がその都度変わるのだという仕組みです。

計算フィールドの計算式

理屈の通り、sortContent フィールドにフィールド内容を計算して転記するわけですが、基本は FileMaker のヘルプに載っているとおり、GetField ( GLBfieldName ) の一行でOKです。

この簡単な1行でフィールド内容は転記されますが、このままではソートに問題があって駄目ですので改良します。どういう問題か、どういう改良か、それは以下です。

結果のタイプ

計算式では、計算結果のタイプを「テキスト」や「数字」など事前に指定しなければなりません。

でも実際にソートしたいフィールドの内容はテキストだったり数字だったりします。

計算フィールドの計算式の結果を「テキスト」にすると、数字のソートが上手くいきません。1 から 10 の数字だとすれば、1の次に10が来てその次に 2 が来てしまいます。 結果を 「数字」にすると、当然ながらテキストが返りません。

FieldType

取りあえず計算結果は「テキスト」にしておきます。その上で、ソートフィールドが数字や日付の場合に備えましょう。

備えるのは良いのですが、その前にソートする指定フィールドのタイプをまず調査しなければなりません。大丈夫。FieldType という関数があります。

FieldType ( ファイル名 ; フィールド名)

これを使うと指定フィールドの情報がスペース区切りのテキストで返ってきます。スペースを¶に置換して GetValue すると2行目がフィールドタイプです。ですので具体的に次のように書きます。

GetValue ( Substitute ( FieldType ( get(ファイル名) ; GLBfieldName ) ; " " ; ¶ ) ; 2 )

結果は「text」「Number」「Date」「Timestamp」「Container」といった文字列になります(Container はオブジェクト)

タイプが Number だったら、無理矢理テキストとしてソートできる形にします。つまり 0 を追加して桁数を増やします。

Right ( "0000000" & フィールド内容 ; 8 )

例えばこんな風に8桁にしてしまえば、テキストフィールドとしてソートしても期待通りの結果となるでしょう。桁数はご自由に。

日付フィールドの場合は、各々お好きに工夫します。私の場合は書式を揃えているのでそのままテキストとしてソートしても問題ありません。秒でズレますが細けえことは気にしません。気にする場合は、タイムスタンプでもテキストとして桁数が揃うように整形します。

計算フィールドはその都度本物フィールドから転記してくるだけのクローンフィールドですから、ソートしやすいように整形しまくっても大丈夫、本物データに何の影響もありません。

Let 関数

ということで、sortContent フィールドの計算式は具体的こうなりました。太字のGLBfieldName はグローバルフィールドです。

Let ( [
 content = GetField ( GLBfieldName ) ;
 fieldtype = GetValue ( Substitute ( FieldType ( get(ファイル名) ; GLBfieldName ) ; " " ; ¶ ) ; 2 ) ;
 content =
   case ( 
     fieldtype = "number" ; Right ( "0000000" & content ; 8 ) ; 
     content
   )
 ]; content )

GLBfieldName にフィールド名を記入することで、sortContent にはソートできる形でフィールド内容が転記されます。あとはスクリプトでソートするだけです。

ソートするスクリプト

ソートスクリプトは、計算フィールド sortContent フィールドをソートさせれば良いだけです。

ダイアログでsortContent フィールドを指定します。

しかしこれだけでは貧弱ですね。状態把握のためのグローバル変数の作成をを交えつつ、次のようなスクリプトを作成しました。

ソートのスクリプト

ソートスクリプトの動き

まず、$$sort を読んで一行目のフィールド名がグローバルフィールドに書かれた指定フィールドと同じかどうか調べます。つまり現在のソートと、今からソートしようとしているソートが同じフィールドなのか、違うのかを最初に見ます。

同じなら、現在昇順なのか降順なのかを調べ、現在と逆の行動を起こします。現在が昇順ならば降順、降順ならば昇順でソートします。

ソートを実行するとグローバル変数 $$sort を更新し「フィールド名 ¶ ソート順」を記録します。

$$sort のフィールドと今からソートしようとしてるフィールドが別であるなら、新たに指定フィールドでソートすることになります。デフォルトのソートです。デフォルトを昇順・降順、どちらにセットしておくかはお好み次第です。この例ではデフォルトを降順としています。

そしてソートしたあとは $$sort を更新、「フィールド名 ¶ ソート順」 を記録します。

ソートボタンのスクリプト

GLBfieldName にフィールド名を記入すればそのフィールドでソートされるようになりました。
実際の運用ではフィールド名を手入力しません。してもいいけど。普通しません。大抵はボタンに仕込みたいです。GLBfieldName にフィールド名を記入し、ソートスクリプトを実行するスクリプトを作りましょう。

ソートボタンに割り当てるスクリプトを追加しました。

ソートボタンのスクリプト

ソートボタンにセットするスクリプト引数

ボタンの設定で、スクリプト引数にフィールド名をセットします。フィールド名は ” ” で囲みます。ここで書いたスクリプト引数が、グローバルフィールドにそのまま入る仕組みです。”” で囲まないと、そのフィールド名のフィールド内容が書かれてしまいます。フィールド名としてセットしたいので必ず括ります。あとはソートスクリプトを実行させ、最後にレコードの先頭に移動させています。

ソートするスクリプトとボタンにセットするスクリプトをこのように分けることで、他の処理と併用しても面倒なことになりませんのでおすすめです。

普通のボタンじゃなくボタンバーを使うことによってボタンタイトルを計算式で作れます。これは半ば必須かもしれません。ソート順を表示できますから。

ボタンバーのラベル

ボタンバーでは計算式でラベルを作れますので重宝します。例として、次のようなラベルの計算式を挙げておきます。

Let ( [
 fieldName = "ファイル名" ;
 label = "ファイル名" ;

 asc = TextSize ( TextFont ( " ▵" ; "Apple Symbols" ) ; 11 ) ;
 desc = TextSize ( TextFont ( " ▿" ; "Apple Symbols" ) ; 11 ) ;
 none = TextSize ( TextFont ( "  " ; "Apple Symbols" ) ; 11 );

 GLBfld = GetValue ( Substitute ( $$sort ; ": " ; ¶ ) ; 1 ) ;
 GLBsort = GetValue ( Substitute ( $$sort ; ": " ; ¶ ) ; 2 ) ;

 sort = Case ( 
GLBfld ≠ fieldName ; none;
GLBsort = "ASC" ; asc ;
GLBsort = "DESC" ; desc
 );

 disp = If ( GLBfld = fieldName ; label & sort ; label & none )

]; disp )

最初の fieldName は、ボタンがターゲットとするフィールド名、スクリプト引数と同じです。
次の label は、表示させるテキスト。
asc, desc, none はテキストの後ろに付ける矢印や三角。この例では小さな三角を指定しています。小さな三角はフォントやサイズの指定をしないと表示サイズが定まらなかったので指定しています。
あとは 現在の $$sort(1行明がフィールド名、2行目が昇順・降順 )を読んで、case で表示を決定しています。

 

 

Penguin icon

ということで、別の方法を追記しました。その2の方法が簡単で良いかもしれないですね。このためだけにフィールドを二つ追加することを許容できるかどうか、それは各々のご判断。

大事なテーブルにこのようなフレキシブルな計算フィールドを追加することで、データの中身が始終ごちゃごちゃ変更され入り乱れて安定しないという弱点はあります。日々の変更が少ない重要なテーブルでは気になるかもしれません。あえて本体テーブルではない別テーブルや別ファイルをリレーションしてそっちで上手く運用するのも一つの手かもしれません。

 

コメント

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください