Archive for the 'リスト処理(list)' Category

04/22 入れ子のリストから、指定フィールドでソートしてデータを返す

{{”Field1″, “Field2″, “Field3″}, {1,2,3}, {4,5,6}}のような入れ子のリストで、指定フィールド名のデータをキーとしてソートして返すAppleScriptです。

CSVファイルをparseしてリストに変換し、リストに対して指定フィールドの値でソートする場合に使用します。

返り値にはフィールドラベルを含んでいません。ソートルーチンは、中途半端な高速化を行ってあります。最速版のソートルーチンを入れ子のリストに対応させられていなかったので、shell sortの入れ子ルーチンをちょっとだけ高速化した状態です。

スクリプト名:入れ子のリストから、指定フィールドでソートしてデータを返す
set aList to {{“ID”, “名称”, “アドレス”, “URL”, “データ”}, {1, 2, 3, 4, 5}, {9, 3, 4, 5, 6}, {3, 4, 5, 6, 7}}

set sortDirecionF to false –true: ascending, false: descending

set resList to getSpecifiedFieldData(aList, “ID”, sortDirecionF) of me
–> {{9, 3, 4, 5, 6}, {3, 4, 5, 6, 7}, {1, 2, 3, 4, 5}}

–入れ子のリストから、指定フィールドのデータを取得する
–1アイテム目はフィールドラベル。2アイテム目以降をデータと見なして指定フィールドのデータをリストで返す
on getSpecifiedFieldData(aData, fieldName, sortAscendF)
  –与えられたデータaDataの1アイテム目はフィールド名と見なす
  
set fieldList to contents of first item of aData
  
set dataList to contents of items 2 thru -1 of aData
  
  
set fNum to getNumberOfField(fieldList, fieldName) of me
  
if fNum = 0 then return false
  
  
if sortAscendF = true then
    –Ascending
    
set resList to shellSortListAscending(dataList, fNum) of me
  else
    –Descending
    
set resList to shellSortListDecending(dataList, fNum) of me
  end if
  
  
–フィールドラベルを先頭に付けておく???
  
–set beginning of dataList to fieldList
  
  
return resList
  
end getSpecifiedFieldData

–与えられたリスト中における任意テキスト要素の出現アイテム番号
on getNumberOfField(aList, targFieldName)
  set aCount to 1
  
repeat with i in aList
    set j to contents of i
    
if j is equal to targFieldName then
      return aCount
    end if
    
    
set aCount to aCount + 1
  end repeat
  
  
return 0 – no hit
end getNumberOfField

–シェルソートで入れ子のリストを昇順ソート
on shellSortListAscending(aSortList, keyItem)
  script oBj
    property list : aSortList
  end script
  
  
set n to count oBj’s list’s items
  
set cols to {1391376, 463792, 198768, 86961, 33936, 13776, 4592, 1968, 861, 336, 112, 48, 21, 7, 3, 1}
  
  
repeat with h in cols
    if (h (n - 1)) then
      repeat with i from h to (n - 1)
        set v to item (i + 1) of list of oBj
        
set j to i
        
repeat while (j h) and ((contents of item keyItem of item (j - h + 1) of list of oBj) > (item keyItem of v))
          set (item (j + 1) of list of oBj) to (item (j - h + 1) of list of oBj)
          
set j to j - h
        end repeat
        
set item (j + 1) of list of oBj to v
      end repeat
    end if
  end repeat
  
  
return aSortList
end shellSortListAscending

–シェルソートで入れ子のリストを降順ソート
on shellSortListDecending(aSortList, keyItem)
  script oBj
    property list : aSortList
  end script
  
  
set n to count oBj’s list’s items
  
set cols to {1391376, 463792, 198768, 86961, 33936, 13776, 4592, 1968, 861, 336, 112, 48, 21, 7, 3, 1}
  
  
repeat with h in cols
    if (h (n - 1)) then
      repeat with i from h to (n - 1)
        set v to item (i + 1) of list of oBj
        
set j to i
        
repeat while (j h) and ((contents of item keyItem of item (j - h + 1) of list of oBj) < (item keyItem of v))
          set (item (j + 1) of list of oBj) to (item (j - h + 1) of list of oBj)
          
set j to j - h
        end repeat
        
set item (j + 1) of list of oBj to v
      end repeat
    end if
  end repeat
  
  
return aSortList
end shellSortListDecending

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

04/22 入れ子のリストから、指定フィールドのデータを取得する

{{”Field1″, “Field2″, “Field3″}, {1,2,3}, {4,5,6}}のような入れ子のリストで、指定フィールド名のデータを取得して返すAppleScriptです。

CSVファイルをparseしてリストに変換し、リストに対して指定フィールドの値を抽出する場合に使用します。

exceltable1.png

オリジナルのデータは、Excel(あるいはNumbersやFileMaker Pro)上で、上図のような状態になっていることを想定しています。1行目がフィールド名ラベルになっているというパターンです(緑色の箇所)。

入れ子のリストの各指定項目を取り出すような場合には、「何アイテム目を取り出す」と記述するケースが多いところですが、アイテム番号を決め打ちでは処理の柔軟性がないので、1アイテム目をフィールドラベルと見なし、そのフィールドラベルで該当する項目番号をサーチして、項目名で指定できるようにしてみました。

AppleScript中心の処理を追求すると、なるべく「テキストエディタやFileMaker Proに依存した処理を行わない」ことが重要になってきます。置換や検索、ソートなどをこれらのアプリケーションに依存していると、いつまでもそのアプリケーションが存在しない環境では自分の処理ができないことになります。

スクリプト名:入れ子のリストから、指定フィールドのデータを取得する
set aList to {{"ID", "名称", "アドレス", "URL", "データ"}, {1, 2, 3, 4, 5}, {2, 3, 4, 5, 6}, {3, 4, 5, 6, 7}}

set resList to getSpecifiedFieldData(aList, "名称") of me
–> {2, 3, 4}

–入れ子のリストから、指定フィールドのデータを取得する
–1アイテム目はフィールドラベル。2アイテム目以降をデータと見なして指定フィールドのデータをリストで返す
on getSpecifiedFieldData(aData, fieldName)
  –与えられたデータaDataの1アイテム目はフィールド名と見なす
  
set fieldList to contents of first item of aData
  
set dataList to contents of items 2 thru -1 of aData
  
  
set fNum to getNumberOfField(fieldList, fieldName) of me
  
if fNum = 0 then return false
  
  
set resList to {}
  
repeat with i in dataList
    set the end of resList to contents of item fNum of i
  end repeat
  
  
return resList
  
end getSpecifiedFieldData

–与えられたリスト中における任意テキスト要素の出現アイテム番号
on getNumberOfField(aList, targFieldName)
  set aCount to 1
  
repeat with i in aList
    set j to contents of i
    
if j is equal to targFieldName then
      return aCount
    end if
    
    
set aCount to aCount + 1
  end repeat
  
  
return 0 – no hit
end getNumberOfField

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

04/22 入れ子のリストで、1アイテム目のアイテム数が他と同じかどうかチェック

{{1,2,3}, {4,5,6}}のような入れ子のリストで、全アイテムのアイテム数が同じかどうかチェックするAppleScriptです。基準となるアイテム数は1アイテム目({1,2,3})を用います。

CSVファイルをparseしてリストに変換し、リストに対して処理を行ってふたたびCSVファイルに書き出すような場合に使用します。合っているとtureを、そうでないとfalseを返します。たいして大きなデータを処理していなかったので、高速化処理などは一切行っていませんが……データの規模によっては高速化処理を行うことがふさわしいところです。

parseした結果としてすべてのアイテムでアイテム数が同じかどうかをチェックすることが目的です。

exceltable.png

オリジナルのデータは、Excel(あるいはNumbersやFileMaker Pro)上で、上図のような状態になっていることを想定しています。1行目がフィールド名ラベルになっているというパターンです(緑色の箇所)。

CSVからparseしたリストの項目数が合っていないと、そのあとでさまざまな処理を行おうにも……処理の内容を保証できません。そこで、まずは項目数が合っているかを検証すると安全でしょう。もちろん、フィールド付加などを行ったあとに項目数をカウントしてチェックを行うことも重要です。

ExcelなどからCSVファイルに書き出して、AppleScript側でCSVファイルをもとにデータ処理を行うケースは非常に多いです。AppleScriptの初心者が陥りがちなポイントですが……そのままExcelに対して必要なデータに(行や列を指定して)アクセスすると、当然のことながらデータ数が増えるとものすごく時間がかかります。selectionからデータを取得する例もありますが、selectionから取得できるデータの大きさに上限が存在していたりで、万能とはいえません(とくに、Mac OS X初期に開発されたExcel v.Xあたりはselectionで取得できるデータサイズの上限が小さめです)。

大量のデータ通信をアプリケーションに対して行って、それをもって「AppleScriptの処理が遅い」と判断するのは間違いです。AppleScriptの内部データ表現形式であるリスト型変数やレコードに取り込んで、その上で処理を行うのがセオリーです。

同様の例はテキストエディタのデータを処理する場合などに多々見られ……テキストエディタの編集中のファイルに対して、テキストエディタ経由でアクセスすれば当然のように遅くなります。しかし、「どのファイルを編集中なのか」という情報をテキストエディタから取得して、AppleScriptで自前でテキストファイルにアクセスして処理すれば圧倒的に高速になります。

スクリプト名:入れ子のリストで、1アイテム目のアイテム数が他と同じかどうかチェック
set aList to {{“ID”, “名称”, “アドレス”, “URL”, “データ”}, {1, 2, 3, 4, 5}, {2, 3, 4, 5, 6}, {3, 4, 5, 6, 7}}

–リスト中のすべての項目(データ行)のフィールド数が同じかどうかチェック
set chRes to chkItemNumInEveryLine(aList) of me
–> true

–入れ子のリストで、1アイテム目のアイテム数が他と同じかどうかチェック
on chkItemNumInEveryLine(aList)
  set aList1 to contents of first item of aList
  
set aList2 to contents of items 2 thru -1 of aList
  
  
set aList1Len to length of aList1
  
repeat with i in aList2
    set tmpLen to length of i
    
if tmpLen is not equal to aList1Len then
      return false
    end if
  end repeat
  
  
