Archive for the 'NSPredicate' Category

2017/06/18 PDFから本文テキストを抽出して配列にストアして文字列検索

指定PDFで指定キーワードを検索して、キーワードが存在するページのノンブル(数値)のリストを返すAppleScriptの改良強化版です。

最初にPDFからページ単位でテキストを抽出し、テキスト検索キャッシュを作成。このテキスト検索キャッシュに対して検索を実行し、存在しなかったらPDFに対してテキスト検索を行うようにしてみました。

最初からPDFに対してテキスト検索するよりも、テキスト抽出後に検索するほうが、複数キーワードの検索ではスピードが有利になるものと期待しています。

これで不満が出るようなら、AppleScriptで並列処理を行なって処理速度をかせぐしかないでしょう。

AppleScript名:PDFから本文テキストを抽出して配列にストアして文字列検索
– Created 2017-06-18 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “Quartz”
–http://piyocast.com/as/archives/4691

property textCache : missing value
property aList : {}

–検索対象の語群
set sList to {“notification”, “Cocoa”} –considering case

set thePath to POSIX path of (choose file of type {“com.adobe.pdf”})

–PDFのテキスト内容をあらかじめページごとに読み取って、検索用のテキストキャッシュを作成
set anNSURL to (current application’s |NSURL|’s fileURLWithPath:thePath)
set theDoc to current application’s PDFDocument’s alloc()’s initWithURL:anNSURL
set theCount to theDoc’s pageCount() as integer

set textCache to current application’s NSMutableArray’s new()

repeat with i from 0 to (theCount - 1)
  set aPage to (theDoc’s pageAtIndex:i)
  
set tmpStr to (aPage’s |string|())
  (
textCache’s addObject:{pageIndex:i + 1, pageString:tmpStr})
end repeat

–主にテキストキャッシュを対象にキーワード検索
repeat with s in sList
  
  
–❶部分一致で抽出
  
set bRes to ((my filterRecListByLabel1(textCache, “pageString contains ’” & s & “’”))’s pageIndex) as list
  
  
–❷、❶のページ単位のテキスト検索で見つからなかった場合(ページ間でまたがっている場合など)
  
if bRes = {} then
    set bRes to {}
    
set theSels to (theDoc’s findString:s withOptions:0)
    
repeat with aSel in theSels
      set thePage to (aSel’s pages()’s objectAtIndex:0)’s label()
      
set curPage to (thePage as integer)
      
if curPage is not in bRes then
        set the end of bRes to curPage
      end if
    end repeat
  end if
  
  
set the end of aList to bRes
  
end repeat

return aList

–リストに入れたレコードを、指定の属性ラベルの値で抽出
on filterRecListByLabel1(aRecList as list, aPredicate as string)
  set aArray to current application’s NSArray’s arrayWithArray:aRecList
  
set aPredicate to current application’s NSPredicate’s predicateWithFormat:aPredicate
  
set filteredArray to aArray’s filteredArrayUsingPredicate:aPredicate
  
return filteredArray
end filterRecListByLabel1

★Click Here to Open This Script 

2017/06/14 Googleで検索して結果を返す v2

指定キーワードをGoogle検索して該当するWebサイトのURLをリストで返すAppleScriptです。

もともとはShane StanleyがAppleScript Users MLで発表したオリジナル版(v1)があり、これに対して、私が検索件数の拡張(100件まで)を行なったものです(3月の時点でShaneにフィードバックずみ)。

本Scriptは、AppleScriptからCocoaの機能を呼び出す処理を洗練させまくった内容であり、よくこんなもんを考えついて実装したものだと心底感心させられます。いままで見たScriptの中でもダントツに(いろんな意味で)突き抜けています。突き抜けすぎていて、掲載するまで(Googleに刺されないかどうか)かなり悩まされたほどです。

# ほかの言語でも同じような処理をしている例を見つけたので、問題は少ないものと判断

Webブラウザ経由でGoogle検索を行い、結果をAppleScriptで取得するようなやりかたは、短期的には使うことができていましたが、Google側の広告や表示スタイルの変更などによってScriptの定期的な書き換えや修正が必要になっていました。その場で試す分には使えるものの、長期的に使い続けることには疑問が生じていました。

本Scriptでは検索結果のHTMLをXMLとして評価し、Webブラウザ側に表示される各種要素には関係なく検索結果のURLのみを抽出。Webブラウザ経由のGoogle検索の手口を悪魔的に洗練させたものといえます。最大検索件数は100件なので用途は制限されますが、お手軽な検索程度であれば使えます。

本当に業務で長期的にGoogle検索をシステムやツールの一部の機能として利用するのであれば、Google Web API(REST API)経由でGoogle検索エンジンを呼び出すようにすることが望ましいでしょう。AppleScriptからMicrosoftのBing検索エンジンにREST API経由で検索実行することも可能です

AppleScript名:Googleで検索して結果を返す v2
– Created 2017-03-31 by Shane Stanley
– Modified 2017-03-31 by Takaaki Naganoya
use AppleScript version “2.4″
use framework “Foundation”
use scripting additions
–http://piyocast.com/as/archives/4687

set theQuery to “AppleScript”
set aResList to searchByGoogle(theQuery, 100) of me

on searchByGoogle(theQuery, maxNum)
  – build and escape query string
  
set theQuery to current application’s NSString’s stringWithString:theQuery
  
set theQuery to theQuery’s stringByReplacingOccurrencesOfString:space withString:“+”
  
set escQuery to theQuery’s stringByAddingPercentEncodingWithAllowedCharacters:(current application’s NSCharacterSet’s URLQueryAllowedCharacterSet())
  
  
– do search
  