return true
end chkItemNumInEveryLine

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

03/12 Find same file name with different extension in same directory

This is a simple AppleScript to find same file name with different extension in same directory.

Sample Data:
finder1.jpg

Output Data:
–> {{”D120_a.jpg”, “D120_a.png”}, {”D120_c.gif”, “D120_c.jpg”, “D120_c.png”, “D120_c.txt”}}

スクリプト名:find same file name with different extension in same directory
–Speed up technic
script aRef
  property aList : {} –every file name of user selected folder ( work)
  
property bList : {} – every file name list (without duplicates) (work)
  
property cList : {} –duplicated file name list (work)
  
property dList : {} –output duplicated file names with different extensions
end script

–Initialize variables
set aList of aRef to {}
set bList of aRef to {}
set cList of aRef to {}
set dList of aRef to {}

set a to choose folder with prompt "Choose check Folder"

tell application "Finder"
  tell folder a
    set aList of aRef to name of every file
  end tell
end tell

–make pure file name list(without extension) to cList
repeat with i in aList of aRef
  set j to contents of i
  
set jj to retFileNameWithoutExt(j) of me
  
  
if jj is in bList of aRef then
    –duplicated file name & does not exist in cList
    
if jj is not in cList of aRef then
      set the end of cList of aRef to jj
    end if
    
    
set tmpName to jj & "."
    
    
tell application "Finder"
      tell folder a
        –set dList of aRef to dList of aRef & (name of every file whose name starts with tmpName)
        
set the end of dList of aRef to (name of every file whose name starts with tmpName)
      end tell
    end tell
    
  end if
  
  
set the end of bList of aRef to jj
end repeat

–Remove duplicates from List
set aRes to removeDuplicates(dList of aRef) of me

–> {{"D120_a.jpg", "D120_a.png"}, {"D120_c.gif", "D120_c.jpg", "D120_c.png", "D120_c.txt"}}

–Remove Duplicated Item from List
on removeDuplicates(aList)
  set newList to {}
  
repeat with i from 1 to (length of aList)
    set anItem to item 1 of aList
    
set aList to rest of aList
    
if {anItem} is not in aList then set end of newList to anItem
  end repeat
  
return newList
end removeDuplicates

–delete extension from file name
on retFileNameWithoutExt(fileNameStr)
  set fLen to length of fileNameStr
  
set revText to (reverse of (characters of fileNameStr)) as string –make reversed string
  
set anOffset to offset of "." in revText
  
set fRes to text 1 thru (fLen - anOffset) of fileNameStr
  
return fRes
end retFileNameWithoutExt

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

03/03 100倍速いシェルソートでリストをソート

edama2さんよりの投稿です

基本的には掲載されている「シェルソートでリストをソート」をスプリクトオブジェクトで処理にしているだけです。サンプルでソートする数が100倍なだけで「100倍速い」という数字には特に根拠はありません。

スクリプト名:100倍速いシェルソートでリストをソート
set theList to {}
set i to 1
repeat 30000 times
  copy (random number from 100 to 99999) to the end of theList
  
set i to i + 1
end repeat

set s0Time to current date
set aRes to shellSort_order_(theList, false) of me
set s1Time to current date

display dialog (s1Time - s0Time) as string

–シェルソートでリストを昇順ソート(入れ子ではないリスト)
on shellSort_order_(a, isAsc)
  
  
script a_ref
    property contents : a
  end script
  
  
set n to length of a
  
set cols to {1391376, 463792, 198768, 86961, 33936, 13776, 4592, 1968, 861, 336, 112, 48, 21, 7, 3, 1}
  
repeat with h in cols
    if (h (n - 1)) then
      repeat with i from h to (n - 1)
        set v to item (i + 1) of contents of a_ref
        
set j to i
        
repeat while (j h) and ((item (j - h + 1) of contents of a_ref) > v)
          set (item (j + 1) of contents of a_ref) to (item (j - h + 1) of contents of a_ref)
          
set j to j - h
        end repeat
        
set item (j + 1) of contents of a_ref to v
      end repeat
    end if
  end repeat
  
  
if not isAsc then set a to a’s reverse
  
  
return a
end shellSort_order_

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

Piyomaru Softwareによる解説:

以前に掲載したシェルソートのルーチンの改良版です。ソートというか、巨大なリスト型変数(配列)の操作をAppleScriptで行うとスピードが低下するので、3,000〜4,000要素(Item)を超えるサイズのリスト型変数を扱う場合には、何らかの高速化を行うべきです。

AppleScriptにおけるソートの高速化手法にはおよそ3つあって、1つが「a reference to」による間接アクセスによる高速化。

著しく高速化するものの、データを格納する変数と間接アクセスする変数の2つをグローバル宣言する必要があり、効果は大きいものの、副作用も大きい(グローバル宣言した変数が他のルーチンの変数をぶつかっていないか確認が必要、など)ため、そのための作業コストが大きく「手軽」とはいえませんでした。

2つ目が、ここで活用されている「スクリプトオブジェクト内にプロパティを持つ」というアクセス方法で、a reference toと同程度ぐらいには(厳密に計測してはいないのですが)高速化することが知られています。

スクリプトオブジェクトの利用による高速化処理の前後で、サンプルの3万項目の乱数リストをソート所要時間を計測比較してみたところ(MacBook Pro Core i7 2.66GHz, Mac OS X 10.6.8, 8GB RAM)、

 高速化前:2967秒
 高速化後:8秒

と、370倍ぐらい高速になっていました。リストの項目が大きいほど高速化の効果が出やすいものと思われます。

3つめが、Mac OS X 10.6から使えるようになったAppleScript ObjCを用いること。この環境内でCocoaのソートルーチンを呼び出すと速いので、一度すべてのやりかたでどのソート方法が速いのか、比較をしておくべきでしょう。

追記(2012/3/4):

MacBook Air 2011 (Core i5 1.6GHz、Mac OS X 10.7.3、RAM 4GB)でこの乱数3万件のソートを試してみたところ……

 本ScriptをAppleScript Editor上で実行:7秒
 本ScriptをAppleScriptObjC on Xcodeのプロジェクトで実行:8秒
 AppleScriptObjC on XcodeのCocoaベースのソートでリスト(正確にはレコード)をScriptのオブジェクトに持たせた場合:129秒
 AppleScriptObjC on XcodeのCocoaベースのソートで何も高速化処理を行っていない場合:(計測不能)

Cocoaベースのソートがたいして速くない(純粋にリスト=NSArrayではないので、同じ処理ではないですが)ことに驚きました。AppleScriptObjCのプログラムも、ソート処理をAppleScriptネイティブのものに書き換えておいたほうがよさそうな……。

あるいは、3万件というデータ件数になると、AppleScriptObjCでもCoreDataを併用することを検討すべきなのかもしれません。データベースに入れてしまえば、3万件ごときのソートなど一瞬でしょうし。

さらに追記(2012/3/4):

AppleScriptObjCで以下のようなプログラム(単なるArrayをソート)にしてみたところ、3万件でもソートに要する時間は1秒以下でした(MacBook Pro Core i7 2.66GHz, Mac OS X 10.6.8, RAM 8GB)。

AppleScriptObjCファイル名:sortArrayAppDelegate.applescript

– sortArrayAppDelegate.applescript
– sortArray

– Created by Takaaki Naganoya on 12/03/04.
– Copyright 2012 Piyomaru Software. All rights reserved.

script aRef
  
property contents : {}
end script

script sortArrayAppDelegate
  
property parent : class “NSObject”
  
  
on applicationWillFinishLaunching_(aNotification)
    
– Nothing
  
end applicationWillFinishLaunching_
  
  
on applicationShouldTerminate_(sender)
    
return current application’s NSTerminateNow
  
end applicationShouldTerminate_
  
  
on clicked_(sender)
    
    
set aList to {}
    
repeat 30000 times
      
tell current application
        
set the end of (contents of aRef) to (random number from 1000 to 9999) as string
      
end tell
    
end repeat
    
    
tell current application
      
set sTime to current date
    
end tell
    
    
set theArray to current application’s class “NSMutableArray”’s arrayWithArray_(contents of aRef)
    
set bList to theArray’s sortedArrayUsingSelector_(“localizedCaseInsensitiveCompare:”)
    
    
tell current application
      
set eTime to current date
      
set totalTime to eTime - sTime
    
end tell
    
    
display dialog (totalTime as string)
    
  
end clicked_
  
end script

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

12/12 「YxX」の形式の文字列を、{Y,X}のように数字のリストにして返す

「YxX」の形式の文字列を、{Y,X}のように数字のリストにして返すAppleScriptです。

ダイアログから縦横の表のサイズを文字で入力させるインタフェースを作成したときに、そのparse用として作成したものです。たいしたものではありません。

スクリプト名:「YxX」の形式の文字列を、{Y,X}のように数字のリストにして返す
set aStr to "3×4"
set aRes to divideYxX(aStr) of me
–> {3, 4}

–「YxX」の形式の文字列を、{Y,X}のように数字のリストにして返す
on divideYxX(aStr)
  set curDelim to AppleScript’s text item delimiters
  
set AppleScript’s text item delimiters to "x"
  
set tList to every text item of aStr
  
set AppleScript’s text item delimiters to curDelim
  
  
copy tList to {item1, item2}
  
try
    set item1 to item1 as integer
    
set item2 to item2 as integer
  on error
    return false
  end try
  
  
return {item1, item2}
  
end divideYxX

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

12/03 リスト項目のシャッフル

リスト項目のシャッフルを行うAppleScriptです。

「リスト項目のシャッフル」という処理自体は、さほど出現頻度の高いものではありません。だいたい、私がいままで作ったことがなかったぐらいなので、この先も作る必要に迫られる可能性は低そうです。

この手の処理は、1項目呼び出すたびにランダム取り出しをするようなケースが多いので、リスト内容をすべて一括でシャッフルするような処理を書くケースは少ないでしょう。

ただ、それでも「ない」とも言い切れないので試しに組んでみました。予想行数は10行ぐらいだったのですが、予想を大幅に超え、書いているうちにトンでもない行数になってしまいました。

当初は投機的にランダムに項目を抽出して、抽出ずみリストに存在する場合には選び直し……という処理を行っていたのですが(→ 投機的処理)、3,000項目ぐらいのリストなら我慢できたものの、5,000項目を超えると遅く感じられました(MacBook Pro 2010 Core i7 2.66GHz+Mac OS X 10.6.8+日常的なアプリケーション立ち上げまくり環境にて検証)。

投機的処理がどのぐらい「無駄な選び直し」を行っているかを調べてみたところ、だいたい与えた項目数の5〜10倍ぐらい無駄な処理を行っていました。この「無駄」がなければ5〜10倍ぐらいは高速に処理できる可能性があったわけです。

そこで、いいかげんに書かないで地道に処理を行ってみたところ、投機的な処理の3倍ぐらいは高速になりました(→ 地道処理)。頻繁にリストの分割/連結を行うので、このあたりのオーバーヘッドが大きそうですが、それでもアイテム数の回数分しかループを回さないので無駄が少なくてすんでいます。

g1.jpg

t1.jpg

ちなみに、投機的処理を無理矢理「a reference to」による(なげやりな)高速化を施してみたところ、処理時間はずいぶんと改善はしたものの、それでも地道処理に及ばなかったので、地道処理を採用するのがよいのでしょう。

g2.jpg

t2.jpg

めんどくさかったので、地道処理を高速化させるパターンは試しませんでした。

一般的に、AppleScriptでリスト型変数を使って処理を行う場合には、だいたい5,000項目のデータ数がひとつの目安であり、それを超える場合には高速化処理を真剣に検討する必要があります。つまり、ズボラに組むときには、あまり大量のデータ処理を行わないことが重要、ということです。

スクリプト名:リスト項目のシャッフル(投機的処理)
–テスト用に1〜1000までの数字のリストを作成
set aList to {}
repeat with i from 1 to 10000
  set the end of aList to i
end repeat

set t1 to current date
set aRes to length of (retShuffledList(aList) of me)
set t2 to current date

return (t2 - t1)

on retShuffledList(aList)
  
  
–出力用リスト
  
set bList to {}
  
  
set aMax to length of aList
  
set curCount to 0
  
  
repeat
    –ランダム抽出および出力用リスト内の存在確認
    
set anItem to contents of some item of aList –項目のランダム抽出
    
if anItem is not in bList then
      set the end of bList to anItem
      
set curCount to curCount + 1
    end if
    
    
–終了?
    
if curCount = aMax then
      exit repeat
    end if
    
  end repeat
  
  
return bList
  
end retShuffledList

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

スクリプト名:リスト項目のシャッフル(地道処理)
–テスト用に1〜1000までの数字のリストを作成
set aList to {}
repeat with i from 1 to 10000
  set the end of aList to i
end repeat

set t1 to current date
set aRes to length of (retShuffledList(aList) of me)
set t2 to current date

return (t2 - t1)

on retShuffledList(origList)
  
  
copy origList to aList
  
  
  
–出力用リスト
  
set bList to {}
  
  
set aMax to length of aList
  
  
repeat aMax times
    –ランダム抽出および出力用リスト内の存在確認
    
set curLen to (length of aList)
    
if curLen > 1 then
      –通常処理
      
set aRandom to random number from 1 to curLen
      
set anItem to contents of item aRandom of aList
      
      
set the end of bList to anItem
      
      
set aList to delListItem(aList, aRandom) of me
    else
      –最後の項目の場合の例外処理
      
set the end of bList to contents of first item of aList
    end if
  end repeat
  
  
return bList
  
end retShuffledList

–リストから指定のアイテムを削除する
on delListItem(aList, anItemNo)
  set newList to {}
  
  
set aLen to length of aList
  
if anItemNo = 1 then
    set newList to contents of items 2 thru -1 of aList
  else if anItemNo = aLen then
    set newList to contents of items 1 thru -2 of aList
  else
    set p1List to contents of items 1 thru (anItemNo - 1) of aList
    
set p2List to contents of items (anItemNo + 1) thru -1 of aList
    
set newList to p1List & p2List
  end if
  
  
return newList
  
end delListItem

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

スクリプト名:リスト項目のシャッフル(投機的処理) +高速化
global aList, aList_r
global aaaList, aaaList_r
global bList, bList_r

–テスト用に1〜1000までの数字のリストを作成
set aList to {}
set aList_r to a reference to aList

repeat with i from 1 to 10000
  set the end of aList_r to i
end repeat

set t1 to current date
set aRes to length of (retShuffledList(aList_r) of me)
set t2 to current date

return (t2 - t1)

on retShuffledList(aaList)
  
  
copy aaList to aaaList
  
set aaaList_r to a reference to aaaList
  
  
–出力用リスト
  
set bList to {}
  
set bList_r to a reference to bList
  
  
set aMax to length of aaaList_r
  
set curCount to 0
  
  
repeat
    –ランダム抽出および出力用リスト内の存在確認
    
set anItem to contents of some item of aaaList_r –項目のランダム抽出
    
if anItem is not in bList_r then
      set the end of bList_r to anItem
      
set curCount to curCount + 1
    end if
    
    
–終了?
    
if curCount = aMax then
      exit repeat
    end if
    
  end repeat
  
  
return bList
  
end retShuffledList

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

12/03 リストから指定のアイテムを削除する(1項目)

指定リストから指定番号のアイテムを削除するAppleScriptです。

ここで言う「指定番号」とは、アイテム番号のことです。

書き捨てレベルのものなので、よく書いてしまうのですが……何らかの巨大なAppleScriptの必須部品として利用するものの、それ自体をsaveするほどでもないので本Blogに掲載していなかったという……。

スクリプト名:リストから指定のアイテムを削除する(1項目)

set aList to {1, 2, 3, 4, 5}
set bList to delListItem(aList, 4) of me
–> {1, 2, 3, 5}

–リストから指定のアイテムを削除する
on delListItem(aList, anItemNo)
  set newList to {}
  
  
set aLen to length of aList
  
if anItemNo = 1 then
    set newList to contents of items 2 thru -1 of aList
  else if anItemNo = aLen then
    set newList to contents of items 1 thru -2 of aList
  else
    set p1List to contents of items 1 thru (anItemNo - 1) of aList
    
set p2List to contents of items (anItemNo + 1) thru -1 of aList
    
set newList to p1List & p2List
  end if
  
  
return newList
  
end delListItem

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

11/28 与えられたリスト中のfalseの項目番号をリストで返す

true/falseの入ったリストを処理して、falseが入っていたアイテム番号をリストで返すAppleScriptです。

スクリプト名:与えられたリスト中のfalseの項目番号をリストで返す
set aaList to {true, true, true, false, true, false}
set aRes to retFalseItemNoFromList(aaList) of me
–> {4, 6}

–与えられたリスト中のfalseの項目番号をリストで返す
on retFalseItemNoFromList(aList)
  set aCount to 1
  
set eResList to {}
  
  
repeat with i in aList
    set j to contents of i
    
    
if j is not equal to true then
      set the end of eResList to aCount
    end if
    
    
set aCount to aCount + 1
  end repeat
  
  
return eResList
end retFalseItemNoFromList

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

10/10 文字列中に指定文字が何個入っているかカウントする v2

文字列中に指定文字が何個入っているかカウントし、出現数と出現位置のリストを返すAppleScriptです。

スクリプト名:文字列中に指定文字が何個入っているかカウントする v2
set a to “Joe A Hisaishi”
set aTargChar to ” “

set bList to countAcharInStrAndDetectPos(a, aTargChar) of me
–> {2, {4, 6}} –指定文字の出現回数カウント、登場位置リスト

–文字列中に指定文字が何個入っているかカウントし、登場位置をリストで返す
on countAcharInStrAndDetectPos(aStr, aTargChar)
  set aList to {}
  
set posC to 1
  
  
considering case
    set aHit to offset of aTargChar in aStr
    
    
if aHit is not equal to 0 then
      set aaList to characters of aStr
      
set aCount to 0
      
      
repeat with i in aaList
        
        
set j to contents of i
        
        
if j = aTargChar then
          set aCount to aCount + 1
          
set the end of aList to posC
        end if
        
        
set posC to posC + 1
        
      end repeat
      
      
      
return {aCount, aList}
    else
      return {0, {}}
    end if
  end considering
end countAcharInStrAndDetectPos

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

08/11 リスト同士の引き算、足し算

リスト同士の引き算、足し算を行うAppleScriptです。

リストの要素数が同じことが前提条件です。

要素数が多いリストの演算だとOSAXを併用することも考えたいところですが、それほど多くないものであれば、けっこうこんな簡単なルーチンを用意して処理させても大丈夫です。

スクリプト名:リスト同士の引き算、足し算
set aList to {15.07, 9.275, 18.63, 19.45}
set bList to {15.381, 11.6, 18.601, 15.7}

set aRes to listSubstraction(aList, bList) of me
–> {-0.311, -2.325, 0.029, 3.75}

–リスト同士の引き算
on listSubstraction(aList, bList)
  –アイテム数をくらべて、合っていなければエラー
  
set aLen to length of aList
  
set bLen to length of bList
  
if aLen is not equal to bLen then return {}
  
  
set aResList to {}
  
repeat with i from 1 to aLen
    set itemA to contents of item i of aList
    
set itemB to contents of item i of bList
    
    
set tmpR to itemA - itemB
    
set the end of aResList to tmpR
  end repeat
  
  
return aResList
  
end listSubstraction

–リスト同士の足し算
on listAddition(aList, bList)
  –アイテム数をくらべて、合っていなければエラー
  
set aLen to length of aList
  
set bLen to length of bList
  
if aLen is not equal to bLen then return {}
  
  
set aResList to {}
  
repeat with i from 1 to aLen
    set itemA to contents of item i of aList
    
set itemB to contents of item i of bList
    
    
set tmpR to itemA + itemB
    
set the end of aResList to tmpR
  end repeat
  
  
return aResList
  
end listAddition

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

07/18 InDesign CS3で選択中のオブジェクトの外周部の座標を取得する

InDesign CS3で選択中のオブジェクトの外周部の座標を取得するAppleScriptです。

idcs_range2.jpg