set searchURLString to current application’s NSString’s stringWithFormat_(“https://www.google.com/search?client=safari&rls=en&ie=UTF-8&oe=UTF-8&q=%@&complete=0&num=%@&as_qdr=all”, escQuery, (maxNum as string))
  
set theURL to current application’s |NSURL|’s URLWithString:searchURLString
  
set {theData, theError} to current application’s NSData’s dataWithContentsOfURL:theURL options:0 |error|:(reference)
  
if theData = missing value then error (theError’s localizedDescription() as text)
  
  
– convert to XML
  
set {theXMLDoc, theError} to current application’s NSXMLDocument’s alloc()’s initWithData:theData options:(current application’s NSXMLDocumentTidyHTML) |error|:(reference)
  
if theXMLDoc = missing value then error (theError’s localizedDescription() as text)
  
  
– filter via XPath and predicate
  
set xpathStr to “//*[@class=\”r\”]/a/@href”
  
set {theNodes, theError} to theXMLDoc’s nodesForXPath:xpathStr |error|:(reference)
  
if theNodes = missing value then error (theError’s localizedDescription() as text)
  
set nodeStrings to (theNodes’s valueForKey:“stringValue”)
  
set thePred to current application’s NSPredicate’s predicateWithFormat:“self BEGINSWITH ’/url?q=’”
  
set nodeStrings to nodeStrings’s filteredArrayUsingPredicate:thePred
  
  
– trim URL strings
  
set theURLStrings to {}
  
repeat with aString in nodeStrings
    set theOffset to (aString’s rangeOfString:“&”)
    
set end of theURLStrings to (aString’s substringWithRange:{7, (theOffset’s location) - 7}) as text
  end repeat
  
  
return theURLStrings
end searchByGoogle

★Click Here to Open This Script 

2017/03/15 Spotlightで指定フォルダ以下の指定文字を含むファイル一覧を取得

Cocoaの機能を呼び出すAppleScriptObjCでは、さまざまなCocoaのAPIを呼び出す手法が模索されてきました。その中でもSpotlight検索は「割とまだ決定版の手法が確立していない」ものでありました。

spotlightでタグを指定して検索
http://piyocast.com/as/archives/3731

ASOCでmdfindするじっけん v4(フルパスを返す)
http://piyocast.com/as/archives/4122

などなど、徐々に進化してきて、Shane StanleyのAppleScript Libraries「BridgePlus」にSpotlightの呼び出し命令が実装されたりと、手段が洗練されてきたという経緯があります。

書籍「AppleScript 10大最新技術」にも掲載していますが、その時点のバージョンよりもはるかにこなれた書き方がML上でShane Stanleyから提示されました。いままでよりもはるかに短いので、ちょっと腰を抜かしてしまったほど。しかも、とてもさりげなく、、、

「ああ、こういう書き方でもいいんだ、、、」

と、かなりすごいことになっています。

AppleScript名:Spotlightで指定フォルダ以下の指定文字を含むファイル一覧を取得
– Created 2017-03-15 By Shane Stanley
use AppleScript version “2.4″ – Yosemite (10.10) or later
use framework “Foundation”
use scripting additions
–http://piyocast.com/as/archives/4528

set thePath to POSIX path of (path to desktop) – whatever

–ファイル名で検索
set theKeyword to “スクリーンショット”
set fResList to my searchPath:thePath searchPredicate:“(kMDItemFSName CONTAINS [c]%@)” predicateArgs:{theKeyword}
–>  {”/Users/me/Desktop/2013/03/スクリーンショット 2016-12-15 8.44.28.png”, “/Users/me/Desktop/FromDesktop/samples/list splitted or broken/スクリーンショット 2015-08-31 10.49.30.png”, …… }

–UTI(File Kind)で検索
set theKeyword to “public.png”
set fResList to my searchPath:thePath searchPredicate:“(kMDItemContentType == [c]%@)” predicateArgs:{theKeyword}
–> {”/Users/me/Desktop/2013/03/スクリーンショット 2016-12-15 8.44.28.png”, “/Users/me/Desktop/FromDesktop/IMG_3143_resized.png”, “/Users/me/Desktop/FromDesktop/asoctable.png”….}

–ファイルの内容で検索
set fResList to my searchPath:thePath searchPredicate:“(kMDItemTextContent == [c]%@)” predicateArgs:{“戦場の絆”}
–>  {”/Users/me/Desktop/2013/01/gundam_games_history.txt”, “/Users/me/Desktop/book1_2.0.pdf”}

on searchPath:thePath searchPredicate:predString predicateArgs:argList
  set thePred to current application’s NSPredicate’s predicateWithFormat:predString argumentArray:argList
  
set targetURL to current application’s |NSURL|’s fileURLWithPath:thePath
  
set theQuery to current application’s NSMetadataQuery’s new()
  
  
theQuery’s setPredicate:thePred
  
theQuery’s setSearchScopes:{targetURL}
  
theQuery’s startQuery()
  
  
repeat while theQuery’s isGathering() as boolean
    delay “0.001″ as real
  end repeat
  
theQuery’s stopQuery()
  
  
set theCount to theQuery’s resultCount()
  
set theResults to current application’s NSMutableArray’s array()
  
  
repeat with i from 1 to theCount
    set aResult to (theQuery’s resultAtIndex:(i - 1))
    
set thePath to (aResult’s valueForAttribute:(current application’s NSMetadataItemPathKey))
    (
theResults’s addObject:thePath)
  end repeat
  
  
return (theResults’s sortedArrayUsingSelector:“compare:”) as list
end searchPath:searchPredicate:predicateArgs:

★Click Here to Open This Script 

2016/12/17 ネットワークデバイスからactiveなものだけをピックアップする

ネットワークデバイスから、activeなものだけをピックアップするAppleScriptです。

DHCPのネットワークアドレスのリフレッシュを試みたときに、接続中のネットワークデバイス名が必要になるので、試しに作ってみました。

試作品レベルなので、実用性についてはいろいろ問題があります。

まず、ネットワーク接続が切れているとまともに動かないので、ネットワーク接続確認は別途行なっておく必要があります。

さらに、複数のネットワークインタフェース(USB-Ethernetアダプタと本体内蔵WiFiとか)がアクティブになっている場合の結果が正しいことは保証しません(まだ、このあたりの詰めが必要)。

本プログラムでは、複数返ってきた場合には最初の項目を返してくるので、自分のマシン環境でも「これはいかがなものか」という状態です(USB-Ethernetアダプタで有線接続しつつ、位置情報を取得するためにWiFiをオンにすることがあるので)。

正規表現の鬼のような人だと、もう少し美しく短いプログラムにまとめられると思います。自分が短く書くために選んだ道具はtext item delimiters。

text item delimitersに複数(2よりも多い複数)の項目を指定できるので、あらかじめ各デバイス情報の1行目だけをピックアップしておき、これをtext item delimitersに指定してifconfigの処理結果をリスト化し、欠けた各デバイス情報の1行目を順次補っていくという、かなり頭のおかしなプログラムです。

AppleScript名:ネットワークデバイスからactiveなものだけをピックアップする
– Created 2016-12-17 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4367

set aDevRes to getActiveNetworkInterfaceDeviceName() of me
set aDevName to devName of aDevRes
–>  ”en3″

–ifconfigからactiveなデバイス名だけをピックアップする
on getActiveNetworkInterfaceDeviceName()
  set aRes to (do shell script “ifconfig”)
  
set aList to paragraphs of aRes
  
  
set devNameList to {}
  
repeat with i in aList
    set j to contents of i
    
set bRes to regexMatches(j, “^[^\\t]*”) of me
    
if bRes is not equal to {{“”}} then
      set the end of devNameList to contents of item 1 of item 1 of bRes
    end if
  end repeat
  
  
–Parse ifconfig results by device name list
  
set curDelim to AppleScript’s text item delimiters
  
set AppleScript’s text item delimiters to devNameList –かなりトリッキー
  
set tmpList to text items of aRes
  
set AppleScript’s text item delimiters to curDelim
  
  
–initialize
  
set bList to rest of tmpList –remove blank item at top
  
set aCount to 1
  
set aResList to current application’s NSMutableArray’s alloc()’s init()
  
  
repeat with i in bList
    set aCon to contents of i
    
set aDat to contents of item aCount of devNameList
    
–Device Name
    
set aDevName to retStrFromTopToAstr(aDat, “:”) of me
    
    
set bCon to paragraphs 2 thru -1 of aCon
    
set cCon to aDat & retDelimedText(bCon, return) of me
    
    
–Activeなdeviceを抽出するための条件付け
    
set activeF1 to cCon contains “status: active”
    
set activeF2 to cCon does not contain “media: autoselect (<unknown type>)”
    
    
set aRec to (current application’s NSDictionary’s dictionaryWithDictionary:{devName:aDevName, isActive:(activeF1 and activeF2), ifconfigRes:cCon})
    (
aResList’s addObject:aRec)
    
set aCount to aCount + 1
  end repeat
  
  
set activeIF to filterRecListByLabel(aResList, “isActive == [c]%@”, {true}) of me
  
(*
  {{devName:”en3″, ifconfigRes:”en3: flags=8863 mtu 1500\toptions=4\r\tether XX:Xx:xX:Xx:XX:xX \r\tinet6 xxXX::XxX:XXXX:XXXx:Xxxx%en3 prefixlen 64 secured scopeid 0×4 \r\tinet 192.168.0.5 netmask 0xffffff00 broadcast 192.168.0.255\r\tinet6 XXXx:XX:XXxX:X:xxX:XXXX:XXXx:XXxX prefixlen 64 autoconf secured \r\tinet6 XXXx:XX:XXxX:X:XXx:XXXX:XXXx:xXXx prefixlen 64 autoconf temporary \r\tnd6 options=201 \r\tmedia: autoselect (100baseTX )\r\tstatus: active”, isActive:true}}
*)
  return first item of activeIF –Multiple Device Name list may return. Now, I choose one.
end getActiveNetworkInterfaceDeviceName

–http://qiita.com/szk-3/items/8bbe841eb0295caee6b0
on regexMatches(aText as text, pattern as text)
  set regularExpression to current application’s NSRegularExpression’s regularExpressionWithPattern:pattern options:0 |error|:(missing value)
  
set aString to current application’s NSString’s stringWithString:aText
  
set matches to regularExpression’s matchesInString:aString options:0 range:{location:0, |length|:aString’s |length|()}
  
set matchResultList to {}
  
repeat with match in matches
    set mRes to {}
    
repeat with i from 0 to (match’s numberOfRanges as integer) - 1
      set end of mRes to (aString’s substringWithRange:(match’s rangeAtIndex:i)) as text
    end repeat
    
set end of matchResultList to mRes
  end repeat
  
return matchResultList
end regexMatches

–リストを指定デリミタを入れつつテキスト化
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 retStrFromTopToAstr(aStr, aTarg)
  set aLen to length of aTarg
  
set anOffset to offset of aTarg in aStr
  
set bStr to text 1 thru (anOffset - aLen) of aStr
  
return bStr
end retStrFromTopToAstr

–リストに入れたレコードを、指定の属性ラベルの値で抽出(predicateとパラメータを分離)
on filterRecListByLabel(aRecList, aPredicate, aParam)
  set aArray to current application’s NSArray’s arrayWithArray:aRecList
  
set aPredicate to current application’s NSPredicate’s predicateWithFormat:aPredicate argumentArray:aParam
  
set filteredArray to aArray’s filteredArrayUsingPredicate:aPredicate
  
set bList to filteredArray as list
  
return bList
end filterRecListByLabel

★Click Here to Open This Script 

2016/06/18 指定フォルダ以下にあるMacDownとPages書類をソートしてPDFに書き出す

指定フォルダ以下にあるMacDownで記述したMarkdown書類と、Pages書類をすべての階層からピックアップしてファイル名でソートして、すべてデスクトップにPDFで書き出すAppleScriptです。

コンパイル(構文確認)および実行に際しては、Shane StanleyのScript Library「Bridge Plus」をインストールしておく必要があります。また、GUI Scriptingを利用しているため「システム環境設定」>「セキュリティとプライバシー」>「プライバシー」>「アクセシビリティ」でスクリプトエディタ(アプレットとして実行する場合にはアプレットそのもの)を登録して許可しておく必要があります。

「技術書典」に出す電子ブックのフォーマットがギリギリまで決まらず、しかも縦長のスクロールさせるタイプのものにできないかとあがいていたのですが、結局iPadあたりで読むことを考慮するとiPadのリーダーの仕様にしたがう必要があります。

……あれ?(^ー^; 結局、ページめくりは発生するし、一般的な本と同じような体裁になってしまいますよ → フォーマットがPDFになりました。

Markdown書類とPages書類が混在しているフォルダ構造のトップ階層のフォルダを指定すると、Markdown書類とPages書類をピックアップし、ファイル名でそれらをソートし、順次PDFに書き出すAppleScriptを書いてみました(必要は発明のマザー!)。

book1.png

ただ、MacDownには「書類をPDFに書き出す」という機能がAppleScript側に公開されていません(T_T)。

macdown_dict.png

ないものを「ないない」と嘆いても仕方がないので、さっさとGUI Scriptingで強制的にメニュー操作することにしました。

macdown_gui.png

で、どこに? どこに保存させるのでしょう??

大丈夫! そんなときには、保存ダイアログで幾つかのフォルダに強制的に移動させるキーボードショートカットが存在しており、Command-Dは「カレントディレクトリをデスクトップに移動させる」=「保存先をデスクトップにする」働きをします。

このため、保存先を操作しづらい(不可能とはいいませんけれども)GUI Scriptingにおいて保存先を指定することが、デスクトップフォルダについては可能になっています。

Pagesの方はひじょうに素直に(GUI Scriptingなんて使わずに)PDF書き出しが可能です。

そんなわけで、時間に追い詰められながらもなんとか大量のデータ処理を行っているのでありました。書き出した大量のPDFもAppleScriptでさくっと連結できるので、非常にいい感じです。あとは、本が完成すれば、、、、

AppleScript名:指定フォルダ以下にあるMDとPagesをソートしてPDFに書き出す
– Created 2016-06-17 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use BridgePlus : script “BridgePlus”
–use spotLight : script “spotlightLib”

property searchRes : {}

load framework

set origPath to POSIX path of (choose folder with prompt “Markdown/Pages ファイルの入っているフォルダを選択”)

set aResList to (spotlightSearch(origPath, “kMDItemKind == ’Markdown’ || kMDItemKind == ’Pages 一般書類’”) of me) as list –Caution! this parameter is *localized*

–フルパスとファイル名のペアの2D Listを作成
set newList to {}
repeat with i in aResList
  set j to contents of i
  
set aStr to (current application’s NSString’s stringWithString:j)
  
set aFileName to aStr’s lastPathComponent()
  
set the end of newList to {aStr, aFileName}
end repeat

–番号順にソート
set sortIndexes to {1} –Key Item id: begin from 0, Sort by filename
set sortOrders to {true}
set sortTypes to {“compare:”}
set resList to (current application’s SMSForder’s subarraysIn:(newList) sortedByIndexes:sortIndexes ascending:sortOrders sortTypes:sortTypes |error|:(missing value)) as list
if resList = {} then return –No Result

–ソートした順番にMarkdownファイル/Pages書類ファイルをオープンしてデスクトップにPDF生成してクローズ
repeat with i in newList
  copy i to {fullPath, aFileName}
  
  
set apFile to (POSIX file (fullPath as string))
  
set anAlias to apFile as alias
  
set aFileName to aFileName as string
  
  
if aFileName ends with “.md” then
    exportFromMacDown(anAlias) of me –Markdown
  else if aFileName ends with “.pages” then
    exportFromPages(anAlias) of me –Pages
  end if
end repeat

–指定のPagesファイル(alias)をデスクトップ上にPDFで書き出し
on exportFromPages(anAlias)
  tell application “Finder”
    set aName to name of anAlias
  end tell
  
  
set dtPath to (path to desktop) as string
  
set outPath to dtPath & aName & “.pdf”
  
  
tell application “Pages”
    close every document without saving
    
open anAlias
    
export document 1 to file outPath as PDF with properties {image quality:Best}
    
close every document without saving
  end tell
end exportFromPages

–指定のMacDownファイル(alias)をデスクトップ上にPDFで書き出し
on exportFromMacDown(anAlias)
  tell application “MacDown”
    open {anAlias}
  end tell
  
  
tell current application
    delay 1 –ここの時間待ちが少ないと画像抜けが発生?
  end tell
  
macDownForceSave() of me
  
  
tell application “MacDown”
    close every document without saving
  end tell
end exportFromMacDown

–注意!! ここでGUI Scriptingを使用。バージョンが変わったときにメニュー階層などの変更があったら書き換え
on macDownForceSave()
  activate application “MacDown”
  
tell application “System Events”
    tell process “MacDown”
      – File > Export > PDF
      
click menu item 2 of menu 1 of menu item 14 of menu 1 of menu bar item 3 of menu bar 1
      
      
–Go to Desktop Folder
      
keystroke “d” using {command down}
      
      
–Save Button on Sheet
      
click button 1 of sheet 1 of window 1
    end tell
  end tell
end macDownForceSave

–Spotlight Libの内容を引っ張り出してきた
on spotlightSearch(origPOSIXpath, aCondition)
  
  
set searchRes to {} –initialize
  
  
initiateSearchForFullPath(aCondition, origPOSIXpath) –Predicate & Scope Directory
  
  
–Waiting for the result
  
repeat while searchRes = {}
    current application’s NSThread’s sleepForTimeInterval:(“0.001″ as real) –delay 0.001
  end repeat
  
  
set anObj to searchRes’s firstObject() –Pick up the first one for test
  
if anObj = missing value then return {} –No Result
  
  
–set anAttrList to anObj’s attributes() –”mdls” attributes
  
–>  (NSArray) {”kMDItemContentTypeTree”, “kMDItemContentType”, “kMDItemPhysicalSize”, …}
  
  
set resArray to {}
  
repeat with anItem in my searchRes
    set j to contents of anItem
    
set aPath to (j’s valueForAttribute:“kMDItemPath”) as string
    
set the end of resArray to aPath
  end repeat
  
  
return resArray
end spotlightSearch

on initiateSearchForFullPath(aQueryStrings, origPath)
  
  
set aSearch to current application’s NSMetadataQuery’s alloc()’s init()
  
  
current application’s NSNotificationCenter’s defaultCenter()’s addObserver:(me) selector:“queryDidUpdate:” |name|:(current application’s NSMetadataQueryDidUpdateNotification) object:aSearch
  
current application’s NSNotificationCenter’s defaultCenter()’s addObserver:(me) selector:“initalGatherComplete:” |name|:(current application’s NSMetadataQueryDidFinishGatheringNotification) object:aSearch
  
  
set aPredicate to current application’s NSPredicate’s predicateWithFormat:aQueryStrings
  
aSearch’s setPredicate:aPredicate
  
  
set aScope to current application’s NSArray’s arrayWithObjects:{origPath}
  
aSearch’s setSearchScopes:aScope
  
  
set sortKeys to current application’s NSSortDescriptor’s sortDescriptorWithKey:“kMDItemFSName” ascending:true
  
aSearch’s setSortDescriptors:(current application’s NSArray’s arrayWithObject:sortKeys)
  
  
aSearch’s startQuery()
  
end initiateSearchForFullPath

on queryDidUpdate:sender
  –  
end queryDidUpdate:

on initalGatherComplete:sender
  set anObject to sender’s object
  
anObject’s stopQuery()
  
current application’s NSNotificationCenter’s defaultCenter()’s removeObserver:me |name|:(current application’s NSMetadataQueryDidUpdateNotification) object:anObject
  
current application’s NSNotificationCenter’s defaultCenter()’s removeObserver:me |name|:(current application’s NSMetadataQueryDidFinishGatheringNotification) object:anObject
  
set my searchRes to anObject’s results()
end initalGatherComplete:

★Click Here to Open This Script 

2016/06/17 ASOCでmdfindするじっけん v4(フルパスを返す)

Spotlightの機能をCocoa経由で呼び出してmdfindコマンドのクエリーと検索対象フォルダをPOSIX pathで与えると、条件に合致するファイルのPOSIX pathをリストに入れて返してくるAppleScriptです。

以前に掲載したバージョンを実際に使おうとしたら、激しく間違っていることを発見。なんで気がつかなかったんでしょう。いえ、なんでこんな忙しいときに気づいてしまうんでしょう。

AppleScript名:ASOCでmdfindするじっけん v4(フルパスを返す)
– Created 2016-06-17 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

property searchRes : {}

set origPath to POSIX path of (choose folder)
set aList to spotlightSearch(origPath, “kMDItemContentType == ’net.daringfireball.markdown’”) of me

on spotlightSearch(origPath, aMDfindParam)
  
  
set my searchRes to {} –initialize
  
initiateSearchForFullPath(aMDfindParam, origPath) –Predicate & Scope Directory
  
  
–Waiting for the result
  
repeat while my searchRes = {}
    delay 0.01
  end repeat
  
  
set anObj to my searchRes’s firstObject() –Pick up the first one for test
  
if anObj = missing value then return {} –No Result
  
  
set resArray to {}
  
repeat with anItem in my searchRes
    set j to contents of anItem
    
set aPath to (j’s valueForAttribute:“kMDItemPath”) as string
    
set the end of resArray to aPath
  end repeat
  
  
return resArray
  
end spotlightSearch

on initiateSearchForFullPath(aQueryStrings, origPath)
  
  
set aSearch to current application’s NSMetadataQuery’s alloc()’s init()
  
  
current application’s NSNotificationCenter’s defaultCenter()’s addObserver:(me) selector:“queryDidUpdate:” |name|:(current application’s NSMetadataQueryDidUpdateNotification) object:aSearch
  
current application’s NSNotificationCenter’s defaultCenter()’s addObserver:(me) selector:“initalGatherComplete:” |name|:(current application’s NSMetadataQueryDidFinishGatheringNotification) object:aSearch
  
  
set aPredicate to current application’s NSPredicate’s predicateWithFormat:aQueryStrings
  
aSearch’s setPredicate:aPredicate
  
  
set aScope to current application’s NSArray’s arrayWithObjects:{origPath}
  
aSearch’s setSearchScopes:aScope
  
  
set sortKeys to current application’s NSSortDescriptor’s sortDescriptorWithKey:“kMDItemFSName” ascending:true
  
aSearch’s setSortDescriptors:(current application’s NSArray’s arrayWithObject:sortKeys)
  
  
aSearch’s startQuery()
  
end initiateSearchForFullPath

on queryDidUpdate:sender
  log sender
  
–> (NSConcreteNotification) NSConcreteNotification 0×7fa618450820 {name = NSMetadataQueryDidFinishGatheringNotification; object = }
end queryDidUpdate:

on initalGatherComplete:sender
  set anObject to sender’s object
  
anObject’s stopQuery()
  
current application’s NSNotificationCenter’s defaultCenter()’s removeObserver:me |name|:(current application’s NSMetadataQueryDidUpdateNotification) object:anObject
  
current application’s NSNotificationCenter’s defaultCenter()’s removeObserver:me |name|:(current application’s NSMetadataQueryDidFinishGatheringNotification) object:anObject
  
  
set my searchRes to anObject’s results()
end initalGatherComplete:

★Click Here to Open This Script 

2015/12/08 都道府県リストから隣接都道府県を含む該当のコードを抽出する

都道府県コードを指定すると、「当該都道府県」+「隣接都道府県」のコードリストを抽出するAppleScriptです。

休みの日に、

「全国の『戦場の絆』が導入されているゲーセンの最寄駅までの距離をすべて計算し、一番辺鄙(へんぴ)な場所にあるゲーセンを算出しよう!」

と思い立ち(余計なお世話)、日本全国の駅のリスト(9,000件以上)を「駅データ.jp」から取得し、全国のゲーセンの住所(から算出した緯度、経度情報)と距離を測って最寄り駅までの距離を求めてみました。

farestgamecenter.png
▲最寄駅が最も遠いゲームセンター「サンゲームス鹿屋」。最寄駅まで23km。ただし、地元の人たちは車で移動しているのでまったく問題ないことが航空写真からもわかる

最初に組んだバージョンは、780箇所ぐらいのゲーセンと日本全国の駅(9,000件以上)の距離をすべて計算する仕様になっていたので、距離計算にCore i7 2.6GHzのMacBook Pro Retinaでも90分ぐらいかかりました(さすがに700万回距離計算したので)。手っ取り早くプログラムを組むことを重視したので、膨大な計算時間も仕方ないところですが、これではとうてい実用的とはいえません。

free_034_005.png

何も東京のド真ん中にある地点と北海道の最果てにある駅の距離を計測しなくても、そこが「最寄駅」ではないことぐらいは明白なので(ただし、用途による)、東京+隣接する都道府県だけに検索範囲をしぼってあげることで大幅な計算の高速化が行えるだろう、とあたりをつけ、47都道府県の隣接都道府県のテーブル(これ)を作ってみました。

次に、このテーブルを用いて「ゲーセンの所在する県+まわりの都道府県(隣接都道府県)」だけの距離計算を行うようにして、実際に圧倒的に演算量を減らすことに成功。わずか10分強で計算完了しました。

さらに、「毎回駅データの抽出を行わずに、県ごとにループして最初だけ抽出処理」するようにしたら、5分ぐらいで計算が完了。当初の90分から大幅な高速化を行うことができました。

最寄駅を求める、といったぐらいの演算であれば、こうして工夫することで計算量を大幅に減らせるので便利です。

目下、かんたんに複数CPUコアに処理を割り振る並列処理化AppleScriptを整備してあり、さまざまな並列処理タスクを実行した経験からいえば・・・4コア8スレッドのCore i7のMacBook Proで並列処理すれば、シングルコアでの実行時間(5分)の半分程度(2〜3分)の処理時間にはなるものと予想されます。

それよりも多いコア数(Mac Proなど)で並列処理実行した場合の性能は不明ですが、とくにノート型では複数コアで並列処理した際に、コアの温度上昇による処理能力の全体的な低下(サーマルスロットリング)が顕著であり、期待値どおりの処理性能を引き出すことは困難です(システム全体では処理能力に余裕があっても、負荷を増やしてすべてを埋め尽くすのは難しい)。

このため、デスクトップ型用のマルチコアCPUで処理を行うと、ノート型用のCPUよりも顕著な処理性能の向上が期待できます(持ってないので、やったことがないですけれども)。

用途によっては、長崎県と大分県は陸路でつながっているので、隣接県として扱ってもよいかもしれないですし、鹿児島県の一部は沖縄県と近いので隣接県として扱っておく必要があるかもしれません。そのあたりは「用途次第」でしょう。さらに、北海道新幹線の開業によって、北海道と青森県が隣接している、とみなすことも可能に。

AppleScript名:都道府県リストから隣接都道府県を含む該当のコードを抽出する
– Created 2015-12-06 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use BridgePlus : script “BridgePlus”

property prefList : {{prefCode:1, prefName:“北海道”, neighbors:{}}, {prefCode:2, prefName:“青森県”, neighbors:{3, 5}}, {prefCode:3, prefName:“岩手県”, neighbors:{2, 4, 5}}, {prefCode:4, prefName:“宮城県”, neighbors:{3, 5, 6, 7}}, {prefCode:5, prefName:“秋田県”, neighbors:{2, 3, 4, 6}}, {prefCode:6, prefName:“山形県”, neighbors:{3, 4, 5, 7, 15}}, {prefCode:7, prefName:“福島県”, neighbors:{4, 6, 8, 9, 10, 15}}, {prefCode:8, prefName:“茨城県”, neighbors:{7, 9, 10, 11, 12}}, {prefCode:9, prefName:“栃木県”, neighbors:{7, 8, 10, 11, 12}}, {prefCode:10, prefName:“群馬県”, neighbors:{7, 9, 11, 15, 20}}, {prefCode:11, prefName:“埼玉県”, neighbors:{8, 9, 10, 12, 13, 19, 20}}, {prefCode:12, prefName:“千葉県”, neighbors:{8, 11, 13}}, {prefCode:13, prefName:“東京都”, neighbors:{11, 12, 19, 14}}, {prefCode:14, prefName:“神奈川県”, neighbors:{13, 19, 22}}, {prefCode:15, prefName:“新潟県”, neighbors:{6, 7, 10, 16, 20}}, {prefCode:16, prefName:“富山県”, neighbors:{15, 17, 20, 21}}, {prefCode:17, prefName:“石川県”, neighbors:{16, 18, 21}}, {prefCode:18, prefName:“福井県”, neighbors:{17, 21, 25, 26}}, {prefCode:19, prefName:“山梨県”, neighbors:{11, 13, 14, 20, 22}}, {prefCode:20, prefName:“長野県”, neighbors:{10, 11, 15, 16, 19, 21, 22, 23}}, {prefCode:21, prefName:“岐阜県”, neighbors:{16, 17, 18, 20, 23, 24, 25}}, {prefCode:22, prefName:“静岡県”, neighbors:{14, 19, 20, 23}}, {prefCode:23, prefName:“愛知県”, neighbors:{20, 21, 22, 24}}, {prefCode:24, prefName:“三重県”, neighbors:{21, 23, 25, 26, 29}}, {prefCode:25, prefName:“滋賀県”, neighbors:{18, 21, 24, 26}}, {prefCode:26, prefName:“京都府”, neighbors:{18, 24, 25, 27, 28, 29}}, {prefCode:27, prefName:“大阪府”, neighbors:{26, 29, 28, 30}}, {prefCode:28, prefName:“兵庫県”, neighbors:{26, 27, 31, 33}}, {prefCode:29, prefName:“奈良県”, neighbors:{24, 25, 26, 27, 30}}, {prefCode:30, prefName:“和歌山県”, neighbors:{24, 27, 29}}, {prefCode:31, prefName:“鳥取県”, neighbors:{28, 33, 32, 34}}, {prefCode:32, prefName:“島根県”, neighbors:{31, 34, 35}}, {prefCode:33, prefName:“岡山県”, neighbors:{28, 31, 34}}, {prefCode:34, prefName:“広島県”, neighbors:{33, 31, 32, 35}}, {prefCode:35, prefName:“山口県”, neighbors:{32, 34}}, {prefCode:36, prefName:“徳島県”, neighbors:{37, 39}}, {prefCode:37, prefName:“香川県”, neighbors:{36, 38, 39}}, {prefCode:38, prefName:“愛媛県”, neighbors:{37, 39}}, {prefCode:39, prefName:“高知県”, neighbors:{36, 37, 38}}, {prefCode:40, prefName:“福岡県”, neighbors:{44, 43, 41}}, {prefCode:41, prefName:“佐賀県”, neighbors:{40, 42}}, {prefCode:42, prefName:“長崎県”, neighbors:{41}}, {prefCode:43, prefName:“熊本県”, neighbors:{40, 42, 44, 45, 46}}, {prefCode:44, prefName:“大分県”, neighbors:{40, 43, 45}}, {prefCode:45, prefName:“宮崎県”, neighbors:{43, 44, 46}}, {prefCode:46, prefName:“鹿児島県”, neighbors:{43, 45}}, {prefCode:47, prefName:“沖縄県”, neighbors:{}}}

set aPref to 13 –Tokyo
set aRes to my filterRecListByLabel2(prefList, “prefCode == [c]%@”, {aPref})
set targList to neighbors of aRes & aPref
–>  {11, 12, 19, 14, 13}

–リストに入れたレコードを、指定の属性ラベルの値で抽出(predicateとパラメータを分離)し、1つのアイテムだけを返す
on filterRecListByLabel2(aRecList as list, aPredicate as string, aParam)
  set aArray to current application’s NSArray’s arrayWithArray:aRecList
  
set aPredicate to current application’s NSPredicate’s predicateWithFormat:aPredicate argumentArray:aParam
  
set filteredArray to aArray’s filteredArrayUsingPredicate:aPredicate
  
set bList to ASify from filteredArray as list
  
set cList to first item of bList
  
return cList
end filterRecListByLabel2

★Click Here to Open This Script 

2015/12/08 データリストに合致するものを抽出

レコードのリストから、指定フィールドに指定した複数の値に該当するレコードを抽出するAppleScriptです。

NSPredicateを用いて、さまざまな凝った抽出条件が指定できますが、”IN”演算子であとにリストを指定するやり方が面倒だったので、NSCompoundPredicateにOR条件で複数条件を合成して検索するようにしてみました。

AppleScript名:データリストに合致するものを抽出
– Created 2015-12-06 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use BridgePlus : script "BridgePlus"

set prefList to {{station_name_k:"", lon:"140.726413", add:"北海道函館市若松町12-13", station_cd:"1110101", lat:"41.773709", e_sort:"1110101", close_ymd:"", station_g_cd:"1110101", station_name:"函館", e_status:"0", line_cd:"11101", open_ymd:"1902-12-10", station_name_r:"", post:"040-0063", pref_cd:"1"}, {station_name_k:"", lon:"140.733539", add:"函館市亀田本町", station_cd:"1110102", lat:"41.803557", e_sort:"1110102", close_ymd:"", station_g_cd:"1110102", station_name:"五稜郭", e_status:"0", line_cd:"11101", open_ymd:"", station_name_r:"", post:"041-0813", pref_cd:"2"}}

set aRes to my filterDictArrayByLabel3(prefList, "pref_cd == %@", {"1"})
–>  {{post:"040-0063", lon:"140.726413", add:"北海道函館市若松町12-13", station_cd:"1110101", lat:"41.773709", e_sort:"1110101", close_ymd:"", station_g_cd:"1110101", station_name:"函館", e_status:"0", line_cd:"11101", open_ymd:"1902-12-10", station_name_r:"", pref_cd:"1", station_name_k:""}}

set bRes to my filterDictArrayByLabel3(prefList, "pref_cd == %@", {"1", "2"})
–>  {{post:"040-0063", lon:"140.726413", add:"北海道函館市若松町12-13", station_cd:"1110101", lat:"41.773709", e_sort:"1110101", close_ymd:"", station_g_cd:"1110101", station_name:"函館", e_status:"0", line_cd:"11101", open_ymd:"1902-12-10", station_name_r:"", pref_cd:"1", station_name_k:""}, {post:"041-0813", lon:"140.733539", add:"函館市亀田本町", station_cd:"1110102", lat:"41.803557", e_sort:"1110102", close_ymd:"", station_g_cd:"1110102", station_name:"五稜郭", e_status:"0", line_cd:"11101", open_ymd:"", station_name_r:"", pref_cd:"2", station_name_k:""}}

–リストに入れたレコードを、指定の属性ラベルの値で抽出(predicateとパラメータを分離、複数条件をORでつなぐ)
on filterDictArrayByLabel3(aList, aPredicate as string, aParam)
  set aArray to current application’s NSArray’s arrayWithArray:aList
  
set predList to {}
  
repeat with i in aParam
    set j to i as text
    
set the end of predList to (current application’s NSPredicate’s predicateWithFormat:aPredicate argumentArray:{j})
  end repeat
  
set pred to current application’s NSCompoundPredicate’s orPredicateWithSubpredicates:predList
  
set filteredArray to aArray’s filteredArrayUsingPredicate:pred
  
set bList to ASify from filteredArray as list
  
return bList
end filterDictArrayByLabel3

★Click Here to Open This Script 

2015/11/07 Systemのアラートサウンド名称を取得して鳴らす v2

Cocoaの機能を無理やり使って、Systemに入っている警告音の名称一覧を取得して鳴らすAppleScriptです。

Shane Stanleyから、「Script中で使っているgetFilePathListFromPOSIXpathの仕様だと、OS X 10.11では動くが10.10ではエラーになるよ」との指摘があり、書き換え例(v2)がナイスな内容だったので、少し試してみました(v2.1)。

NSArrayの中に入っている全要素を加工しつつ別のArrayに出力する(しかもループしない)という書き方はスマートなので、ぜひ覚えておきたいと思います。

AppleScript名:ASOCでSystemのアラートサウンド名称を取得して鳴らす v2
– Created 2015-11-06 by Takaaki Naganoya
– Modified 2015-11-07 by Shane Stanley–Recovery the compatibility for OS X 10.10.x
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”

set sList to getSystemAlertSoundNames() of me
–>  {”Basso.aiff”, “Blow.aiff”, “Bottle.aiff”, “Frog.aiff”, “Funk.aiff”, “Glass.aiff”, “Hero.aiff”, “Morse.aiff”, “Ping.aiff”, “Pop.aiff”, “Purr.aiff”, “Sosumi.aiff”, “Submarine.aiff”, “Tink.aiff”}

repeat with i in sList
  tell (current application’s NSSound’s soundNamed:i) to play()
  
delay 0.5 –時間待ちしないと全部一緒に鳴るので(Let’s Challenge!)
end repeat

–Get System Alert FileName List
on getSystemAlertSoundNames()
  set aExt to “aiff” – no dot
  
set aFol to “/System/Library/Sounds”
  
set fList to getFileNameListFromPOSIXpath(aFol, aExt) of me
  
return fList
end getSystemAlertSoundNames

–Get File Name List from POSIX path and file Extensions
on getFileNameListFromPOSIXpath(aFol, aExt)
  set aFM to current application’s NSFileManager’s defaultManager()
  
set aURL to current application’s |NSURL|’s fileURLWithPath:aFol
  
set urlArray to aFM’s contentsOfDirectoryAtURL:aURL includingPropertiesForKeys:{} options:(current application’s NSDirectoryEnumerationSkipsHiddenFiles) |error|:(missing value)
  
set thePred to current application’s NSPredicate’s predicateWithFormat:“pathExtension == [c]%@” argumentArray:{aExt}
  
set anArray to urlArray’s filteredArrayUsingPredicate:thePred
  
return (anArray’s valueForKey:“lastPathComponent”) as list –便利!!
end getFileNameListFromPOSIXpath

★Click Here to Open This Script 

AppleScript名:ASOCでSystemのアラートサウンド名称を取得して鳴らす v2.1
– Created 2015-11-06 by Takaaki Naganoya
– Modified 2015-11-07 by Shane Stanley–Recovery the compatibility for OS X 10.10.x
– Modified 2015-11-07 by Takaaki Naganoya–NSArrayからvalueForKeyで加工しつつ一気にArrayで値を返す内容を検証
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”

set sList to getSystemAlertSoundNames() of me
–>  {”Basso”, “Blow”, “Bottle”, “Frog”, “Funk”, “Glass”, “Hero”, “Morse”, “Ping”, “Pop”, “Purr”, “Sosumi”, “Submarine”, “Tink”}

repeat with i in sList
  tell (current application’s NSSound’s soundNamed:i) to play()
  
delay 0.5 –時間待ちしないと全部一緒に鳴るので(Let’s Challenge!)
end repeat

–Get System Alert FileName List
on getSystemAlertSoundNames()
  set aExt to “aiff” – no dot
  
set aFol to “/System/Library/Sounds”
  
set fList to getFileNameListFromPOSIXpath(aFol, aExt) of me
  
return fList
end getSystemAlertSoundNames

–Get File Name List from POSIX path and file Extensions
on getFileNameListFromPOSIXpath(aFol, aExt)
  set aFM to current application’s NSFileManager’s defaultManager()
  
set aURL to current application’s |NSURL|’s fileURLWithPath:aFol
  
set urlArray to aFM’s contentsOfDirectoryAtURL:aURL includingPropertiesForKeys:{} options:(current application’s NSDirectoryEnumerationSkipsHiddenFiles) |error|:(missing value)
  
set thePred to current application’s NSPredicate’s predicateWithFormat:“pathExtension == [c]%@” argumentArray:{aExt}
  
set anArray to urlArray’s filteredArrayUsingPredicate:thePred
  
set bArray to (anArray’s valueForKey:“lastPathComponent”) –便利!!
  
set cArray to (bArray’s valueForKey:“stringByDeletingPathExtension”) –便利!!
  
return cArray as list
end getFileNameListFromPOSIXpath

★Click Here to Open This Script 

AppleScript名:ASOCでSystemのアラートサウンド名称を取得して鳴らす v2.2
– Created 2015-11-06 by Takaaki Naganoya
– Modified 2015-11-07 by Shane Stanley–Recovery the compatibility for OS X 10.10.x
– Modified 2015-11-07 by Takaaki Naganoya–NSArrayからvalueForKeyで加工しつつ一気にArrayで値を返す内容を検証
– Modified 2015-11-07 by Shane Stanley–Cooler processing
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”

set sList to getSystemAlertSoundNames() of me
–>  {”Basso”, “Blow”, “Bottle”, “Frog”, “Funk”, “Glass”, “Hero”, “Morse”, “Ping”, “Pop”, “Purr”, “Sosumi”, “Submarine”, “Tink”}

repeat with i in sList
  tell (current application’s NSSound’s soundNamed:i) to play()
  
delay 0.5 –時間待ちしないと全部一緒に鳴るので(Let’s Challenge!)
end repeat

–Get System Alert FileName List
on getSystemAlertSoundNames()
  set aExt to “aiff” – no dot
  
set aFol to “/System/Library/Sounds”
  
set fList to getFileNameListFromPOSIXpath(aFol, aExt) of me
  
return fList
end getSystemAlertSoundNames

–Get File Name List from POSIX path and file Extensions
on getFileNameListFromPOSIXpath(aFol, aExt)
  set aFM to current application’s NSFileManager’s defaultManager()
  
set aURL to current application’s |NSURL|’s fileURLWithPath:aFol
  
set urlArray to aFM’s contentsOfDirectoryAtURL:aURL includingPropertiesForKeys:{} options:(current application’s NSDirectoryEnumerationSkipsHiddenFiles) |error|:(missing value)
  
set thePred to current application’s NSPredicate’s predicateWithFormat:“pathExtension == [c]%@” argumentArray:{aExt}
  
set anArray to urlArray’s filteredArrayUsingPredicate:thePred
  
set bArray to (anArray’s valueForKeyPath:“lastPathComponent.stringByDeletingPathExtension”) –Cool !!
  
return bArray as list
end getFileNameListFromPOSIXpath

★Click Here to Open This Script 

2015/11/06 Systemのアラートサウンド名称を取得して鳴らす

Cocoaの機能を無理やり使って、Systemに入っている警告音の名称一覧を取得して鳴らすAppleScriptです。

sounds.png

そういう「OSの警告音一覧を取得する」といったサービスがあるのかと思って探していたら、どうも存在していない雰囲気なので、Cocoaの機能を使ってものすごく常識的かつ地道に/System/Library/Soundsの中に入っているaiffファイルのファイル名の一覧を取得というところに落ち着きました。

AppleScript名:ASOCでSystemのアラートサウンド名称を取得して鳴らす
– Created 2015-11-06 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"
use framework "AppKit"

set sList to getSystemAlertSoundNames() of me
–>  {"Basso", "Blow", "Bottle", "Frog", "Funk", "Glass", "Hero", "Morse", "Ping", "Pop", "Purr", "Sosumi", "Submarine", "Tink"}

repeat with i in sList
  tell (current application’s NSSound’s soundNamed:i) to play()
  
delay 0.5 –時間待ちしないと全部一緒に鳴るので(Let’s Challenge!)
end repeat

on getSystemAlertSoundNames()
  set sList to getSystemAlertSoundPathList() of me
  
set nameList to {}
  
repeat with i in sList
    set j to contents of i
    
set aPOSIX to POSIX path of j
    
set pathString to (current application’s NSString’s stringWithString:aPOSIX)
    
set aFileName to pathString’s lastPathComponent()
    
set aFileName2 to aFileName’s stringByDeletingPathExtension()
    
set the end of nameList to (aFileName2 as text)
  end repeat
  
  return nameList
end getSystemAlertSoundNames

–Get System Alert Path List
on getSystemAlertSoundPathList()
  set aExt to "aiff" – no dot
  
set aFol to "/System/Library/Sounds"
  
set fList to getFilePathListFromPOSIXpath(aFol, aExt) of me
  
return fList
end getSystemAlertSoundPathList

–Get File List from POSIX path and file Extensions
on getFilePathListFromPOSIXpath(aFol, aExt)
  set aFM to current application’s NSFileManager’s defaultManager()
  
set aURL to current application’s |NSURL|’s fileURLWithPath:aFol
  
set urlArray to aFM’s contentsOfDirectoryAtURL:aURL includingPropertiesForKeys:{} options:(current application’s NSDirectoryEnumerationSkipsHiddenFiles) |error|:(missing value)
  
set thePred to current application’s NSPredicate’s predicateWithFormat:"pathExtension == [c]%@" argumentArray:{aExt}
  
set anArray to urlArray’s filteredArrayUsingPredicate:thePred
  
return anArray as list
end getFilePathListFromPOSIXpath

★Click Here to Open This Script 

2015/09/28 NSPredicateによる正規表現を利用した部分一致抽出

Cocoaの機能を用いて、リストのレコードから正規表現による要素の抽出を行うAppleScriptです。実行にはShane StanleyのAppleScript Library「BridgePlus」のインストールが必要になります。

NSPredicateや複数条件を併記するNSCompoundPredicateで、リストに入ったレコードを抽出するのが楽すぎて、もはやこれなしにデータの抽出ができなくなりつつあります。ゴリゴリにPure AppleScriptで記述できないこともないのですが、NSPredicateが使えるだけでもASOCを覚える価値があると思えるほどです。

そんな中、徐々に記述レベルが上がってくると、無茶な抽出にもチャレンジしてみたくなるもので・・・

  「98」で始まる8桁の数字が入った要素をリストアップ。ただし、途中に出現するパターンも含む

という抽出にチャレンジしてみました(仕事で必要だったもので)。完全一致ならなんとか書けるのですが、上記の条件の文字列を「含む」ものまで抽出することを考えると、とたんに難易度が上がります。

結局、AppleのPredicate Programming Guideを総チェックして、「.*」で囲めば「条件に合うものを含む」という状態を抽出できることを見つけました(プログラミングではなくて、情報収集とかパズルのレベルですね)。

AppleScript名:ASOCでNSPredicateによる正規表現を併用した抽出
– Created 2015-09-28 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use BridgePlus : script “BridgePlus” —Shane Stanley’s BridgePlus

set sampleList to {{textData:“Piyomaru”, uID:1}, {textData:“Xx Piyomaru x”, uID:2}, {textData:“xxxxx 11111111 98 x xxxxxxxx.”, uID:3}, {textData:“98x Xxxxxx (xx xxxxxxxxxx)”, uID:4}, {textData:“< < 98158113 >>”, uID:5}, {textData:“#98158084 Xxxxx Xxxxx xxxx”, uID:6}, {textData:“#98158084 Xxxxx Xxxxx xxxx”, uID:7}, {textData:“Office # 98158107″, uID:8}, {textData:“ID#98158087″, uID:9}, {textData:“98158089″, uID:10}, {textData:“00158098″, uID:11}}

–全文一致で抽出
set aRes to my filterRecListByLabel1(sampleList, “textData == ’Piyomaru’”)
–>  {​​​​​{​​​​​​​textData:”Piyomaru”, ​​​​​​​uID:1​​​​​}​​​}

–部分一致で抽出
set bRes to my filterRecListByLabel1(sampleList, “textData contains ’Piyomaru’”)
–>  {​​​​​{​​​​​​​textData:”Piyomaru”, ​​​​​​​uID:1​​​​​}, ​​​​​{​​​​​​​textData:”Xx Piyomaru x”, ​​​​​​​uID:2​​​​​}​​​}

–正規表現で抽出(8桁の数字)
set cRes to my filterRecListByLabel1(sampleList, “textData MATCHES ’\\\\d{8}’”)
–>  {​​​​​{​​​​​​​textData:”98158089″, ​​​​​​​uID:10​​​​​}, ​​​​​{​​​​​​​textData:”00158089″, ​​​​​​​uID:11​​​​​}​​​}

set dRes to my filterRecListByLabel1(sampleList, “textData MATCHES ’98\\\\d{6}’”)
–>  {​​​​​{​​​​​​​textData:”98158089″, ​​​​​​​uID:10​​​​​}​​​}

set eRes to my filterRecListByLabel1(sampleList, “textData LIKE ’*98??????*’”)
–>  {​​​​​{​​​​​​​textData:”xxxxx 11111111 98 x xxxxxxxx.”, ​​​​​​​uID:3​​​​​}, ​​​​​{​​​​​​​textData:”98x Xxxxxx (xx xxxxxxxxxx)”, ​​​​​​​uID:4​​​​​}, ​​​​​{​​​​​​​textData:”< < 98158113 >>”, ​​​​​​​uID:5​​​​​}, ​​​​​{​​​​​​​textData:”#98158084 Xxxxx Xxxxx xxxx”, ​​​​​​​uID:6​​​​​}, ​​​​​{​​​​​​​textData:”#98158084 Xxxxx Xxxxx xxxx”, ​​​​​​​uID:7​​​​​}, ​​​​​{​​​​​​​textData:”Office # 98158107″, ​​​​​​​uID:8​​​​​}, ​​​​​{​​​​​​​textData:”ID#98158087″, ​​​​​​​uID:9​​​​​}, ​​​​​{​​​​​​​textData:”98158089″, ​​​​​​​uID:10​​​​​}​​​}

set fRes to my filterRecListByLabel1(sampleList, “textData LIKE ’*\”98\”[0-9][0-9][0-9][0-9][0-9][0-9]*’”) –Oops!!
–>  {}

set gRes to my filterRecListByLabel1(sampleList, “textData LIKE ’*[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]*’”) –Oops!!
–>  {}

set hRes to my filterRecListByLabel1(sampleList, “textData MATCHES ’.*[98]\\\\d{6}.*’”) –OK!!
–>  {​​​​​{​​​​​​​textData:”< < 98158113 >>”, ​​​​​​​uID:5​​​​​}, ​​​​​{​​​​​​​textData:”#98158084 Xxxxx Xxxxx xxxx”, ​​​​​​​uID:6​​​​​}, ​​​​​{​​​​​​​textData:”#98158084 Xxxxx Xxxxx xxxx”, ​​​​​​​uID:7​​​​​}, ​​​​​{​​​​​​​textData:”Office # 98158107″, ​​​​​​​uID:8​​​​​}, ​​​​​{​​​​​​​textData:”ID#98158087″, ​​​​​​​uID:9​​​​​}, ​​​​​{​​​​​​​textData:”98158089″, ​​​​​​​uID:10​​​​​}​​​}

–リストに入れたレコードを、指定の属性ラベルの値で抽出
on filterRecListByLabel1(aRecList as list, aPredicate as string)
  set aArray to current application’s NSArray’s arrayWithArray:aRecList
  
set aPredicate to current application’s NSPredicate’s predicateWithFormat:aPredicate
  
set filteredArray to aArray’s filteredArrayUsingPredicate:aPredicate
  
set bList to ASify from filteredArray as list
  
return bList
end filterRecListByLabel1

★Click Here to Open This Script 

2015/09/25 spotlightでタグを指定して検索

Cocoaの機能を用いてSpotlight検索で指定フォルダ以下の指定タグのついているファイルを検索するAppleScriptです。

Shane StanleyがAppleScript Users MLに投稿したもので、Spotlightの検索待ち処理でプロパティをループで監視するやり方が採用されていますが、delayの秒数に0.01と指定して済ますあたりが書きこなれています。

実際0.1秒以下の数値指定は無効ではあるものの、delayを使わないとうまく動きません。動いているし、この書き方でエラーを回避できているので、これでいいんでしょう。

複数のPOSIX pathおよび定数で指定されるフォルダをリストにして与え、一括検索を行っているあたりもみどころです。

AppleScript名:spotlightでタグを指定して検索
– Created 2015-09-25 by Shane Stanley
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

property searchIsDone : false

set aRes to my searchInListOfPaths:{POSIX path of (path to home folder)} forTags:{“レッド”}
–>  {{kMDItemPath:”/Users/me/Xxxxxxxxx/XxxxxXxxxxx/XX出撃回数集計(xxxxx)/XXXX_9xxxx_x9(XXXX_XXXX統合版) _9999.9.9XX変更対応.scptd”, kMDItemUserTags:{”レッド”}}, {kMDItemPath:”/Users/me/Xxxxxxxxx/XxxxxXxxxxx/XxxXxxx上のリプレイのダウンロード(alive)/指定IDの戦場の絆のムービーをOffLiberty経由でローカルにダウンロード v3.scpt”, kMDItemUserTags:{”レッド”}}, {kMDItemPath:”/Users/me/Xxxxxxxxx/XxxxxXxxxxx/クリップボードの内容をRTFとPDFとHTMLで書き出す v2.scptd”, kMDItemUserTags:{”レッド”}},…….}

(* scope can be a list of POSIX paths and/or the following enums:
NSMetadataQueryUserHomeScope
NSMetadataQueryLocalComputerScope
NSMetadataQueryNetworkScope
NSMetadataQueryUbiquitousDocumentsScope
NSMetadataQueryUbiquitousDataScope
NSMetadataQueryAccessibleUbiquitousExternalDocumentsScope
NSMetadataQueryIndexedLocalComputerScope — 10.10 or later
NSMetadataQueryIndexedNetworkScope

Eg:
searchInListOfPaths:{current application’s NSMetadataQueryLocalComputerScope} forTags:tagList
*)

–スコープリストの範囲のフォルダから、指定タグを含むファイルをすべてリストアップ
on searchInListOfPaths:scopeList forTags:tagList – pass empty list for any tags
  
  
– Build the search predicate
  
set tagListCount to count of tagList
  
if tagListCount = 0 then – any tags
    set pred to current application’s NSPredicate’s predicateWithFormat:(“kMDItemUserTags LIKE ’*’”)
  else if tagListCount = 1 then
    set pred to current application’s NSPredicate’s predicateWithFormat:“kMDItemUserTags CONTAINS[c] %@” argumentArray:tagList
  else
    set predList to {}
    
repeat with aTag in tagList
      set end of predList to (current application’s NSPredicate’s predicateWithFormat:“kMDItemUserTags CONTAINS[c] %@” argumentArray:{aTag})
    end repeat
    
set pred to current application’s NSCompoundPredicate’s andPredicateWithSubpredicates:predList
  end if
  
  
– make the query
  
set theQuery to current application’s NSMetadataQuery’s alloc()’s init()
  
theQuery’s setPredicate:pred
  
  
– set its scope
  
theQuery’s setSearchScopes:scopeList
  
  
– tell the notification center to tell us when it’s done
  
set notifCenter to current application’s NSNotificationCenter’s defaultCenter()
  
notifCenter’s addObserver:me selector:“searchDone:” |name|:(current application’s NSMetadataQueryDidFinishGatheringNotification) object:theQuery
  
  
– start searching
  
set my searchIsDone to false
  
theQuery’s startQuery()
  
  
– loop until it’s done
  
repeat
    delay 0.01
    
if searchIsDone then exit repeat
  end repeat
  
  
– get results
  
theQuery’s stopQuery()
  
set theCount to theQuery’s resultCount()
  
set resultsArray to current application’s NSMutableArray’s array() – to hold results
  
repeat with i from 1 to theCount
    (resultsArray’s addObject:((theQuery’s resultAtIndex:(i - 1))’s valuesForAttributes:{current application’s NSMetadataItemPathKey, “kMDItemUserTags”}))
  end repeat
  
  
return resultsArray as list – will be list of records
  
end searchInListOfPaths:forTags:

on searchDone:notification
  set my searchIsDone to true
end searchDone:

★Click Here to Open This Script 

2015/09/15 指定URLをロードして1枚ものの大きなPDFにレンダリング

Cocoaの機能を用いて、指定URLのページを読み込み、1枚ものの大きなPDFにレンダリングするAppleScriptです。

デスクトップ上に「outPdf_2.pdf」の名前で書き出します。実行には、Script Editor上でControl-Command-Rによるフォアグラウンド実行する必要があります。

まだまだ、完成にはほど遠い感じがしますが、とりあえずPDF出力できたので。

render2.png
▲本AppleScriptでレンダリングしたPDF

render1.png
▲同じページをSafariで表示させたところ

ページの上と下が「ものすごく残念な状態」になっています。一応、User AgentをSafariに合わせてみたのですが、あまり状況はよくなっていないようです。

まだまだ、本Blogを表示させると、右側のメニュー部分しか表示されなかったり、PDFの縦の長さをページ内容に合わせて自動調整できていなかったり、ページ上のグラフィックがロードされていない状態になることがあったり、、、と、前途多難な印象を受けます。

ただ、これまでこの手のオフラインレンダラーは有用な割にAppleScriptへの対応度が低く、「ならAppleScriptで自力でやっちゃえばいいじゃん」というアプローチは間違っていない(たぶん)はず。また、オフラインレンダラー自体のアップデートが割とおざなりで、OSのアップデートについてこられないことがままあります(便利なのに)。

AppleScript名:ASOCで指定URLをロードして1枚ものの大きなPDFにレンダリング
– Created 2015-09-15 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “WebKit”
use framework “Quartz”
use framework “AppKit”

property loadDone : false
property theWebView : missing value
property userAgentName : “Version/9.0 Safari/601.1.56″

set aOutPath to “~/Desktop/outPdf_2.pdf”
set aURL to “http://www.apple.com/jp/shop/browse/home/specialdeals/mac”
–set aURL to “http://piyocast.com/as”–このBlogをレンダリングすると悲惨な状態に、、、
set targX to 1024
set targY to 2000 –動的に変更できないか?

–Check If this script runs in foreground
if not (current application’s NSThread’s isMainThread()) as boolean then
  display alert “This script must be run from the main thread (Command-Control-R in Script Editor).” buttons {“Cancel”} as critical
  
error number -128
end if

set aPath to current application’s NSString’s stringWithString:aOutPath
set bPath to aPath’s stringByExpandingTildeInPath()

–URL Validation check
set aRes to validateURL(aURL)
if aRes = false then return
set aTitle to getURLandRender(aURL)
–>  ”AS Hole(AppleScriptの穴) By Piyomaru Software”
set aTargetView to (my theWebView)’s mainFrame()’s frameView()’s documentView()

set aScreen to current application’s NSScreen’s mainScreen()
set {scrX, scrY} to aScreen’s frame()’s |size|() as list

(*
set aWin to current application’s NSWindow’s alloc()’s init()
aWin’s setContentSize:{scrX, scrY}
aWin’s setContentView:(my theWebView)
aWin’s |center|()
*)

set aPrintInfo to current application’s NSPrintInfo’s sharedPrintInfo()
set newPrintOp to current application’s NSPrintOperation’s PDFOperationWithView:(aTargetView) insideRect:(current application’s NSMakeRect(0, 0, targX, targY)) toPath:bPath printInfo:aPrintInfo

set runPrint to newPrintOp’s runOperation()
return (runPrint as boolean)

–Download the URL page to WebView and get page title
on getURLandRender(aURL)
  
  
set my loadDone to false
  
set my theWebView to missing value
  
openURL(aURL)
  
  
set waitLoop to 1000 * 60 –60 seconds
  
  
set hitF to false
  
repeat waitLoop times
    if my loadDone = true then
      set hitF to true
      
exit repeat
    end if
    
current application’s NSThread’s sleepForTimeInterval:(“0.001″ as real) –delay 0.001
  end repeat
  
if hitF = false then return
  
  
set jsText to “document.title”
  
set x to ((my theWebView)’s stringByEvaluatingJavaScriptFromString:jsText) as text
  
  
return x
end getURLandRender

–Down Load URL contents to WebView
on openURL(aURL)
  set noter1 to current application’s NSNotificationCenter’s defaultCenter()
  
set (my theWebView) to current application’s WebView’s alloc()’s init()
  (
my theWebView)’s setApplicationNameForUserAgent:userAgentName
  
noter1’s addObserver:me selector:“webLoaded:” |name|:(current application’s WebViewProgressFinishedNotification) object:(my theWebView)
  (
my theWebView)’s setMainFrameURL:aURL
end openURL

–Web View’s Event Handler:load finished
on webLoaded:aNotification
  set my loadDone to true
end webLoaded:

–URL Validation Check
on validateURL(anURL as text)
  set regEx1 to current application’s NSString’s stringWithString:“((https|http)://)((\\w|-)+)(([.]|[/])((\\w|-)+))+”
  
set predicate1 to current application’s NSPredicate’s predicateWithFormat_(“SELF MATCHES %@”, regEx1)
  
set aPredRes1 to (predicate1’s evaluateWithObject:anURL) as boolean
  
return aPredRes1
end validateURL

★Click Here to Open This Script 

2015/09/14 地域、事業所名のリストを出現頻度カウントなどの処理を行って整形

家族から頼まれて作成したプログラムで、割と手こずったデータ処理のAppleScriptです。

Excelに入っているデータを読み取ってAppleScript側で集計処理を行ってみました。Pure AppleScript的な処理とCocoa的な処理がいろいろと入り混じっており、逆に効率が悪くなっているかもしれません(ただし、BridgePlusを活用しまくり、Cocoaの機能を利用して処理を書きまくっているので、処理速度は爆速)。

ここに掲載したScriptは、その処理の一部ですが・・・単体で実行可能なものです。

{「営業所エリア名」、「事業所名」}のペアになっているリストで頻度集計を行うというものですが、同時に「営業所エリア名」を「エリア名略称」に変換します。

Input:{{”西営業所”, “◯◯◯◯◯”}, {”四国営業所”, “●●●●”}, {”西営業所”, “◯◯◯◯◯”}}

Output:
(XXXX西部)◯◯◯◯◯ (2)
(XXXX四国)●●●●

非常に些細な処理ですが、実際に組んでみるとそれなりに手間がかかりました。

NSCountedSetにNSDictionaryを突っ込んで頻度カウントができるんじゃないかと思って試行錯誤してみたのですが、なかなかうまくいきませんでした(どうやら突っ込んでカウントはできているものの、値を取り出すのに失敗)。まだ、なかなか難しいです(汗)。

AppleScript名:地域、事業署名のリストを出現頻度カウントなどの処理を行って整形v2
– Created 2015-09-13 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use BridgePlus : script “BridgePlus”

load framework

set oList to {{“西営業所”, “◯◯◯◯◯”}, {“四国営業所”, “●●●●”}, {“西営業所”, “◯◯◯◯◯”}}

set aRes to formatBranchDescriptions(oList)
–>
(*
“(XXXX西部)◯◯◯◯◯ (2)
(XXXX四国)●●●●

*)

on formatBranchDescriptions(oList)
  set labelList to {“branchName”, “elipName”}
  
set aList to BridgePlus’s sublistsIn:oList asDictionariesUsingLabels:labelList
  
  
—set aList to {{branchName:”西営業所”, elipName:”◯◯◯◯◯”}, {branchName:”四国営業所”, elipName:”●●●●”}, {branchName:”西営業所”, elipName:”◯◯◯◯◯”}}
  
  
set anArray to Cocoaify aList
  
  
set elipNameList to (anArray’s valueForKey:“elipName”) as list
  
–>  {”◯◯◯◯◯”, “●●●●”, “”◯◯◯◯◯”}
  
set eList to uniquify1DList(elipNameList) of me –ユニーク化
  
  
set outStr to “”
  
repeat with i in eList
    set j to contents of i
    
set aRes to countSpecifiedItem(elipNameList, {j}) of me
    
    
set aPredicate to current application’s NSPredicate’s predicateWithFormat_(“elipName == %@”, j)
    
set aRec to (anArray’s filteredArrayUsingPredicate:aPredicate)
    
set anItem to branchName of first item of aRec
    
set eName2 to convBranchToElip(anItem)
    
if aRes > 1 then
      set outStr to outStr & {eName2 & j & “ (” & aRes & “)”} & return
    else
      set outStr to outStr & {eName2 & j} & return
    end if
  end repeat
  
return outStr
end formatBranchDescriptions

–リスト中の指定項目の出現回数を返す
on countSpecifiedItem(aList, countItem)
  set aRes to BridgePlus’s indexesOfItems:countItem inList:aList inverting:false
  
set aCount to length of aRes
  
return aCount
end countSpecifiedItem

–1D Listをユニーク化
on uniquify1DList(theList as list)
  set aArray to current application’s NSArray’s arrayWithArray:theList
  
set bArray to aArray’s valueForKeyPath:“@distinctUnionOfObjects.self”
  
set bList to ASify from bArray as list
  
return bList
end uniquify1DList

–営業所エリア名、エリア名略称変換
on convBranchToElip(aBranch)
  set aList to {{branchName:“東京営業所”, elipName:“(XXXX東京)”}, {branchName:“千葉営業所”, elipName:“(XXXX千葉)”}, {branchName:“神奈川営業所”, elipName:“(XXXX神奈川)”}, {branchName:“西営業所”, elipName:“(XXXX西部)”}, {branchName:“四国営業所”, elipName:“(XXXX四国)”}, {branchName:“茨城営業所”, elipName:“(XXXX茨城)”}}
  
set aDic to Cocoaify aList
  
set aPredicate to current application’s NSPredicate’s predicateWithFormat_(“branchName == %@”, aBranch)
  
set aRes to (aDic’s filteredArrayUsingPredicate:aPredicate)
  
try
    set anItem to contents of first item of (aRes as list)
    
set eName to (elipName of anItem)
  on error
    set eName to “”
  end try
  
return eName
end convBranchToElip

★Click Here to Open This Script 

2015/09/11 与えられたテキストの言語を自動判別して対応する言語のTTS Voiceで読み上げ

Cocoaの機能を用いて、与えられたテキストの言語を自動判別して対応する言語のTTS Voiceで読み上げするAppleScriptです。Shane StanleyのAppleScript Libraryである「BridgePlus」(フリー)のインストールが必要です。

こういうのがやりたくて、地道に部品を揃えてきたわけで・・・やらないわけにはいきません。

ただし、思ったよりも泥沼に足を突っ込みかけました。食後のハイキングのはずが、雪山で遭難しかけたという印象です。

NSSpeechSynthesizerのavailableVoices()は「インストールされていないTTS Voice」も返してくるため、せっかくテキストの言語を特定して、TTS Voiceを取得しても・・・インストールされていないTTS Voiceが返ってくるとお手上げです(T_T)。

tts_voice_list.png

英語だとNSLinguisticTaggerによる判定で”en”が、フランス語だと”fr”が返ってきますが、”en”はあくまで英語を示すものであり、”en_GB”もあれば”en_US”もあれば、”en_IN”(インド)、”en-scotland”(スコットランド)、”en_ZA”(南アフリカ)もあるわけです(自分も物好きですが、南アフリカのTTS Voiceはインストールしていません)。

“en”をキーにして、全TTS Voiceから言語コードを取得し、最初にヒットした言語コードが”en_ZA”であれば南アフリカのTTS Voiceでsayコマンドの実行を行おうとしてしまうので、”en”を”en-US”に置き換えるような、つじつまあわせの処理が必要になってしまいました(もう少しうまいやり方もありそうですが、食後の散歩のレベルを超えてしまいますので)。

実際に実戦レベルのAppleScriptを書いてみるまで、見えてこない「落とし穴」もあるので、こうして細かいScriptを書いておくという作業はなかなかあなどれません、、、

AppleScript名:与えられたテキストの言語を自動判別して対応する言語のTTS Voiceで読み上げ
– Created 2015-09-11 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”
use BridgePlus : script “BridgePlus”

load framework

set aRes to getVoiceLanguageFromTTSVoices() –Language Code

–English, French, Japanese, Chinese
set cList to {“With the new MacBook, we set out to do the impossible.”, “Le nouveau MacBook est le résultat d’un défi presque impossible”, “新しいMacBookとともに、私たちは不可能だと思えることに挑みました。”, 于全新 MacBook,我们给自己定了一个几乎不可能实现的目}

–各言語テキストでループ
repeat with i in cList
  set j to contents of i
  
set aLang to specifyLanguageOfText(j) –テキストから言語コードを取得
  
  
–つじつまあわせ。ちょっとカッコ悪い
  
–(NSSpeechSynthesizerのavailableVoicesでインストールされていないVoiceまで返ってくることへの対策)
  
if aLang = “en” then
    set aLang to “en-US”
  else if aLang = “fr” then
    set aLang to “fr-FR”
  else if aLang = “ja” then
    set aLang to “ja-JP”
  end if
  
  
–全ボイス中から言語をサポートしているものを抽出
  
repeat with ii in aRes
    set jj to contents of ii
    
    
if (jj as text) is equal to (aLang as text) then
      
      
–最初にヒットした言語コードでPremium Voiceを検索
      
set v1fList to getPremiumVoiceNameWithFiltering(“VoiceGenderFemale”, jj)
      
set v1mList to getPremiumVoiceNameWithFiltering(“VoiceGenderMale”, jj)
      
set v1List to v1fList & v1mList
      
      
if v1List = {} then
        –Premium Voiceでヒットしなかった場合に、すべてのVoiceを検索
        
set v2fList to getAllVoiceNameWithFiltering(“VoiceGenderFemale”, jj)
        
set v2mList to getAllVoiceNameWithFiltering(“VoiceGenderMale”, jj)
        
set v2List to v2fList & v2mList
        
        
if v2List = {} then
          display dialog “この言語(” & jj & “)をサポートするTTS Voiceはインストールされていません。” –Error
          
exit repeat
        else
          copy v2List to v1List
        end if
        
      end if
      
      
repeat with iii in v1List
        set aVoice to contents of iii
        
try
          –総当たりでSayコマンド実行(インストールされていないTTS Voiceも取得できてしまうため)
          
tell current application
            say j using aVoice
          end tell
          
exit repeat
        end try
      end repeat
      
      
exit repeat
    end if
  end repeat
  
end repeat

–Premium Voices Only
on getPremiumVoiceNameWithFiltering(voiceGender, voiceLang)
  –Make Blank Array
  
set outArray to current application’s NSMutableArray’s arrayWithObject:{}
  
  
–Make Installed Voice List
  
set aList to current application’s NSSpeechSynthesizer’s availableVoices()
  
set bList to aList as list
  
  
repeat with i in bList
    set j to contents of i
    
set aDIc to (current application’s NSSpeechSynthesizer’s attributesForVoice:j)
    (
outArray’s addObject:aDIc)
  end repeat
  
  
–Filter Voice
  
set aPredicate to current application’s NSPredicate’s predicateWithFormat_(“VoiceLanguage == %@ and VoiceGender == %@ and VoiceIdentifier ENDSWITH[c] %@”, voiceLang, voiceGender, “premium”)
  
set filteredArray to outArray’s filteredArrayUsingPredicate:aPredicate
  
set aReList to (filteredArray’s valueForKey:“VoiceName”) as list
  
  
return aReList
  
end getPremiumVoiceNameWithFiltering

–All Voices (VoiceLanguageで抽出)
on getAllVoiceNameWithFiltering(voiceGender, voiceLang)
  –Make Blank Array
  
set outArray to current application’s NSMutableArray’s arrayWithObject:{}
  
  
–Make Installed Voice List
  
set aList to current application’s NSSpeechSynthesizer’s availableVoices()
  
set bList to aList as list
  
  
repeat with i in bList
    set j to contents of i
    
set aDIc to (current application’s NSSpeechSynthesizer’s attributesForVoice:j)
    (
outArray’s addObject:aDIc)
  end repeat
  
  
–Filter Voice
  
set aPredicate to current application’s NSPredicate’s predicateWithFormat_(“VoiceLanguage == %@ and VoiceGender == %@”, voiceLang, voiceGender)
  
  
set filteredArray to outArray’s filteredArrayUsingPredicate:aPredicate
  
set aReList to (filteredArray’s valueForKey:“VoiceName”) as list
  
  
return aReList
  
end getAllVoiceNameWithFiltering

on getLocaleIdentifierFromTTSVoices()
  –Make Blank Array
  
set outArray to current application’s NSMutableArray’s arrayWithObject:{}
  
  
–Make Installed Voice List
  
set aList to current application’s NSSpeechSynthesizer’s availableVoices()
  
set bList to aList as list
  
  
repeat with i in bList
    set j to contents of i
    
set aDIc to (current application’s NSSpeechSynthesizer’s attributesForVoice:j)
    (
outArray’s addObject:aDIc)
  end repeat
  
  
set aResArray to (outArray’s valueForKey:“VoiceLocaleIdentifier”)
  
  
set aSet to current application’s NSMutableSet’s setWithArray:aResArray
  
set aResList to aSet’s allObjects()
  
set bResList to BridgePlus’s listByDeletingBlanksIn:aResList –Remove Blank Items
  
  
return bResList
end getLocaleIdentifierFromTTSVoices

on getVoiceLanguageFromTTSVoices()
  –Make Blank Array
  
set outArray to current application’s NSMutableArray’s arrayWithObject:{}
  
  
–Make Installed Voice List
  
set aList to current application’s NSSpeechSynthesizer’s availableVoices()
  
set bList to aList as list
  
  
repeat with i in bList
    set j to contents of i
    
set aDIc to (current application’s NSSpeechSynthesizer’s attributesForVoice:j)
    (
outArray’s addObject:aDIc)
  end repeat
  
  
set aResArray to (outArray’s valueForKey:“VoiceLanguage”)
  
set aSet to current application’s NSMutableSet’s setWithArray:aResArray
  
set aResList to aSet’s allObjects()
  
set bResList to BridgePlus’s listByDeletingBlanksIn:aResList –Remove Blank Items
  
  
return bResList
end getVoiceLanguageFromTTSVoices

on specifyLanguageOfText(aStr)
  set aNSstring to current application’s NSString’s stringWithString:aStr
  
set tagSchemes to current application’s NSArray’s arrayWithObjects:(current application’s NSLinguisticTagSchemeLanguage)
  
set tagger to current application’s NSLinguisticTagger’s alloc()’s initWithTagSchemes:tagSchemes options:0
  
tagger’s setString:aNSstring
  
set aLanguage to tagger’s tagAtIndex:0 |scheme|:(current application’s NSLinguisticTagSchemeLanguage) tokenRange:(missing value) sentenceRange:(missing value)
  
return aLanguage as text
end specifyLanguageOfText

–1D Listをユニーク化してソート
on uniquifyAndSort1DList(theList as list, aBool as boolean)
  set aArray to current application’s NSArray’s arrayWithArray:theList
  
set bArray to aArray’s valueForKeyPath:“@distinctUnionOfObjects.self”
  
set aDdesc to current application’s NSSortDescriptor’s sortDescriptorWithKey:“self” ascending:aBool selector:“compare:”
  
set cArray to bArray’s sortedArrayUsingDescriptors:{aDdesc}
  
set bList to (ASify from cArray) as list
  
return bList
end uniquifyAndSort1DList

–文字置換
on repChar(aStr, targStr, repStr)
  set aString to current application’s NSString’s stringWithString:aStr
  
set bString to aString’s stringByReplacingOccurrencesOfString:targStr withString:repStr
  
set cString to (ASify from bString) as string
  
return cString
end repChar

★Click Here to Open This Script 

2015/09/07 メーラーから取得したメールアドレスのうち純粋なアドレス部分を抽出 v2

Cocoaの機能を用いて、メーラーから取得した「name<address>」的な余計な文字のついたメールアドレス文字列から純粋なアドレス部分を抽出するAppleScriptです。

Shaneから「こう書けば短くなるよ」と、3分の1ぐらいに短くなる書き方を教えてもらいました。しかも、与えた文字列にメールアドレスが複数記載してあってもlistで複数返すようになっています。

AppleScript名:ASOCでメーラーから取得したメールアドレスのうち純粋なアドレス部分を抽出 v2
– Created 2015-09-07 by Shane Stanley
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

set theString to “Takaaki Naganoya<maro@piyocast.com>”
set theAddress to extractMailAddress(theString)
–>  {”maro@piyocast.com”}

set theString to “Takaaki Naganoya<maro@piyocast.com>,Takaaki Naganoya<maro@piyocast.com>,Takaaki Naganoya<maro@piyocast.com>”
set theAddress to extractMailAddress(theString)
–> {”maro@piyocast.com”, “maro@piyocast.com”, “maro@piyocast.com”}

on extractMailAddress(aString)
  set anNSString to current application’s NSString’s stringWithString:aString
  
set {theDetector, theError} to current application’s NSDataDetector’s dataDetectorWithTypes:(current application’s NSTextCheckingTypeLink) |error|:(reference)
  
set theMatches to theDetector’s matchesInString:anNSString options:0 range:{0, anNSString’s |length|()}
  
set theResults to theMatches’s valueForKey:“URL”
  
set thePredicate to current application’s NSPredicate’s predicateWithFormat:“scheme == ’mailto’”
  
set theResults to theResults’s filteredArrayUsingPredicate:thePredicate
  
return (theResults’s valueForKey:“resourceSpecifier”) as list
end extractMailAddress

★Click Here to Open This Script 

2015/09/06 URLの妥当性チェック

Cocoaの機能を用いて、URLの妥当性チェックを行うAppleScriptです。

ドメイン自体が生きているかどうか、ドメイン上のディレクトリの存在チェックは行わず、単に文字列の並びがRFCに則っているかどうかのチェックだけです。

AppleScript名:ASOCでURLの妥当性チェック
– Created 2015-09-06 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
–http://stackoverflow.com/questions/1471201/how-to-validate-an-url-on-the-iphone

set aRes1 to validateURL("http://www.apple.com/jp")
–>  true

set aRes2 to validateURL("http.s://www.gmail.com")
–>  false
set aRes3 to validateURL("https:.//gmailcom")
–>  false
set aRes4 to validateURL("https://gmail.me.")
–>  false
set aRes5 to validateURL("https://www.gmail.me.com.com.com.com")
–>  true
set aRes6 to validateURL("http:/./ww-w.wowone.com")
–>  false
set aRes7 to validateURL("http://.www.wowone")
–>  false
set aRes8 to validateURL("http://www.wow-one.com")
–>  true
set aRes9 to validateURL("http://www.wow_one.com")
–>  true
set aRes10 to validateURL("http://.")
–>  false
set aRes11 to validateURL("http://")
–>  false
set aRes12 to validateURL("http://k")
–>  false

return {aRes2, aRes3, aRes4, aRes5, aRes6, aRes7, aRes8, aRes9, aRes10, aRes11, aRes12}
–>  {​​​​​false, ​​​​​false, ​​​​​false, ​​​​​true, ​​​​​false, ​​​​​false, ​​​​​true, ​​​​​true, ​​​​​false, ​​​​​false, ​​​​​false​​​}

–URLの妥当性チェック
on validateURL(anURL as text)
  –set regEx1 to current application’s NSString’s stringWithString:"(http|https)://((\\w)*|([0-9]*)|([-|_])*)+([\\.|/]((\\w)*|([0-9]*)|([-|_])*))+"
  
set regEx1 to current application’s NSString’s stringWithString:"((https|http)://)((\\w|-)+)(([.]|[/])((\\w|-)+))+"
  
set predicate1 to current application’s NSPredicate’s predicateWithFormat_("SELF MATCHES %@", regEx1)
  
set aPredRes1 to (predicate1’s evaluateWithObject:anURL) as boolean
  
return aPredRes1
end validateURL

★Click Here to Open This Script 

2015/09/06 メールアドレスの妥当性チェック

Cocoaの機能を用いて、メールアドレスの妥当性チェックを行うAppleScriptです。

メールアドレスを全角英数字で書いてしまう(”maro@hiyoko.com”→”maro@hiyoko.com”)人への対策は、全角→半角変換(”A”→”A”)をあらかじめ行っておくなどの対策が別途必要です。

AppleScript名:ASOCでメールアドレスの妥当性チェック
– Created 2015-09-06 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://stackoverflow.com/questions/800123/what-are-best-practices-for-validating-email-addresses-in-objective-c-for-ios-2

set aRes1 to validateEmailAddress(“maro@piyocast.com”)
–>  true

set aRes2 to validateEmailAddress(“maro@piyocast..com”) –ピリオドを2つ連続して書いてみた
–>  false

–メールアドレスの妥当性チェック(文字列的に。実際にアカウントが有効かどうかは別の話で。全角文字で書いてしまう人への対策は別途)
on validateEmailAddress(anAddress as text)
  set regEx1 to current application’s NSString’s stringWithString:“\\A[a-z0-9]+([-._][a-z0-9]+)*@([a-z0-9]+(-[a-z0-9]+)*\\.)+[a-z]{2,4}\\z”
  
set regEx2 to current application’s NSString’s stringWithString:“^(?=.{1,64}@.{4,64}$)(?=.{6,100}$).*”
  
set predicate1 to current application’s NSPredicate’s predicateWithFormat_(“SELF MATCHES %@”, regEx1)
  
set predicate2 to current application’s NSPredicate’s predicateWithFormat_(“SELF MATCHES %@”, regEx2)
  
set aPredRes1 to (predicate1’s evaluateWithObject:anAddress) as boolean
  
set aPredRes2 to (predicate2’s evaluateWithObject:anAddress) as boolean
  
return (aPredRes1 and aPredRes2)
end validateEmailAddress

★Click Here to Open This Script 

2015/09/05 ASOCでspotlight検索するじっけん v2

Cocoaの機能を用いてspotlight検索を行うAppleScriptです。Script Editor上でControl-Command-RでForeground実行する必要があります。

前バージョンでは、ファイル名しか取得できていませんでしたが、検索でヒットしたアイテムのフルパスを返すようにできたので、ようやくこれで実用レベルに達したと思います。

(1)検索対象フォルダを指定
(2)検索条件を設定

すると、実際にspotlight検索を行って結果を返します。

検索結果待ちループ側でdelayを極小にできるよう、試行錯誤してみましたが、

(1)delayを削除するとうまく動かない
(2)delay 0.01のとおりに本当に0.01秒待っているかはわからない
(3)手元の環境ではうまく動いているが、環境に応じたチューニングが必要?(2台で確認)

という印象を持っています。

ASOCで0.001という数値をプログラム内に記述するのにえっらい苦労しました。そのまま書くと指数表現されてしまい、Pure AppleScriptの世界ではエラーにならないんですが、Cocoaのオブジェクトに渡されるときにうまくブリッジされないのか、エラーに。NSSNumberでfloatValueを設定しようとしてもうまく行かないわで、結局、文字列で書いて強制的にcastするという強引な方法で解決。もっといい方法があるとよいのですが、相当苦労しました。

いろいろ条件を変えて検索の実験をしたところ、与えた検索条件でヒットがない場合には早々に{}を返してくるため、無限ループで処理が終わらないということはないようです。

これでもう、spotlight検索のためにshell commandのmdfindを呼び出す必要はなくなりました。

AppleScript Users MLの過去ログや、ShaneのEveryday AppleScriptObjC添付のサンプルコード、ASObjC Explorer 4添付のサンプルコードなども探し回ってspotlight検索のサンプルを探してみましたが、検索結果のフルパスを返すものはなかったので(ついでに言うとObjective-Cのプログラムを検索エンジンで探しても、けっこう見つからない)、これを書いておいた意義はあるものと思います。

AppleScript名:ASOCでmdfindするじっけん v2(フルパスを返す)
– Created 2015-09-03 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

property searchRes : {}

set searchRes to {} –initialize

set origPath to POSIX path of (choose folder)
initiateSearchForFullPath(“kMDItemDisplayName == ’ICON0.PNG’”, origPath) –Predicate & Scope Directory

–Waiting for the result
repeat while searchRes = {}
  current application’s NSThread’s sleepForTimeInterval:(“0.001″ as real) –delay 0.001
end repeat

set anObj to searchRes’s firstObject() –Pick up the first one for test
if anObj = missing value then return {} –No Result

–set anAttrList to anObj’s attributes() –”mdls” attributes
–>  (NSArray) {”kMDItemContentTypeTree”, “kMDItemContentType”, “kMDItemPhysicalSize”, …}

set resArray to current application’s NSMutableArray’s alloc()’s init()
repeat with anItem in searchRes
  (resArray’s addObject:(anObj’s valueForAttribute:“kMDItemPath”))
  
–>  (NSString) “/Users/me/Documents/機動戦士ガンダム戦場の絆 Folder/ULJS00181USERID_0000/ICON0.PNG”
end repeat

resArray
–>  (NSArray) {”/Users/me/Documents/機動戦士ガンダム戦場の絆 Folder/ULJS00181USERID_0000/ICON0.PNG”,……}

on initiateSearchForFullPath(aQueryStrings, origPath)
  
  
set aSearch to current application’s NSMetadataQuery’s alloc()’s init()
  
  
current application’s NSNotificationCenter’s defaultCenter()’s addObserver:(me) selector:“queryDidUpdate:” |name|:(current application’s NSMetadataQueryDidUpdateNotification) object:aSearch
  
current application’s NSNotificationCenter’s defaultCenter()’s addObserver:(me) selector:“initalGatherComplete:” |name|:(current application’s NSMetadataQueryDidFinishGatheringNotification) object:aSearch
  
  
set aPredicate to current application’s NSPredicate’s predicateWithFormat:aQueryStrings
  
aSearch’s setPredicate:aPredicate
  
  
set aScope to current application’s NSArray’s arrayWithObjects:{origPath}
  
aSearch’s setSearchScopes:aScope
  
  
set sortKeys to current application’s NSSortDescriptor’s sortDescriptorWithKey:“kMDItemFSName” ascending:true
  
aSearch’s setSortDescriptors:(current application’s NSArray’s arrayWithObject:sortKeys)
  
  
aSearch’s startQuery()
  
end initiateSearchForFullPath

on queryDidUpdate:sender
  log sender
  
–> (NSConcreteNotification) NSConcreteNotification 0×7fa618450820 {name = NSMetadataQueryDidFinishGatheringNotification; object = }
end queryDidUpdate:

on initalGatherComplete:sender
  set anObject to sender’s object
  
anObject’s stopQuery()
  
current application’s NSNotificationCenter’s defaultCenter()’s removeObserver:me |name|:(current application’s NSMetadataQueryDidUpdateNotification) object:anObject
  
current application’s NSNotificationCenter’s defaultCenter()’s removeObserver:me |name|:(current application’s NSMetadataQueryDidFinishGatheringNotification) object:anObject
  
  
set my searchRes to anObject’s results()
end initalGatherComplete:

★Click Here to Open This Script 

2015/08/31 ASOCでspotlight検索するじっけん

Cocoaの機能を用いてspotlightによる検索を行うAppleScriptの実験的な実装です。

オリジナルは、Appleのオンラインドキュメント「File Metadata Search Programming Guide」にあった、「Listing 2-1 Static Spotlight search implementation」です。これをAppleScriptObjCに(少しアレンジを加えつつ)翻訳したものです。

Script Editor上でCommand-Control-RによるForeground実行する必要があります。プログラムリスト中におけるログ内容はASObjC Explorer 4で表示させたもので、同様の内容を確認したい場合にはASObjC Explorer 4が必要です。

海外のユーザーから「どうやって表示してんの、それ?(Cocoa Objectのログ)」と、問い合わせがあって「通常のScript Editorでは表示できないよ」と回答。意外と知られていないようで、、、(もったいないので、ここで宣伝しておくことにしました)。

ASObjC Explorer 4のログ表示は、AppleScriptのプログラムの間にログ表示用のコードを挟んでいるとのことなので、必ずしも実際のオブジェクトとイコールの内容が表示されているわけではありませんが、実際の内容をつかみやすいものになっています。この機能を持つAppleScript系のエディタは、目下のところASObjC Explorer 4だけです。

Objective-CのプログラムをさらっとAppleScriptObjCのプログラムに書き換えできるのは、ASObjC ExplorerのCocoaオブジェクトログ機能があればこそです。ログを出しつつ属性値を確認できるため、おおいに役立っています。逆に、Cocoaオブジェクトログ機能がないと、全く歯が立ちません(AppleがShaneのプログラムを買い取って、標準のScript Editorに機能をマージすればいいのに、、、)。

AppleScript名:ASOCでspotlight検索するじっけん
– Created 2015-08-31 by Takaaki Naganoya
– 2015 Piyomaru Software

use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

property searchRes : {}

set searchRes to {} –initialize
initiateSearch(“kMDItemContentTypeTree == ’public.image’”)

–Waiting for the result
repeat while searchRes = {} –Inifinity loop, danger!!
  delay 0.1
end repeat

set anObj to searchRes’s firstObject() –Pick up the first one for test
set anAttrList to anObj’s attributes() –”mdls” attributes
–>  (NSArray) {”kMDItemContentTypeTree”, “kMDItemContentType”, “kMDItemPhysicalSize”, …}

set aFileName to anObj’s valueForKey:“kMDItemDisplayName”
–>  (NSString) ” 1 - 95.7.16.jpg” — example

on initiateSearch(aQueryStrings)
  
  
set aSearch to current application’s NSMetadataQuery’s alloc()’s init()
  
–>  (NSMetadataQuery)
  
  
current application’s NSNotificationCenter’s defaultCenter()’s addObserver:(me) selector:“queryDidUpdate:” |name|:(current application’s NSMetadataQueryDidUpdateNotification) object:aSearch
  
current application’s NSNotificationCenter’s defaultCenter()’s addObserver:(me) selector:“initalGatherComplete:” |name|:(current application’s NSMetadataQueryDidFinishGatheringNotification) object:aSearch
  
  
  
set aPredicate to current application’s NSPredicate’s predicateWithFormat:aQueryStrings
  
–> (NSComparisonPredicate) kMDItemContentTypeTree == “public.image”
  
aSearch’s setPredicate:aPredicate
  
  
  
set aScope to current application’s NSArray’s arrayWithObjects:{current application’s NSMetadataQueryUserHomeScope, current application’s NSMetadataQueryUbiquitousDocumentsScope}
  
–> (NSArray) {{”kMDQueryScopeHome”, “NSMetadataQueryUbiquitousDocumentsScope”}
  
aSearch’s setSearchScopes:aScope
  
  
  
set sortKeys to current application’s NSSortDescriptor’s sortDescriptorWithKey:“kMDItemDisplayName” ascending:true
  
aSearch’s setSortDescriptors:(current application’s NSArray’s arrayWithObject:sortKeys)
  
  
  
aSearch’s startQuery()
  
end initiateSearch

on queryDidUpdate:sender
  log sender
  
–> (NSConcreteNotification) NSConcreteNotification 0×7fa618450820 {name = NSMetadataQueryDidFinishGatheringNotification; object = }
end queryDidUpdate:

on initalGatherComplete:sender
  log sender
  
–> (NSConcreteNotification) NSConcreteNotification 0×7fa618450820 {name = NSMetadataQueryDidFinishGatheringNotification; object = }
  
  
set anObject to sender’s object
  
anObject’s stopQuery()
  
  
set aRes to anObject’s resultCount()
  
–>  173352 –different in each environment. Just a test
  
  
set my searchRes to anObject’s results()
  
–>  (NSArray) {(NSMetadataItem) , (NSMetadataItem) , ……
  
  
current application’s NSNotificationCenter’s defaultCenter()’s removeObserver:me |name|:(current application’s NSMetadataQueryDidUpdateNotification) object:anObject
  
current application’s NSNotificationCenter’s defaultCenter()’s removeObserver:me |name|:(current application’s NSMetadataQueryDidFinishGatheringNotification) object:anObject
  
  
set anObject to missing value –really needed?
  
end initalGatherComplete:

★Click Here to Open This Script 

2015/08/26 ASOCで性別と言語コードを指定してTTS voiceを取得 v2

Cocoaの機能を用いて、Text To Speechの音声から性別と言語を指定して、TTS Voice名称をリストで返すAppleScriptです。最初のバージョンに対して、「Premium」voiceに限定しないvoice名取得ルーチン「getAllVoiceNameWithFiltering」を追加しています。ほかに、機能的な差異はありません。

前バージョンに対し、Shane StanleyからNSPredicateの指定について、

set aStr to current application’s NSString’s stringWithString:(”VoiceLocaleIdentifier == ‘” & voiceLang & “‘ and  VoiceGender == ‘” & voiceGender & “‘ and VoiceIdentifier ENDSWITH[c] ‘premium’”)
set aPredicate to current application’s NSPredicate’s predicateWithFormat:aStr

って、書いてあるけど引数をNSPredicateの指定文字列から分離したほうがクォートで囲む囲まないで悩まなくていいから、分離しといたほうがいいよ(意訳)という指摘があって・・・実際、NSPredicate以外のコードはすぐに書けたものの・・・クォートの指定でけっこう試行錯誤して悩んでいたので、まったくその通り(^ー^;;;;

set aPredicate to current application’s NSPredicate’s predicateWithFormat_(”VoiceLocaleIdentifier == %@ and VoiceGender == %@ and VoiceIdentifier ENDSWITH[c] %@”, voiceLang, voiceGender, “premium”)

とか(ここで「predicateWithFormat_」とアンダースコアで書かないと構文確認をパスできないところにASOCの闇と、Shaneの試行錯誤を感じます、、、)、

set aPredicate to current application’s NSPredicate’s predicateWithFormat:”VoiceLocaleIdentifier == %@ and VoiceGender == %@ and VoiceIdentifier ENDSWITH[c] %@” argumentArray:{voiceLang, voiceGender, “premium”}

みたいに書いたほうがシンプルでいいよ的な話で、まさにそのとおり。で、いろいろ書き換えておきました。

各TTS Voiceについては、性別、言語、国などのほかに「年齢」というパラメータも持っているのですが、年齢を条件にして綿密に絞り込む・・・というほどには、TTS Voiceの数が多くないので、年齢は考慮していません。

これ(↑)を掲載するまでに、「クリップボードの内容をスタイル付きテキストで取得する方法」について調べ、調べきれなかったので、do shell script使いまくりの昔作ったAppleScriptでHTMLに書き出した、というオチもありました。Cocoaのクリップボード関連、普段使わないうえに、いざ真剣に調べだしてもあんまりまとまったまともでわかりやすい情報がなくて困りものです。

AppleScript名:ASOCで性別と言語コードを指定してTTS voiceを取得 v2
– Created 2015-08-25 by Takaaki Naganoya
– Modified 2015-08-26 by Shane Stanley, Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”
use BridgePlus : script “BridgePlus”

load framework

set v1Res to getPremiumVoiceNameWithFiltering(“VoiceGenderMale”, “ja_JP”)
–>  {​​​​​”Otoya”​​​}

set v2Res to getPremiumVoiceNameWithFiltering(“VoiceGenderFemale”, “en_US”)
–>  {​​​​​”Allison”, ​​​​​”Ava”, ​​​​​”Jill”, ​​​​​”Samantha”​​​}

set v3Res to getAllVoiceNameWithFiltering(“VoiceGenderFemale”, “en_US”)
–>  {​​​​​”Agnes”, ​​​​​”Allison”, ​​​​​”Ava”, ​​​​​”Jill”, ​​​​​”Kathy”, ​​​​​”Princess”, ​​​​​”Samantha”, ​​​​​”Vicki”, ​​​​​”Victoria”​​​}

–Premium Voices Only
on getPremiumVoiceNameWithFiltering(voiceGender, voiceLang)
  –Make Blank Array
  
set outArray to current application’s NSMutableArray’s arrayWithObject:{}
  
  
–Make Installed Voice List
  
set aList to current application’s NSSpeechSynthesizer’s availableVoices()
  
set bList to aList as list
  
  
repeat with i in bList
    set j to contents of i
    
set aDIc to (current application’s NSSpeechSynthesizer’s attributesForVoice:j)
    (
outArray’s addObject:aDIc)
  end repeat
  
  
–Filter Voice
  
set aPredicate to current application’s NSPredicate’s predicateWithFormat_(“VoiceLocaleIdentifier == %@ and VoiceGender == %@ and VoiceIdentifier ENDSWITH[c] %@”, voiceLang, voiceGender, “premium”)
  
set filteredArray to outArray’s filteredArrayUsingPredicate:aPredicate
  
set aReList to (filteredArray’s valueForKey:“VoiceName”) as list
  
  
return aReList
  
end getPremiumVoiceNameWithFiltering

–All Voices
on getAllVoiceNameWithFiltering(voiceGender, voiceLang)
  –Make Blank Array
  
set outArray to current application’s NSMutableArray’s arrayWithObject:{}
  
  
–Make Installed Voice List
  
set aList to current application’s NSSpeechSynthesizer’s availableVoices()
  
set bList to aList as list
  
  
repeat with i in bList
    set j to contents of i
    
set aDIc to (current application’s NSSpeechSynthesizer’s attributesForVoice:j)
    (
outArray’s addObject:aDIc)
  end repeat
  
  
–Filter Voice
  
set aPredicate to current application’s NSPredicate’s predicateWithFormat_(“VoiceLocaleIdentifier == %@ and VoiceGender == %@”, voiceLang, voiceGender)
  
  
set filteredArray to outArray’s filteredArrayUsingPredicate:aPredicate
  
set aReList to (filteredArray’s valueForKey:“VoiceName”) as list
  
  
return aReList
  
end getAllVoiceNameWithFiltering

on getLaunguageCodeFromTTSVoices()
  
  
–Make Blank Array
  
set outArray to current application’s NSMutableArray’s arrayWithObject:{}
  
  
–Make Installed Voice List
  
set aList to current application’s NSSpeechSynthesizer’s availableVoices()
  
set bList to aList as list
  
  
repeat with i in bList
    set j to contents of i
    
set aDict to (current application’s NSSpeechSynthesizer’s attributesForVoice:j)
    (
outArray’s addObject:aDict)
  end repeat
  
  
set aResArray to (outArray’s valueForKey:“VoiceLocaleIdentifier”)
  
  
set aSet to current application’s NSMutableSet’s setWithArray:aResArray
  
set aResList to aSet’s allObjects()
  
  
set bResList to BridgePlus’s listByDeletingBlanksIn:aResList –Remove Blank Items
  
  
return bResList
  
end getLaunguageCodeFromTTSVoices

★Click Here to Open This Script 

2015/08/25 ASOCで性別と言語コードを指定して高音質voiceを取得

Cocoaの機能を用いて、Text To Speechの音声から性別と言語を指定して、高音質のVoice名称をリストで返すAppleScriptです。

voice1.png
▲システム環境設定「音声入力と読み上げ」>「テキスト読み上げ」

voice2.png
▲OSにインストールされているVoiceリスト

男性:VoiceGenderMale
女性:VoiceGenderFemale

で性別を指定して高音質のvoice名称を取得します。

アメリカ英語(en_US)で男性の音声をどれでもいいから指定したいが、どの音声が入っているかは特定できないしデフォルトで指定されているかどうかもわからない、といった場合にある程度の見当をつけるために作ったものです。

TTS Voiceで使用可能な言語コード一覧については、「ASOCで全voiceから言語コードを抽出」のプログラムで取得できるようにしておきました。

AppleScript名:ASOCで性別と言語コードを指定して高音質voiceを取得
– Created 2015-08-25 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”

set v1Res to getPremiumVoiceNameWithFiltering(“VoiceGenderMale”, “ja_JP”)
–>  {​​​​​”Otoya”​​​}

set v2Res to getPremiumVoiceNameWithFiltering(“VoiceGenderFemale”, “en_US”)
–>  {​​​​​”Allison”, ​​​​​”Ava”, ​​​​​”Jill”, ​​​​​”Samantha”​​​}

set v3Res to getPremiumVoiceNameWithFiltering(“VoiceGenderMale”, “en_GB”)
–> {”Daniel”}

–Premium Voices Only
on getPremiumVoiceNameWithFiltering(voiceGender, voiceLang)
  –Make Blank Array
  
set outArray to current application’s NSMutableArray’s arrayWithObject:{}
  
  
–Make Installed Voice List
  
set aList to current application’s NSSpeechSynthesizer’s availableVoices()
  
set bList to aList as list
  
  
repeat with i in bList
    set j to contents of i
    
set aDIc to (current application’s NSSpeechSynthesizer’s attributesForVoice:j)
    (
outArray’s addObject:aDIc)
  end repeat
  
  
–Filter Voice
  
set aStr to current application’s NSString’s stringWithString:(“VoiceLocaleIdentifier == ’” & voiceLang & “’ and VoiceGender == ’” & voiceGender & “’ and VoiceIdentifier ENDSWITH[c] ’premium’”)
  
set aPredicate to current application’s NSPredicate’s predicateWithFormat:aStr
  
set filteredArray to outArray’s filteredArrayUsingPredicate:aPredicate
  
set aReList to (filteredArray’s valueForKey:“VoiceNameRoot”) as list
  
  
return aReList
  
end getPremiumVoiceNameWithFiltering

★Click Here to Open This Script 

AppleScript名:ASOCで全voiceから言語コードを抽出
– Created 2015-08-25 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”
use BridgePlus : script “BridgePlus”

load framework

set cRes to getLaunguageCodeFromTTSVoices()
–>  {​​​​​”fr_FR”, ​​​​​”zh_TW”, ​​​​​”it_IT”, ​​​​​”en_ZA”, ​​​​​”es_AR”, ​​​​​”ko_KR”, ​​​​​”ro_RO”, ​​​​​”en_IN”, ​​​​​”fr_CA”, ​​​​​”hi_IN”, ​​​​​”da_DK”, ​​​​​”en-scotland”, ​​​​​”pt_BR”, ​​​​​”zh_CN”, ​​​​​”sv_SE”, ​​​​​”es_ES”, ​​​​​”hu_HU”, ​​​​​”ar_SA”, ​​​​​”en_GB”, ​​​​​”ja_JP”, ​​​​​”fi_FI”, ​​​​​”zh_HK”, ​​​​​”tr_TR”, ​​​​​”nb_NO”, ​​​​​”pl_PL”, ​​​​​”id_ID”, ​​​​​”cs_CZ”, ​​​​​”el_GR”, ​​​​​”he_IL”, ​​​​​”ru_RU”, ​​​​​”de_DE”, ​​​​​”en_AU”, ​​​​​”nl_BE”, ​​​​​”pt_PT”, ​​​​​”th_TH”, ​​​​​”sk_SK”, ​​​​​”en_US”, ​​​​​”en_IE”, ​​​​​”nl_NL”, ​​​​​”es_MX”​​​}

on getLaunguageCodeFromTTSVoices()
  
  
–Make Blank Array
  
set outArray to current application’s NSMutableArray’s arrayWithObject:{}
  
  
–Make Installed Voice List
  
set aList to current application’s NSSpeechSynthesizer’s availableVoices()
  
set bList to aList as list
  
  
repeat with i in bList
    set j to contents of i
    
set aDIc to (current application’s NSSpeechSynthesizer’s attributesForVoice:j)
    (
outArray’s addObject:aDIc)
  end repeat
  
  
set aResArray to (outArray’s valueForKey:“VoiceLocaleIdentifier”)
  
  
set aSet to current application’s NSMutableSet’s setWithArray:aResArray
  
set aResList to aSet’s allObjects()
  
  
set bResList to BridgePlus’s listByDeletingBlanksIn:aResList –Remove Blank Items
  
  
return bResList
  
end getLaunguageCodeFromTTSVoices

★Click Here to Open This Script 

2015/08/10 使用中のMacの製品呼称を取得する v2

使用中のMacの製品呼称を取得するAppleScriptです。

@fixdotさんにTwitterで教えていただいた ここの情報 でplistから検索するように変更してみました。

ただ、やはり製品が新しくなってもマシンの世代が変わっていないものについては、違いを検出できないため、決定的なものにはなっていません。製品のシリアル番号から生産年と生産週は取得できるため、このあたりをからめて(モデルごとの生産年・週の境目がわかれば)、擬似的な判定は可能と思われます。

AppleScript名:使用中のMacの製品呼称を取得する v2
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use BridgeP : script “BridgePlus”

set pListPath to “/System/Library/PrivateFrameworks/ServerInformation.framework/Versions/A/Resources/English.lproj/SIMachineAttributes.plist”
set aRec to retDictFromPlist(pListPath) of me

set hwName to (do shell script “sysctl -n hw.model”) as text
–>  ”MacBookPro10,1″

set aMachineRec to retRecordByLabel(aRec, hwName)
–>  {​​​​​”x86_64″, ​​​​​”/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/com.apple.macbookpro-15-retina-display.icns”, ​​​​​{​​​​​​​marketingModel:”MacBook Pro with Retina display, Intel Core i7, 15\” (Mid 2012)”, ​​​​​​​model:”MacBook Pro”, ​​​​​​​description:”15\” MacBook Pro with Retina display, dual-core Intel Core i7 processor and aluminum unibody, introduced mid 2012.”, ​​​​​​​processor:”Intel Core i7″​​​​​}​​​}

set macName to marketingModel of (last item of aMachineRec)
–>  ”MacBook Pro with Retina display, Intel Core i7, 15\” (Mid 2012)”

–Read plist as record
on retDictFromPlist(aPath)
  set thePath to current application’s NSString’s stringWithString:aPath
  
set thePath to thePath’s stringByExpandingTildeInPath()
  
set theDict to current application’s NSDictionary’s dictionaryWithContentsOfFile:thePath
  
return theDict as record
end retDictFromPlist

–リストに入れたレコードを、指定の属性ラベルの値で抽出
on filterRecListByLabel(aRecList as list, aPredicate as string)
  –ListからNSArrayへの型変換
  
set aArray to current application’s NSArray’s arrayWithArray:aRecList
  
  –抽出
  
set aPredicate to current application’s NSPredicate’s predicateWithFormat:aPredicate
  
set filteredArray to aArray’s filteredArrayUsingPredicate:aPredicate
  
  –NSArrayからListに型変換して返す
  
set bList to (ASify from filteredArray) as list
  
return bList
end filterRecListByLabel

–指定レコードの指定ラベルの値を取得する
on retRecordByLabel(aRec as record, aKey as string)
  set aDic to current application’s NSDictionary’s dictionaryWithDictionary:aRec
  
set aVal to aDic’s valueForKey:aKey
  
set aValNum to (ASify from aVal) as list
  
return aValNum
end retRecordByLabel

★Click Here to Open This Script 

2015/03/06 ワイルドカードでSafariのダウンロードフォルダ中のファイルを抽出

This AppleScript gets pattern-matched file names in Safari’s Download folder.

Safariのダウンロードフォルダ中のファイルから、与えられたパターンに該当するファイル名のリストを取得するAppleScriptです。

(2015/8/10更新)チルダ付きのパスをechoコマンドで展開していたので、ASOC版のルーチンに差し替えました。

AppleScript名:ワイルドカードでSafariのダウンロードフォルダ中のファイルを抽出 v2
– Created 2015-03-06 by Takaaki Naganoya
– Created 2015-08-10 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

–Get Safari Download Folder Path
set downloadFolder to POSIX path of getSafariDownloadFolder()

–Make Filtering Predicates with WIldcard
set matchPattern to current application’s NSString’s stringWithString:“*.zip” –can use “*” , “?”
set aPredicate to current application’s NSPredicate’s predicateWithFormat_(“SELF like %@”, matchPattern)

–Get every file name in Safari Download Folder
set aFM to current application’s NSFileManager’s defaultManager()
set aConArray to aFM’s contentsOfDirectoryAtPath:downloadFolder |error|:(missing value)

–Filtering with Predicates
set aResArray to aConArray’s filteredArrayUsingPredicate:aPredicate
set aResList to aResArray as list
–>  {”33_fusagamepad_0.3.zip”, “expressmaci.zip”, “filer6.6.zip”, “Glyphs2.1.1-768.zip”, “LinCastor.zip”, “recordpadmaci.zip”, “TessOCR-1.08.zip”, “wavepadmaci.zip”}

–Safariのダウンロードフォルダーを取得する–Updated
on getSafariDownloadFolder()
  set theID to id of application “Safari” –> “com.apple.Safari”
  
set storedDefaults to (current application’s NSUserDefaults’s standardUserDefaults()’s persistentDomainForName:theID)
  
set downloadFolder to storedDefaults’s valueForKeyPath:“DownloadsPath.stringByExpandingTildeInPath”
  
return downloadFolder as «class furl»
end getSafariDownloadFolder

★Click Here to Open This Script 

2015/01/19 2Dリストから、指定アイテムNoで、指定データが該当する最初のものを返す v3

2D Listから、指定アイテムNoが指定データとイコールの要素を返すAppleScriptです。

{{”piyomaru”, 1}, {”Piyomaru Software”, 2}, {”Naganoya”, 3}, {”Takaaki”, 4}, {”MacBook Pro Retina mid 2012″, 5}}

という2D Listがあった場合に、各アイテム中のアイテムNo.が1(item 1)のデータが”Takaaki”のものについて、

{”Takaaki”, 4}

というデータを、該当するデータがなかった場合には{}を返します。

AppleScript名:2Dリストから、指定アイテムNoで、指定データが該当する最初のものを返す v3
– Created 2015-01-19 by Takaaki Naganoya
– 2014 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "ASObjCExtras"

set aList to {{"piyomaru", 1}, {"Piyomaru Software", 2}, {"Naganoya", 3}, {"Takaaki", 4}, {"MacBook Pro Retina mid 2012", 5}}

set gList to searchInListByIndexItem(aList, 1, "Takaaki")
–> {"Takaaki", 4}

–2Dリストから、指定インデックスアイテムで、指定データが該当する最初のものを返す
on searchInListByIndexItem(aList as list, itemNum as integer, hitData as string)
  –ListからNSMutableSetへの型変換
  
set setKey to current application’s NSMutableSet’s setWithArray:aList
  
  
–Predicate Stringを組み立てる
  
if itemNum < 1 then return {}
  
set aPredicateStr to ("SELF[" & (itemNum - 1) as string) & "] == ’" & hitData & "’"
  
  
–抽出
  
set aPredicate to current application’s NSPredicate’s predicateWithFormat:aPredicateStr
  
set aRes to (setKey’s filteredSetUsingPredicate:aPredicate)
  
set bRes to aRes’s allObjects()
  
  
–NSArrayからListに型変換して返す
  
set cRes to (bRes’s ASify()) as list
  
  
if cRes is not equal to {} then
    set cRes to contents of first item of cRes
  end if
  
  
return cRes
end searchInListByIndexItem

★Click Here to Open This Script 

2014/11/22 レコードのリストから抽出(別リストに該当するもののみ)

レコードのリスト(List化したRecord)から、条件に合う項目を抽出するAppleScriptです。

指定項目が別途指定したリストの中に入っている値とマッチする場合のみ抽出します。

AppleScript名:asoc_レコードの抽出(別リストに入っている値に該当するかチェック)
– Created 2014-11-21 by Takaaki Naganoya
– 2014 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "ASObjCExtras"

set aRecList to {{aName:"piyoko", aVal:100}, {aName:"piyomaru", aVal:80}, {aName:"piyoo", aVal:10}, {aName:"Gundamo", aVal:10}}
set nList to {"piyoo", "piyoko"}

set bList to filterRecListByLabelAndSublist(aRecList, "aName IN %@", nList) of me
–> {{aName:"piyoko", aVal:100}, {aName:"piyoo", aVal:10}}

–リストに入れたレコードを、指定の属性ラベルの値で抽出。値が別途指定のリストの中に存在していることが条件
on filterRecListByLabelAndSublist(aRecList as list, aPredicate as string, aSubList as list)
  –ListからNSArrayへの型変換
  
set aArray to current application’s NSArray’s arrayWithArray:aRecList
  
set aSubArray to current application’s NSArray’s arrayWithArray:aSubList
  
  
–抽出
  
set aPredicate to current application’s NSPredicate’s predicateWithFormat_(aPredicate, aSubArray)
  
set filteredArray to aArray’s filteredArrayUsingPredicate:aPredicate
  
  
–NSArrayからListに型変換して返す
  
set bList to (filteredArray’s ASify()) as list
  
return bList
end filterRecListByLabelAndSublist

★Click Here to Open This Script 

2014/11/22 リストから抽出(asoc)

リストから条件に合う項目だけを抽出するAppleScriptです。

こういう感じで1D Listだけでなく2D Listを抽出できると便利でしょう。ああ、2D Listの抽出をやりたい、、、

AppleScript名:listの項目をフィルタリング(項目文字長)
– Created 2014-11-21 by Takaaki Naganoya
– 2014 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “ASObjCExtras”

set aList to {“piyomaru”, “Piyomaru Software”, “Naganoya”, “Takaaki”, “MacBook Pro Retina mid 2012″}

set bList to filterListUsingPredicate(aList, “length > 8″) –文字列長が8文字より長い項目を返す
–> {”MacBook Pro Retina mid 2012″, “Piyomaru Software”}

set cList to filterListUsingPredicate(aList, “SELF MATCHES ’.*e$’”) –正規表現で末尾が”e”
–> {”Piyomaru Software”}

set dList to filterListUsingPredicate(aList, “SELF LIKE ’piyo*’”)
–> {”piyomaru”}

set eList to filterListUsingPredicate(aList, “SELF LIKE[c] ’piyo*’”)
–> {”piyomaru”, “Piyomaru Software”}

set fList to filterListUsingPredicate(aList, “SELF CONTAINS[c] ’Piyo’”)
–> {”piyomaru”, “Piyomaru Software”}

on filterListUsingPredicate(aList as list, aPredicateStr as string)
  –ListからNSArrayへの型変換
  
set setKey to current application’s NSMutableSet’s setWithArray:aList
  
  
–抽出
  
set aPredicate to current application’s NSPredicate’s predicateWithFormat:aPredicateStr
  
set aRes to (setKey’s filteredSetUsingPredicate:aPredicate)
  
set bRes to aRes’s allObjects()
  
  
–NSArrayからListに型変換して返す
  
set cRes to (bRes’s ASify()) as list
  
return cRes
end filterListUsingPredicate

★Click Here to Open This Script 

2014/11/22 レコードのリストから抽出(asoc)

レコードのリスト(List化したRecord)から、条件に合う項目を抽出するAppleScriptです。

本来、AppleScriptの処理系だけで、

  set aList to {{aName:”ぴよまる”, weight:70}, {aName:”ぴよこ”, wight:60}}

のようなリスト化したレコードをフィルタ参照(set bList to every record whose cell “aName” is equal to “ぴよまる” とか)で抽出できるべきですが、長年搭載されてきませんでした。

こうした処理をAppleScriptで実現するためには、FileMaker Proなどのデータベースを併用するか、OS標準搭載のDataBase Events(フィルタ参照で該当データを抽出する専用のDBシステム。ソート機能がない)を利用することに(ASだけでゴリゴリ記述するという方向性もありますが、、、)。

さすがにDataBase Eventsを使うぐらいなら、ASOCで記述したほうが簡単です。

数百万件や数千万件の規模のデータを扱うにはFileMaker Proなどのデータベースを併用すべきだと思いますが、十万件ぐらいの規模のデータであればAppleScriptだけで便利にデータ処理できてよさそうです。

AppleScript名:asoc_レコードのリストから抽出
– Created 2014-11-21 by Takaaki Naganoya
– 2014 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “ASObjCExtras”

set aRecList to {{aName:“piyoko”, aVal:100}, {aName:“piyomaru”, aVal:80}, {aName:“piyoo”, aVal:10}, {aName:“Gundamo”, aVal:10}}

set bList to filterRecListByLabel(aRecList, “aName like ’Gun*’”) of me
–> {{aName:”Gundamo”, aVal:10}}

set cList to filterRecListByLabel(aRecList, “aVal >=80″) of me
–> {{aName:”piyoko”, aVal:100}, {aName:”piyomaru”, aVal:80}}

set dList to filterRecListByLabel(aRecList, “aVal=80 or aVal=10″) of me
–> {{aName:”piyomaru”, aVal:80}, {aName:”piyoo”, aVal:10}, {aName:”Gundam”, aVal:10}}

set eList to filterRecListByLabel(aRecList, “aVal=10 and aName like ’piyo*’”) of me
–> {{aName:”piyoo”, aVal:10}}

set fList to filterRecListByLabel(aRecList, “aName matches ’.*u$’”) of me –名前の最後が u で終わるものを、正規表現を用いて抽出
–> {{aName:”piyomaru”, aVal:80}}

set eList to filterRecListByLabel(aRecList, “aName == ’piyomaru’”) of me
–> {{aName:”piyomaru”, aVal:80}}

set eList to filterRecListByLabel(aRecList, “aName.length > 6″) of me –指定ラベルのデータの文字列長が6以上
–> {{aName:”piyomaru”, aVal:80}, {aName:”Gundamo”, aVal:10}}

–リストに入れたレコードを、指定の属性ラベルの値で抽出
on filterRecListByLabel(aRecList as list, aPredicate as string)
  –ListからNSArrayへの型変換
  
set aArray to current application’s NSArray’s arrayWithArray:aRecList
  
  
–抽出
  
set aPredicate to current application’s NSPredicate’s predicateWithFormat:aPredicate
  
set filteredArray to aArray’s filteredArrayUsingPredicate:aPredicate
  
  
–NSArrayからListに型変換して返す
  
set bList to (filteredArray’s ASify()) as list
  
return bList
end filterRecListByLabel

★Click Here to Open This Script