複数のオブジェクトの外周部の座標のみ取得したいという場合、安直にグループ化してその座標(geometric bouds)を取得する方法も考えないではないですが、元に戻すときに何かよくないこと(前後関係が狂うとか)が起こる可能性があるため、「グループ化→座標取得→グループ化解除」の順で処理するのは避けたいところ。

そこで、力ワザでありもののソートルーチンを用いて、ソートだけで取得するように処理してみました。y1とx1は昇順ソート、y2とx2は降順ソートして、それぞれ最初の項目から値を取得しています。

基礎的な属性アクセスしか使っていないため、とくにInDesignのバージョンに依存せずに使用できるはずです(tellブロックのアプリケーション名を書き換えるぐらい)。

スクリプト名:InDesign CS3で選択中のオブジェクトの外周部の座標を取得する
tell application “Adobe InDesign CS3″
  tell active document
    set gList to geometric bounds of selection
    
    
copy gList to y1List
    
copy gList to y2List
    
copy gList to x1List
    
copy gList to x2List
    
    
set y1Res to shellSortListAscending(y1List, 1) of me
    
set x1Res to shellSortListAscending(x1List, 2) of me
    
set y2Res to shellSortListDecending(y2List, 3) of me
    
set x2Res to shellSortListDecending(x2List, 4) of me
    
    
–外周部の座標を取得する
    
–y1, x1は最小のものを取得
    
set y1min to item 1 of item 1 of y1Res
    
set x1min to item 2 of item 1 of x1Res
    
    
–y2, x2は最大のものを取得
    
set y2max to item 3 of item 1 of y2Res
    
set x2max to item 4 of item 1 of x2Res
    
    
return {y1min, x1min, y2max, x2max}
    
–> {64.666666666667, 39.0, 177.0, 202.0}
  end tell
end tell

–シェルソートで入れ子のリストを昇順ソート
on shellSortListAscending(a, keyItem)
  set n to length of a
  
set cols to {1391376, 463792, 198768, 86961, 33936, 13776, 4592, 1968, 861, 336, 112, 48, 21, 7, 3, 1}
  
repeat with h in cols
    if (h (n - 1)) then
      repeat with i from h to (n - 1)
        set v to item (i + 1) of a
        
set j to i
        
repeat while (j h) and ((contents of item keyItem of item (j - h + 1) of a) > (item keyItem of v))
          set (item (j + 1) of a) to (item (j - h + 1) of a)
          
set j to j - h
        end repeat
        
set item (j + 1) of a to v
      end repeat
    end if
  end repeat
  
return a
end shellSortListAscending

–シェルソートで入れ子のリストを降順ソート
on shellSortListDecending(a, keyItem)
  set n to length of a
  
set cols to {1391376, 463792, 198768, 86961, 33936, 13776, 4592, 1968, 861, 336, 112, 48, 21, 7, 3, 1}
  
repeat with h in cols
    if (h (n - 1)) then
      repeat with i from h to (n - 1)
        set v to item (i + 1) of a
        
set j to i
        
repeat while (j h) and ((contents of item keyItem of item (j - h + 1) of a) < (item keyItem of v))
          set (item (j + 1) of a) to (item (j - h + 1) of a)
          
set j to j - h
        end repeat
        
set item (j + 1) of a to v
      end repeat
    end if
  end repeat
  
return a
end shellSortListDecending

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

07/10 指定矩形内に含まれるデータをアイテム番号で返す

指定矩形内に含まれるデータをアイテム番号で返すAppleScriptです。

以前に、指定矩形座標内に含まれる座標をピックアップというAppleScriptを掲載しましたが、その時にはデータ内容そのもの(contents)を返していました。InDesignの座標系である{Y1,X1,Y2,X2} という(珍妙な)座標の並び順を想定しています。

本ルーチンでは、アイテム番号を返すように一部変更したものです。

別途、オブジェクト参照のリストを保持しておき、本ルーチンで座標値の判定を行ったのちに、オブジェクト参照リストから対象をピックアップするような処理を想定しています。

スクリプト名:指定矩形内に含まれるデータをアイテム番号で返す
set aList to {{243.0, 17.0, 245.0, 77.0}, {169.000015228534, 12.0, 187.500015228534, 166.999999999992}, {189.000007614311, 13.0, 242.000007614312, 17.0}, {188.999990448174, 17.0, 241.999990448174, 76.999999025133}, {188.999990448174, 87.5, 241.999990448174, 123.5}}

set rangeList to {236.999990448174, 12.0, 246.999990448174, 81.999999025133} –{y1,x1,y2,x2}
set resList to withinRange_Y1X1Y2X2_retByID(rangeList, aList) of me

–指定矩形内に含まれるデータをアイテム番号で返す
on withinRange_Y1X1Y2X2_retByID(rangeList, targetList)
  set includedList to {}
  
  
set aCount to 1
  
  
set y1 to item 1 of rangeList
  
set x1 to item 2 of rangeList
  
set y2 to item 3 of rangeList
  
set x2 to item 4 of rangeList
  
  
repeat with i in targetList
    set targY1 to item 1 of i
    
set targX1 to item 2 of i
    
set targY2 to item 3 of i
    
set targX2 to item 4 of i
    
    
if (x1 < targX1) and (x2 > targX2) and (y1 < targY1) and (y2 > targY2) then
      set the end of includedList to aCount
    end if
    
    
set aCount to aCount + 1
    
  end repeat
  
  
return includedList
end withinRange_Y1X1Y2X2_retByID

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

07/10 指定アイテム以降は指定アイテム目に改行で区切って連結する

与えられたリストのうち、指定アイテム以降は指定アイテム目に改行で区切って連結するAppleScriptです。

たとえば、要素数が7つあるリストのうち、4アイテム目以降をこの処理対象と指定した場合、4〜7アイテム目が(改行をはさんて)連結されます。

スクリプト名:指定アイテム以降は指定アイテム目に改行で区切って連結する
set aList to {"○○初等部", "○○中等部", "○○高等部(400)", "○○大学", "○○女子短期大学", "○○女子短期大学"}

set a to mergeLastItem(aList, 4) of me
–>
(*
{"○○初等部", "○○中等部", "○○高等部(400)", "○○大学
○○女子短期大学
○○女子短期大学"}

*)

–指定アイテム以降は指定アイテム目に改行で区切って連結する
on mergeLastItem(aList, aLimit)
  set aLen to length of aList
  
if aLen > aLimit then
    set newItem to ""
    
    
set tmpList to items aLimit thru aLen of aList
    
set newItem to retDelimedText(tmpList, return) of me
    
    
set bList to items 1 thru (aLimit - 1) of aList
    
set the end of bList to newItem
    
set aList to bList
  end if
  
  
return aList
end mergeLastItem

on retDelimedText(aList, aDelim)
  set aText to ""
  
set curDelim to AppleScript’s text item delimiters
  
set AppleScript’s text item delimiters to aDelim
  
set aText to aList as text
  
set AppleScript’s text item delimiters to curDelim
  
return aText
end retDelimedText

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

06/10 リスト中の値が連続する箇所をアイテム番号のリストで返す v2

リスト中の値が連続する箇所をアイテム番号のリストで返すAppleScriptの修正版です。

実際にほかのルーチンと組み合わせてみたら間違いが判明したので修正しました。

さきほどの、

{”プロジェクト階層図”, “ストレージ仕様”, “データ仕様”, “年間仕様”, “年間仕様”, “年間仕様”, “YP仕様”, “YP仕様”, “YP仕様”, “月間仕様”, “月間仕様”, “月間仕様”, “月間仕様”, “月間仕様”, “月間仕様”, “月間仕様”, “月間仕様”}

というデータを処理した場合に、先ほどは……

{{4, 6}, {8, 9}, {11, 17}}

という結果が返ってきていましたが、本当は……

{{4, 6}, {7, 9}, {10, 17}}

が返ってこなくてはならなかったのでした。連続部分の末尾を見つけた直後の動作が一部おかしかったので、そこだけ修正しました。

スクリプト名:リスト中の値が連続する箇所をアイテム番号のリストで返す v2
set aList to {"プロジェクト階層図", "ストレージ仕様", "データ仕様", "年間仕様", "年間仕様", "年間仕様", "YP仕様", "YP仕様", "YP仕様", "月間仕様", "月間仕様", "月間仕様", "月間仕様", "月間仕様", "月間仕様", "月間仕様", "月間仕様"}

set sList to detectRepetition(aList) of me

sList
–> {{4, 6}, {7, 9}, {10, 17}}

–リスト中の値が連続する箇所をアイテム番号のリストで返す
on detectRepetition(aList)
  
  
set curItem to first item of aList
  
set aList to rest of aList
  
  
set aCount to 1
  
set sucF to false
  
  
set sList to {}
  
set curList to {}
  
  
repeat with i in aList
    set j to contents of i
    
if curItem is equal to j then
      if sucF = true then
        –何もしない。データの連続部分の中をスキャン中  
      else
        set curItem to j
        
set curList to {aCount}
        
set sucF to true
      end if
    else
      if sucF = true then
        –連続部分の末尾を見つけた
        
set the end of curList to aCount
        
set the end of sList to curList
        
set curList to {}
        
set curItem to j –ここを修正した
        
set sucF to false
      else
        –なにもしない
        
set curItem to j
      end if
    end if
    
    
set aCount to aCount + 1
  end repeat
  
  
if sucF = true then
    set the end of curList to aCount
    
set the end of sList to curList
  end if
  
  
return sList
  
end detectRepetition

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

06/10 リスト中の値が連続する箇所をアイテム番号のリストで返す

リスト中の値が連続する箇所をアイテム番号のリストで返すAppleScriptです。

{”プロジェクト階層図”, ”ストレージ仕様”, ”データ仕様”, ”年間仕様”, ”年間仕様”, ”年間仕様”, ”YPLAN仕様”, ”YPLAN仕様”, ”YPLAN仕様”, ”月間仕様”, ”月間仕様”, ”月間仕様”, ”月間仕様”, ”月間仕様”, ”月間仕様”, ”月間仕様”, ”月間仕様”}

というリストがあった場合に、本Scriptを実行すると、

{{4, 6}, {8, 9}, {11, 17}}

という結果が得られます。これは、4〜6アイテム目、8〜9アイテム目そして11〜17アイテム目が同一内容が連続していることを示しています。

Keynoteで仕様書を作っていたら、どんどん内容が増えてしまい、

「どこかの部分の仕様(1/3)」

などと書いておいたものが、あとからあとから「(1/3)」の部分が変更になって、なんでこんなものを人の手で更新しなければならないのだとイライラが最高潮に。

おもむろにAppleScriptエディタを立ち上げ、AppleScriptでしばらくKeynoteをこづき回していたところ、各slideのtitleの取得および設定ができることを確認。

ふだん、Keynoteで書類を作る場合にはKeynoteが用意している「マスタースライド」の機能は完全に無視して、「空白」のマスタースライドの上に好き勝手にアイテムを配置していたのですが……あえてマスタースライドの機能を利用すると、きちんとslideのtitle属性部分に各ページのタイトルが入ることを確認。

「じゃあ、AppleScriptでKeynoteのスライドのタイトルを取得して連続部分のリナンバーをさせればよいのではないか?」

と、思い立ち……アイデアだけメモっておいて実際に作りはじめてみた次第です。

本ルーチンはその処理を実現するためのキーとなるものであり、もう少し組めば実現でき……るのではないでしょうか?

スクリプト名:リスト中の値が連続する箇所をアイテム番号のリストで返す
set aList to {“プロジェクト階層図”, “ストレージ仕様”, “データ仕様”, “年間仕様”, “年間仕様”, “年間仕様”, “YPLAN仕様”, “YPLAN仕様”, “YPLAN仕様”, “月間仕様”, “月間仕様”, “月間仕様”, “月間仕様”, “月間仕様”, “月間仕様”, “月間仕様”, “月間仕様”}

set sList to detectRepetition(aList) of me

sList
–> {{4, 6}, {8, 9}, {11, 17}}

–リスト中の値が連続する箇所をアイテム番号のリストで返す
on detectRepetition(aList)
  
  
set curItem to first item of aList
  
set aList to rest of aList
  
  
set aCount to 1
  
set sucF to false
  
  
set sList to {}
  
set curList to {}
  
  
repeat with i in aList
    set j to contents of i
    
if curItem is equal to j then
      if sucF = true then
        –何もしない。データの連続部分の中をスキャン中  
      else
        set curItem to j
        
set curList to {aCount}
        
set sucF to true
      end if
    else
      if sucF = true then
        –連続部分の末尾を見つけた
        
set the end of curList to aCount
        
set the end of sList to curList
        
set curList to {}
        
set curItem to “”
        
set sucF to false
      else
        –なにもしない
        
set curItem to j
      end if
    end if
    
    
set aCount to aCount + 1
  end repeat
  
  
if sucF = true then
    set the end of curList to aCount
    
set the end of sList to curList
  end if
  
  
return sList
  
end detectRepetition

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

04/16 数値リストを連続部分に分解する(データを返す)

与えられたリストを連続部分に分解するAppleScriptです。本ルーチンは、連続状態を与えられたリストのデータで返します。

リスト中に「n文字目」といった情報が入っている場合には、アイテム番号を返されるよりはデータ内容を返したほうが有用です。実際に必要に迫られて作成した次第。

スクリプト名:数値リストを連続部分に分解する(データを返す)
set aList to {30, 40, 41, 42, 43, 44, 45, 46, 47, 48, 63, 64, 85, 86, 88, 89, 90, 107, 108, 109, 110, 111, 112, 113, 114, 115}

set aRes to splitListByBreakAndReturnContentList(aList) of me
–> {{30, 30}, {40, 48}, {63, 64}, {85, 86}, {88, 90}, {107, 115}}

–数値リストを連続部分に分解する(データを返す)
on splitListByBreakAndReturnContentList(aList)
  
  
set bList to detectChangeInNumSeriesList(aList) of me
  
–return bList
  
–> {9, 11, 13, 16}–アイテムの切れ目を検出する
  
  
  
if length of bList is not equal to 1 then
    set prevItem to contents of first item of bList
    
    
set bList to rest of bList
    
    
set outList to {{first item of aList, item prevItem of aList}}
    
    
repeat with i in bList
      set j1 to prevItem + 1
      
set j2 to contents of i
      
set the end of outList to {item j1 of aList, item j2 of aList}
      
set prevItem to j2
    end repeat
    
    
set aLen to length of aList
    
if j2 is not equal to aLen then
      set the end of outList to {item (j2 + 1) of aList, item aLen of aList}
    end if
  else
    set outList to {item (contents of first item of bList) of aList}
  end if
  
  
return outList
end splitListByBreakAndReturnContentList

on detectChangeInNumSeriesList(aNumList)
  set prevNum to (first item of aNumList)
  
set bNumList to rest of aNumList
  
  
set outList to {}
  
  
set iCount to 1
  
  
repeat with i in bNumList
    set j to contents of i
    
if prevNum = (j - 1) then
      
    else
      set the end of outList to iCount
    end if
    
    
copy j to prevNum
    
set iCount to iCount + 1
  end repeat
  
  
–すべて連続していた場合への対処
  
if outList = {} then
    set outList to {{1, length of aNumList}}
  end if
  
  
return outList
  
end detectChangeInNumSeriesList

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

04/16 数値リストを連続部分に分解する(アイテム番号を返す)

与えられたリストを連続部分に分解するAppleScriptです。本ルーチンは、連続状態を与えられたリストのアイテム番号で返します。

AppleScriptによる高速処理、とくにアプリケーションのコントロールの高速化を考えたときに、たとえばあるオブジェクト中の文字の書式を変更するという処理。頭から1文字ずつ処理するよりも、「○文字目から○文字目までの書式を変更しろ」と指定したほうが、はるかに速く処理できます。

アイテム番号で結果を返す本ルーチンは、そうした高速処理に絶大な威力を発揮します。

スクリプト名:数値リストを連続部分に分解する(アイテム番号を返す)
set aList to {40, 41, 42, 43, 44, 45, 46, 47, 48, 63, 64, 85, 86, 88, 89, 90, 107, 108, 109, 110, 111, 112, 113, 114, 115}

set aRes to splitListByBreakAndReturnItemNumList(aList) of me
–> {{1, 9}, {10, 11}, {12, 13}, {14, 16}, {17, 25}}

–数値リストを連続部分に分解する(アイテム番号を返す)
on splitListByBreakAndReturnItemNumList(aList)
  set bList to detectChangeInNumSeriesList(aList) of me
  
  
if length of bList is not equal to 1 then
    set prevItem to contents of first item of bList
    
    
set bList to rest of bList
    
    
set outList to {{1, prevItem}}
    
    
repeat with i in bList
      set j1 to prevItem + 1
      
set j2 to contents of i
      
set the end of outList to {j1, j2}
      
set prevItem to j2
    end repeat
    
    
set aLen to length of aList
    
if j2 is not equal to aLen then
      set the end of outList to {j2 + 1, aLen}
    end if
  else
    set outList to {contents of first item of bList}
  end if
  
  
return outList
end splitListByBreakAndReturnItemNumList

on detectChangeInNumSeriesList(aNumList)
  set prevNum to (first item of aNumList)
  
set bNumList to rest of aNumList
  
  
set outList to {}
  
  
set iCount to 1
  
  
repeat with i in bNumList
    set j to contents of i
    
if prevNum = (j - 1) then
      
    else
      set the end of outList to iCount
    end if
    
    
copy j to prevNum
    
set iCount to iCount + 1
  end repeat
  
  
–すべて連続していた場合への対処
  
if outList = {} then
    set outList to {{1, length of aNumList}}
  end if
  
  
return outList
  
end detectChangeInNumSeriesList

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

04/08 入れ子のリストをタブ区切りのテキストに

入れ子のリストをタブ区切りのテキストに変換するAppleScriptです。

分布リスト作成v1で作成したデータをテキスト化するために、さくっと作ってみましたが……同じものを以前に何度か作ったような……。

スクリプト名:入れ子のリストをタブ区切りのテキストに
set aList to {{"on handler1(thisYear, m)", "●", "●"}, {"on handler2(writeOutPathStr)", "●", ""}, {"on handler3(templatePath)", "●", ""}, {"on handler4(lastCellAdrNum)", "●", ""}, {"on handler5(aList, namePrefix, layerName)", "●", ""}, {"on handler2(aList, aDelim)", "", "●"}, {"on handler3(aList)", "", "●"}, {"on handler7(targetFrameName)", "", "●"}, {"on handler8(aStr)", "", "●"}}

set aText to retItemDelimedAndParagraphDelimedText(aList, tab, return) of me
–>
(*
"on handler1(thisYear, m)  ●  ●
on handler2(writeOutPathStr)  ●  
on handler3(templatePath)  ●  
on handler4(lastCellAdrNum)  ●  
on handler5(aList, namePrefix, layerName)  ●  
on handler2(aList, aDelim)    ●
on handler3(aList)    ●
on handler7(targetFrameName)    ●
on handler8(aStr)    ●
"
*)

–入れ子のリストを、アイテム間のデリミタとパラグラフ間のデリミタを指定してテキスト化
–というか、入れ子のリストをタブ区切りテキストにするのが目的
on retItemDelimedAndParagraphDelimedText(aList, itemDelim, paragraphDelim)
  set aText to ""
  
  
repeat with i in aList
    set aStr to retDelimedText(i, itemDelim) of me
    
set aText to aText & aStr & paragraphDelim
  end repeat
  
  
return aText
end retItemDelimedAndParagraphDelimedText

on retDelimedText(aList, aDelim)
  set aText to ""
  
set curDelim to AppleScript’s text item delimiters
  
set AppleScript’s text item delimiters to aDelim
  
set aText to aList as text
  
set AppleScript’s text item delimiters to curDelim
  
return aText
end retDelimedText

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

04/08 分布リスト作成 v1

2つのAppleScriptプログラム内の使用ハンドラ一覧の分布リストを作成するAppleScriptです。AppleScriptのプログラム解析用のAppleScriptです。

構成が似通ったプログラムを大量に作ったときに、使用サブルーチンの差異を一覧表にまとめるべく作成しました。

資料を作成するために手作業で集計を行っていたのですが、あまりに面倒なのでプログラムで片づけることにしてみました。

このバージョンでは、2つのAppleScriptのハンドラ同士を比較していますが、実際には8つぐらいのAppleScriptのサブルーチン分布リストを作成。ハンドラの抽出については、別途AppleScriptのプログラムで行っておきましたが……そこまですべて通しで全自動で比較表を作ってみるようにしてもよいかもしれません。

冷静に評価してみると、このプログラム……けっこう応用範囲が広そうです。

スクリプト名:分布リスト作成 v1
–ハンドラ対照表を作成する

property trueChar : “●”
property falseChar : “”

set h1List to getH1() of me
set h2List to getH2() of me

–元になるアイテムリストを作成する
set newList to {}
repeat with i in h1List
  set aLine to {contents of i, trueChar}
  
set the end of newList to aLine
end repeat

set aRes to makeDiffTable(newList, h2List, 1) of me
–> {{”on handler1(thisYear, m)”, “●”, “●”}, {”on handler2(writeOutPathStr)”, “●”, “”}, {”on handler3(templatePath)”, “●”, “”}, {”on handler4(lastCellAdrNum)”, “●”, “”}, {”on handler5(aList, namePrefix, layerName)”, “●”, “”}, {”on handler2(aList, aDelim)”, “”, “●”}, {”on handler3(aList)”, “”, “●”}, {”on handler7(targetFrameName)”, “”, “●”}, {”on handler8(aStr)”, “”, “●”}}

on makeDiffTable(newList, h2List, blankCount)
  set aLen to length of h2List
  
set aaLen to length of newList
  
  
repeat with i from 1 to aLen
    –ハンドラを取り出す
    
set aCon to contents of item i of h2List
    
    
set hitF to false
    
    
repeat with ii from 1 to aaLen
      set bCon to (first item of (item ii of newList))
      
      
if bCon = aCon then
        set the end of (item ii of newList) to trueChar
        
set hitF to true
        
–exit repeat
      end if
    end repeat
    
    
if hitF = false then
      set newItem to {aCon}
      
repeat blankCount times
        set the end of newItem to falseChar
      end repeat
      
set the end of newItem to trueChar
      
      
set the end of newList to newItem
    end if
    
  end repeat
  
  
–newListのアイテム数が合わない箇所にブランクを足す
  
set maxItemNum to retMaxListNumber(newList) of me
  
set newList to setBlankToEndToShorterItem(newList, maxItemNum, “”) of me –ブランク要素として”_”を指定する
  
  
  
return newList
  
end makeDiffTable

–与えられたリストのうち、最大長に足りないアイテムに対し、blankItemを追加する
on setBlankToEndToShorterItem(aList, maxItemNum, blankItem)
  set newList to {}
  
repeat with i in aList
    set j to contents of i
    
set aLen to length of j
    
if aLen is not equal to maxItemNum then
      set loopNum to maxItemNum - aLen
      
repeat with ii from 1 to loopNum
        set the end of j to blankItem
      end repeat
    end if
    
set the end of newList to j
  end repeat
  
  
return newList
end setBlankToEndToShorterItem

–リスト中のすべてのアイテムをループして、アイテム数の最大のものを求める
on retMaxListNumber(aList)
  set maxNum to 0
  
repeat with i in aList
    set aLen to length of i
    
if maxNum < aLen then
      set maxNum to aLen
    end if
  end repeat
  
  
return maxNum
end retMaxListNumber

on getH1()
  return paragraphs of “on handler1(thisYear, m)
on handler2(writeOutPathStr)
on handler3(templatePath)
on handler4(lastCellAdrNum)
on handler5(aList, namePrefix, layerName)”

end getH1

on getH2()
  return paragraphs of “on handler1(thisYear, m)
on handler2(aList, aDelim)
on handler3(aList)
on handler7(targetFrameName)
on handler8(aStr)”

  
end getH2

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

03/08 aliasのリストをファイルの作成日でソートして返す

与えられたaliasのリストを各ファイルの作成日でソートして古い順に返すAppleScriptです。

ドラッグ&ドロップすると指定のプリンタにInDesignから印刷を行うAppleScriptのドロップレットを書いて使ってみたら、プリンタに送られる順番が目茶苦茶になってしまいました。

あとから印刷結果を手でソートすることほどむなしいことはありません。

そこで、ドロップレット用にファイルの作成日時でソートするサブルーチンを、汎用ルーチンの寄せ集めで作った次第です。

find1.jpg

こんな風にFinder上でファイルを選択しておいて本Scriptを実行すると、作成日時が古い順にソートして返してくれます。AppleScriptから数百ファイルもドキュメントを生成して、印刷しなければならないような場合でも、作成日時の古い順から印刷してくれます。

のんびりしていそうな割には、切実なニーズを満たすために作成したものだったりします。

スクリプト名:aliasのリストをファイルの作成日でソートして返す
tell application "Finder"
  set aSel to selection as alias list
end tell

–ファイル作成日でソート(古いものから新しいものへ)
set aliasRes to sortAliasByCreationDate(aSel) of me

–> {alias "Cherry:Users:maro:Documents:AppleScript:MacJournalで選択中のjournal entryのBlog公開ステータスを取得する.scpt", alias "Cherry:Users:maro:Documents:AppleScript:目に見えるプロセス一覧を取得する.scpt", alias "Cherry:Users:maro:Documents:AppleScript:最前面のアプリケーションを取得する.scpt", alias "Cherry:Users:maro:Documents:AppleScript:InDesignの文字置換機能を用いた文字置換.scpt", alias "Cherry:Users:maro:Documents:AppleScript:aliasのリストをファイルの作成日でソートして返す.scpt"}

on sortAliasByCreationDate(aliasList)
  
  
tell application "Finder"
    set aList to {}
    
    
repeat with i in aliasList
      set j to contents of i
      
set the end of aList to {j, creation date of j}
    end repeat
    
    
set cList to shellSortListAscending(aList, 2) of me –で作成日時でソート
    
    
    
set aliasList to {}
    
set fnCount to 1
    
repeat with i in cList
      set j to contents of (item 1 of i)
      
      
set the end of aliasList to j
    end repeat
  end tell
  
  
return aliasList
  
end sortAliasByCreationDate

–シェルソートで入れ子のリストを昇順ソート
on shellSortListAscending(a, keyItem)
  set n to length of a
  
set cols to {1391376, 463792, 198768, 86961, 33936, 13776, 4592, 1968, 861, 336, 112, 48, 21, 7, 3, 1}
  
repeat with h in cols
    if (h (n - 1)) then
      repeat with i from h to (n - 1)
        set v to item (i + 1) of a
        
set j to i
        
repeat while (j h) and ((contents of item keyItem of item (j - h + 1) of a) > (item keyItem of v))
          set (item (j + 1) of a) to (item (j - h + 1) of a)
          
set j to j - h
        end repeat
        
set item (j + 1) of a to v
      end repeat
    end if
  end repeat
  
return a
end shellSortListAscending

–シェルソートで入れ子のリストを降順ソート
on shellSortListDecending(a, keyItem)
  set n to length of a
  
set cols to {1391376, 463792, 198768, 86961, 33936, 13776, 4592, 1968, 861, 336, 112, 48, 21, 7, 3, 1}
  
repeat with h in cols
    if (h (n - 1)) then
      repeat with i from h to (n - 1)
        set v to item (i + 1) of a
        
set j to i
        
repeat while (j h) and ((contents of item keyItem of item (j - h + 1) of a) < (item keyItem of v))
          set (item (j + 1) of a) to (item (j - h + 1) of a)
          
set j to j - h
        end repeat
        
set item (j + 1) of a to v
      end repeat
    end if
  end repeat
  
return a
end shellSortListDecending

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

02/06 指定年のすべての日付をdateオブジェクトで取得する

指定年のすべての日付をdateオブジェクトで取得するAppleScriptです。

こういう基礎的なプログラムは、誰が組んでもあんまり差が出ないので、あえてゼロから組む必要性がなく……こうした基礎的な部品を組み合わせてどれだけ高度な処理を組み上げられるか、というところに頭を使いたいところです。

スクリプト名:指定年のすべての日付をdateオブジェクトで取得する
set thisYear to 2020
set dList to {}

repeat with i from 1 to 12
  set mLen to getMlen(thisYear, i) of me
  
repeat with ii from 1 to mLen
    set dStr to (thisYear as string) & "/" & (i as string) & "/" & (ii as string)
    
set the end of dList to date dStr
  end repeat
end repeat

dList

–指定月の長さを得る(日数)
on getMlen(aYear, aMonth)
  
  
set aDat to (aYear as text) & "/" & (aMonth as text) & "/1"
  
if aMonth is not equal to 12 then
    set eDat to ((aYear as text) & "/" & (aMonth + 1) as text) & "/1"
  else
    set eDat to ((aYear + 1) as text) & "/" & (1 as text) & "/1"
  end if
  
  
–set sDat to date aDat
  
set eDat to date eDat
  
set eDat to eDat - 1
  
  
set mLen to day of eDat
  
return mLen
  
end getMlen

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

02/04 2行に別れているデータを1行分にまとめる

2行に別れているデータを1行分にまとめるAppleScriptです。

Excel上の選択範囲からデータを取得し、その内容が2行でワンセットになっているような場合に、2行目に存在しているデータについては1行目のアイテムにリストとして統合するものです。

たとえば、

   {{”4″, “56″, “52″}, {”", “64″, “61″}}

というデータがあったとして、これを本AppleScriptで処理すると……

  {{”4″, {”56″, “64″}, {”52″, “61″}}}

という結果が得られます。

スクリプト名:2行に別れているデータを1行分にまとめる
set aList to {{"4", "56", "52"}, {"", "64", "61"}}
set aLen to length of aList

set gList to {}


repeat with i from 1 to aLen by 2
  set line1List to contents of item i of aList
  
set line2List to contents of item (i + 1) of aList
  
  
–1データセット分を処理する
  
set aaLen to length of line1List
  
  
set aaList to {}
  
repeat with ii from 1 to aaLen
    set line1item to contents of item ii of line1List
    
set line2item to contents of item ii of line2List
    
    
if line2item is not equal to "" then
      set the end of aaList to {line1item, line2item}
    else
      set the end of aaList to line1item
    end if
    
  end repeat
  
  
set the end of gList to aaList
  
end repeat

gList
–> {{"4", {"56", "64"}, {"52", "61"}}}

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

01/25 リストで与えられたデータの変動ベクトル方向を取得する

リストで与えられたデータの変動ベクトル方向を取得するAppleScriptです。

……こう書くと何のことやら分かりませんが、かなり具体的なニーズに応えるために作成したものです。

Illustrator上でグラフを作成し、グラフのマーカーに近接して数値情報を配置しようとしたときに、グラフの線と重ならないように数値情報を配置することが必要になりました。

そこで、グラフの値が上昇している部分なのか、下降している部分なのか……といった傾向の判断を行うためにこうしたデータを作成し、さらにグラフの左右の端にあるかどうかなどのデータを取得して総合的に位置決めを行うことにした次第。

こうしたデータの変動ベクトル方向を求めるという用途は意外にたくさんあり、自分は割とよく使う処理です。

Illustratorのグラフ機能は貧弱で、どうせそのままでは使い物にならないので、GUI Scripting経由でAppleScriptからデータを突っ込んで、グラフオブジェクトをグループ解除して「ただのpath item」にまで分解してから料理しています。データを差し替えるとグラフのマーカーが変わってしまったとしても、分解してしまえば大丈夫です。

スクリプト名:リストで与えられたデータの変動ベクトル方向を取得する
set aList to {0.325, 0.4, 0.35, 0.012, 0.025, 0.55}

set curItem to first item of aList
set aList to rest of aList

set trendList to {}

repeat with i in aList
  
  
if curItem < i then
    set aTrend to “u”
  else if curItem > i then
    set aTrend to “d”
  else
    aTrend to “-”
  end if
  
  
set the end of trendList to aTrend
  
copy i to curItem
  
end repeat

trendList
–> {”u”, “d”, “d”, “u”, “u”}

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

01/12 リストの項目数をそろえてタブで区切ったテキストに変換する

与えられたリストの項目数をプロパティ(maxRow)で指定した数と比較し、足りない分はヌル要素を追加して、最終的にタブで区切ったテキストに変換するAppleScriptです。

Illustratorでグラフデータを連続自動作成する処理で、グラフに与えるデータを作成したものです。

スクリプト名:リストの項目数をそろえてタブで区切ったテキストに変換する
set maxRow to 9 –最大項目数
set aList to {40, 30, 20, 10}

–項目数が足りない場合には項目を追加する
set outStr to ""
set itemC to count every item of aList
if itemC < maxRow then
  repeat (maxRow - itemC) times
    set aList to aList & 0
  end repeat
end if
–> {40, 30, 20, 10, 0, 0, 0, 0, 0}

–リストを指定デリミタでテキスト化
set sList to {}
repeat with i in aList
  set j to contents of i
  
  
–このあたりで、適度にデータ加工するとよさそう(指定桁以下の切り捨てとか四捨五入とか)
  
if j = 0 then
    set jj to ""
  else
    set jj to j as string
  end if
  
  
set the end of sList to jj
end repeat
–> {"40", "30", "20", "10", "", "", "", "", ""}

set resStr to retDelimedText(sList, tab) of me
–> "40  30  20  10          "

–リストを指定デリミタでテキスト化
on retDelimitedText(aList, aNewDelim)
  set aText to ""
  
set curDelim to AppleScript’s text item delimiters
  
set AppleScript’s text item delimiters to aNewDelim
  
set aText to aList as text
  
set AppleScript’s text item delimiters to curDelim
  
return aText
end retDelimitedText

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

01/05 リスト同士のdiffをとる

2つのリスト同士を比較して、相互に存在しない要素をピックアップします。いわゆる、リスト間のdiffをとるAppleScriptです。

正直なところ、相当昔(2〜3年ぐらい昔)に作ったので処理の細部を覚えていないのですが……なにげに途中に不思議な処理(なぜか同じ置換処理を2回行っている……)があったりします。1行だけにしても、置換処理そのものをコメントアウトしても問題なく動いているので、何か込み入ったデータを喰わせた際の対策だったのかもしれません。

<追記>
HTMLに書き出してはじめて分かりましたが、スペースの置換とタブの置換を行っていたようです。いやはや。
</追記ここまで>

例によって汎用サブルーチンを組み合わせて作ったため、「diffをとる」という単純で簡素な目的とはそぐわないほどの行数になっていますが、短時間で作ったためにそうなったものとご理解ください。

スクリプト名:リスト同士のdiffをとる
set aList to {1, 2, 3, 4, 0, 9, 10, 11}
set bList to {4, 3, 2, 1, 5, 10}
set aRes to diffList(aList, bList) of listDiffKit
–> {{0, 9, 11}, {5}}

set aList to {“abc”, “cde”, “def”}
set bList to {“abc”, “cde”, “def”, “ddd”}
set aRes to diffList(aList, bList) of listDiffKit
–> {{}, {”ddd”}}

set aList to {“ぴよ”, “ひよ”, “ビよ”, “よよ”}
set bList to {“ビよ”, “ひよ”, “ぴよ”, “ぱよ”}
set aRes to diffList(aList, bList) of listDiffKit
–>{{”よよ”}, {”ぱよ”}}

script listDiffKit
  
  
–リスト同士のdiffをとる
  
on diffList(aaList, bbList)
    copy {aaList, bbList} to {aList, bList} –fix?
    
set notExistInBlist to {}
    
    
if aList is equal to bList then
      return {{}, {}}
    end if
    
    
set aListText to (retDelimedText(aList, “|”) of me) as Unicode text
    
set aListText to repChar(aListText, “ ” as Unicode text, “”) of me
    
set aListText to repChar(aListText, ” “ as Unicode text, “”) of me –?????
    
    
set bListText to (retDelimedText(bList, “|”) of me) as Unicode text
    
set bListText to repChar(bListText, “ ” as Unicode text, “”) of me
    
set bListText to repChar(bListText, ” “ as Unicode text, “”) of me –?????
    
    
if aListText is equal to bListText then
      return {{}, {}}
    end if
    
    
repeat with i in aList
      set j to contents of i
      
if j is in bList or j is equal to bList then
        set bList to deleteSpecifiedItemFromList(bList, j) of me
      else
        set the end of notExistInBlist to j
      end if
    end repeat
    
    
return {notExistInBlist, bList}
  end diffList
  
  
–指定内容の要素をリストから削除して返す
  
on deleteSpecifiedItemFromList(aList, anItem)
    set iCount to 1
    
repeat with i in aList
      set j to contents of i
      
if j = anItem then
        set aaList to oneItemDelete(aList, iCount) of me
        
return aaList
      end if
      
set iCount to iCount + 1
    end repeat
    
    
return aList
    
  end deleteSpecifiedItemFromList
  
  
–リスト中の指定要素を削除して返す
  
on oneItemDelete(aList, chgNum)
    set newList to {}
    
set aLen to length of aList
    
    
if chgNum = 1 then
      set aLen to length of aList
      
if aLen > 1 then
        set maeList to items 2 thru aLen of aList
        
set newList to maeList
      else
        set newList to {}
      end if
    else if chgNum = aLen then
      set maeList to items 1 thru (aLen - 1) of aList
      
set newList to maeList
      
    else
      set maeList to items 1 thru (chgNum - 1) of aList
      
set atoList to items (chgNum + 1) thru -1 of aList
      
set newList to maeList & atoList
    end if
    
    
return newList
  end oneItemDelete
  
  
on retDelimedText(aList, aDelim)
    set aText to “”
    
set curDelim to AppleScript’s text item delimiters
    
set AppleScript’s text item delimiters to aDelim
    
set aText to aList as text
    
set AppleScript’s text item delimiters to curDelim
    
return aText
  end retDelimedText
  
  
–文字置換ルーチン
  
on repChar(origText, targStr, repStr)
    set {txdl, AppleScript’s text item delimiters} to {AppleScript’s text item delimiters, targStr}
    
set temp to text items of origText
    
set AppleScript’s text item delimiters to repStr
    
set res to temp as text
    
set AppleScript’s text item delimiters to txdl
    
return res
  end repChar
end script

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

12/14 入れ子のリストから、指定アイテムの値が連続して同じ値で続くブロックを検出する

入れ子になっているリストから、要素中の指定アイテムの値が連続して同じ値で続くブロックを検出するAppleScriptです。

{{”ぴよぴよ”, 123}, {”ぴよぴよ”, 125}, {”ぴよぴよ”, 127}, {”ぴよりん”, 200}, {”ぴよきち”, 210}, {”ぴよこ”, 230}, {”ぴよこ”, 231}}

のようなリストがあったときに、各要素の第1項目が連続している箇所の始点アイテムと終点アイテムをペアにして返します。

膨大なデータのソーティングを行ったときに作ったもので、項目が重複している場合には別項目をキーにして重複部分のみ再ソートして返すようにしていました。

プログラム中でグローバル宣言を行っているのは、もともとの記述からa reference toによる変数への間接アクセスを行うようにしたためで、これをやるのとやらないのとではデータ数が増えたときのスピードがケタ違いです。

スクリプト名:入れ子のリストから、指定アイテムの値が連続して同じ値で続くブロックを検出する
global subA01List, subA01List_r

set aDataList to {{"ぴよぴよ", 123}, {"ぴよぴよ", 125}, {"ぴよぴよ", 127}, {"ぴよりん", 200}, {"ぴよきち", 210}, {"ぴよこ", 230}, {"ぴよこ", 231}}
set rList to detectSuccessBlock(aDataList, 1) of me
–> {{1, 3}, {6, 7}}

–アイテムitemNoの値が連続して同じ値で続くブロックを検出する
–aDataListには、{item1,item2}の形式でデータが入っている。値の検出を行うのはitem1の部分のみ
on detectSuccessBlock(aDataList, itemNo)
  
  
copy aDataList to subA01List
  
  
set aFlag to ""
  
set blockListSub to {} – ブロック分割のすべてを記録しておく変数
  
set blockListSub_r to a reference to blockListSub
  
  
set blockItem to {} –小ブロックの記録変数
  
set sFlag to false
  
  
set prevItem to item itemNo of first item of subA01List
  
set subA01List to rest of subA01List
  
set iCount to 1
  
  
set subA01List_r to a reference to subA01List
  
  
repeat with i in subA01List_r
    set curItem to contents of item itemNo of i
    
    
if (prevItem as string) = (curItem as string) then
      if sFlag = false then
        –これまではブロック中にはいなかった。新たに値が連続するブロックを検出した
        
set blockItem to {iCount}
        
      else
        –連続したブロック中にいる。継続して同じ値が続いている
      end if
      
      
set sFlag to true
      
    else
      if sFlag = false then
        –これまでも連続ブロック中にはいなかった
      else
        –直前まで連続ブロックの中をスキャンしていた
        
–連続ブロックの末尾を見つけた
        
set the end of blockItem to iCount
        
set the end of blockListSub to blockItem
        
        
set blockItem to {} –ブロック保持用リストをクリア(ブロック末尾を検出したため)
      end if
      
      
set sFlag to false
    end if
    
    
set prevItem to curItem
    
set iCount to iCount + 1
  end repeat
  
  
–連続ブロックが継続したままになっていた場合の処理
  
–末尾に存在していた連続ブロックを検出する
  
if sFlag = true and blockItem is not equal to {} then
    set the end of blockItem to iCount
    
set the end of blockListSub to blockItem
  end if
  
  
return blockListSub
end detectSuccessBlock

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

09/21 指定の数字の組み合わせのすべてのパターンをリストアップ

指定の数字の組み合わせのすべてのパターンをリストアップするAppleScriptです。

あるアプリケーションでGUI側からパラメータを変更して、すべての組み合わせパターンについて総当たりで計算を行い、その計算結果をパラメータ入りのファイル名で保存したい。そんなときに、各パラメータのすべての組み合わせからなる特定桁の数のすべての組み合わせを算出……………

ああ、めんどくさい〜。考えていたらまじめに計算するのがバカバカしくなってきました。

そこで、発想の大転換。

指定桁の数値をすべて作成しておいて、ループで条件に合うものだけをピックアップすればよいのではないか、と。コンピュータのCPUパワーにモノを言わせて抽出するように路線転換してみました。

作ってみると……たしかに、プログラムは簡単。一応、チェックしてみましたが、組み合わせ数もきっちり合っています。

5つの数値パラメータから構成される数字の組み合わせを計算するために、10000〜99999までの89999個のシーケンシャル・ナンバーを作成し、パラメータの範囲を入れたリストをもとに条件にすべて合うものだけを抽出。

スクリプト名:指定の数字の組み合わせのすべてのパターンをリストアップ
set aTime to current date

set aList to {}
set aList_r to a reference to aList

set bList to {}
set bList_r to a reference to bList

set okList to {{1, 2}, {1, 2, 3}, {0, 1}, {1, 2, 3, 4}, {1, 2, 3, 4, 5, 6, 7, 8}}
set okL_r to a reference to okList

–指定桁数の数字をジェネレートする
repeat with i from 10000 to 99999
  set the end of aList_r to i
end repeat

–生成した数字リストを評価
repeat with i in aList_r
  set j to i as string
  
set cList to characters of j
  
  
set cLen to length of cList
  
set hitF to true
  
repeat with ii from 1 to cLen
    if (item ii of cList) as number is not in (item ii of okL_r) then
      set hitF to false
      
exit repeat
    end if
  end repeat
  
  
–条件にすべて合ったものだけピックアップ
  
if hitF = true then
    set the end of bList_r to contents of i
  end if
end repeat

set bTIme to current date
set cTIme to bTIme - aTime

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

結局、384個のデータを抽出。Core i7 2.66GHzのMacBook Proで7秒ほどかかりました。まあ、このぐらいで計算できたら上出来でしょう。さらにこれを4スレッドぐらいの並列処理で同時に計算したら、3秒ぐらいで終わりそうですが……

実際に処理対象の分布図を描いてみると、ほとんどが無駄な演算になってしまうので、この「無駄な演算」をどれだけ減らせるかが処理速度向上のキーポイントになります。

そこで、最上位桁にあてはまる数字に着目。ここが1つ違うだけでも1万データもかわってくるので、最上位桁だけに着目しただけでも、計算範囲を大幅に絞ることができ、大幅に処理高速化ができそうです。

ちなみに、最上位桁に入る最大の数字が2であれば…………残りの3〜9の可能性はすべて捨てて考えることができるわけで、処理対象データは89999個から19999個へと大幅に減らせます。処理対象データが80%減れば、処理速度はおのずと向上します。

実際に試してみたら、2秒で処理完了。なかなか、いい感じです。

スクリプト名:指定の数字の組み合わせのすべてのパターンをリストアップ v2
set aTime to current date

set aList to {}
set aList_r to a reference to aList

set bList to {}
set bList_r to a reference to bList

set okList to {{1, 2}, {1, 2, 3}, {0, 1}, {1, 2, 3, 4}, {1, 2, 3, 4, 5, 6, 7, 8}}
set okL_r to a reference to okList

set paramLen to length of okList

set maxDigitNum to (last item of first item of okList)

set fromNum to “1″
repeat paramLen - 1 times
  set fromNum to fromNum & “0″
end repeat
set fromNum to fromNum as number

set toNum to maxDigitNum as string
repeat paramLen - 1 times
  set toNum to toNum & “9″
end repeat
set toNum to toNum as number

–指定桁数の数字をジェネレートする
repeat with i from fromNum to toNum
  set the end of aList_r to i
end repeat

–生成した数字リストを評価
repeat with i in aList_r
  set j to i as string
  
set cList to characters of j
  
  
set cLen to length of cList
  
set hitF to true
  
repeat with ii from 1 to cLen
    if (item ii of cList) as number is not in (item ii of okL_r) then
      set hitF to false
      
exit repeat
    end if
  end repeat
  
  
–条件にすべて合ったものだけピックアップ
  
if hitF = true then
    set the end of bList_r to contents of i
  end if
end repeat

set bTIme to current date
set cTIme to bTIme - aTime

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

08/12 数値リストを連続部分に分解する

数値リストを連続する部分ごとに「起点」「終点」ペアのリストに分解します。

InDesignで文字書式を変更する際に、characterを1つずつ処理していては時間の無駄なので、起点と終点を指定して書式を変更する改良を加えた際に使用しました。

本ルーチンの投入により、10倍以上の高速化に成功しました。文字数が多くなればなるほど差がつくはずです。

スクリプト名:数値リストを連続部分に分解する
set cNumList to {40, 41, 42, 43, 44, 45, 46, 47, 48, 63, 64, 85, 86, 88, 89, 90, 107, 108, 109, 110, 111, 112, 113, 114, 115}
set nList to splitNumList(cNumList) of me
–> {{40, 48}, {63, 64}, {85, 86}, {88, 90}, {107, 115}}

set cNumList to {54, 55, 56, 57, 58, 59, 60, 61, 62}
set nList to splitNumList(cNumList) of me
–> {{54, 62}}

–数値リストを、連続する値ごとにペアで「起点」「終点」リストにして返す
on splitNumList(cNumList)
  set bList to detectChangeInNumSeriesList(cNumList) of me
  
  
if length of bList is not equal to 1 then
    set prevItem to contents of first item of bList
    
    
set bList to rest of bList
    
    
set outList to {{1, prevItem}}
    
    
repeat with i in bList
      set j1 to prevItem + 1
      
set j2 to contents of i
      
set the end of outList to {j1, j2}
      
set prevItem to j2
    end repeat
    
    
set aLen to length of cNumList
    
if j2 is not equal to aLen then
      set the end of outList to {j2 + 1, aLen}
    end if
  else
    set outList to {contents of first item of bList}
  end if
  
  
set newList to {}
  
repeat with i in outList
    set j1 to item (item 1 of i) of cNumList
    
set j2 to item (item 2 of i) of cNumList
    
set the end of newList to {j1, j2}
  end repeat
  
  
return newList
  
end splitNumList

–数値リストで、値が連続しないポイントをアイテム番号のリストで返す
on detectChangeInNumSeriesList(aNumList)
  set prevNum to (first item of aNumList)
  
set bNumList to rest of aNumList
  
  
set outList to {}
  
  
set iCount to 1
  
  
repeat with i in bNumList
    set j to contents of i
    
if prevNum = (j - 1) then
      
    else
      set the end of outList to iCount
    end if
    
    
copy j to prevNum
    
set iCount to iCount + 1
  end repeat
  
  
if outList = {} then
    set outList to {{1, length of aNumList}}
  end if
  
  
return outList
  
end detectChangeInNumSeriesList

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

07/23 指定矩形座標内に含まれる座標をピックアップ

指定の矩形座標内に含まれる座標をリスト内からピックアップするAppleScriptです。

{y1, x1, y2, x2} 形式で指定した座標内に含まれる座標(InDesignから取得したgeometric bounds)をピックアップします。

スクリプトラベルのついていないText frameからデータを取り出しを行う必要があったときに、他のスクリプトラベルが付いているオブジェクトからの相対矩形座標内に存在するObjectを求めるようにしてみました。その時に試作したものです。

スクリプト名:指定矩形座標内に含まれる座標をピックアップ
set aList to {{243.0, 17.0, 245.0, 77.0}, {169.000015228534, 12.0, 187.500015228534, 166.999999999992}, {189.000007614311, 13.0, 242.000007614312, 17.0}, {188.999990448174, 17.0, 241.999990448174, 76.999999025133}, {188.999990448174, 87.5, 241.999990448174, 123.5}}

set rangeList to {236.999990448174, 12.0, 246.999990448174, 81.999999025133} –{y1,x1,y2,x2}
set resList to withinRange_Y1X1Y2X2(rangeList, aList) of me

–指定矩形内に含まれるデータをリストで返す
on withinRange_Y1X1Y2X2(rangeList, targetList)
  set includedList to {}
  
  
set y1 to item 1 of rangeList
  
set x1 to item 2 of rangeList
  
set y2 to item 3 of rangeList
  
set x2 to item 4 of rangeList
  
  
repeat with i in targetList
    set targY1 to item 1 of i
    
set targX1 to item 2 of i
    
set targY2 to item 3 of i
    
set targX2 to item 4 of i
    
    
if (x1 < targX1) and (x2 > targX2) and (y1 < targY1) and (y2 > targY2) then
      set the end of includedList to (contents of i)
    end if
  end repeat
  
  
return includedList
end withinRange_Y1X1Y2X2

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に