Archive for the 'NSArray' Category

2017/04/15 リストをクラス名でフィルタする v2

リストをクラス名でフィルタするAppleScriptです。指定クラスに合致するデータをリスト中から抽出します。

修正:listに対して簡単に直接フィルターする方法については別途記述しておきます。本Scriptは他の用途に転用するような場合の記述例として掲載しておきます

NSArrayに入れてNSPredicateで抽出を行う方法があります。ただし、これだとクラス名がCocoaとAppleScriptで合わないので、いまひとつ使えません。

そこで、ひたすら原始的ではありますが、リストをループで回してクラス名を照合して別リストに追加、という処理を回してみました。超高級言語のAppleScriptですが、たまーにこういう泥くさいチェック処理を書く瞬間があります。

そのままだと知性のカケラもなんにもないので、せめてクラス名の同義語辞書を定義して、クラス名の表記ゆらぎ(というよりも、クラス間の内包関係。integerとrealはnumberに含まれるなど)に対応させてみました。

AppleScript名:リストをクラス名でフィルタする v2
– Created 2017-04-15 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

set aList to {1, “ABC”, 0.3, 5, {1, 2, 3}, {abc:“record”}, false}
set bList to filterListByClass(aList, “integer”)
–> {1, 5}
set cList to filterListByClass(aList, “number”)
–> {1, 0.3, 5}
set dList to filterListByClass(aList, “real”)
–> {0.3}
set eList to filterListByClass(aList, “string”)
–> {”ABC”}
set fList to filterListByClass(aList, “record”)
–> {{abc:”record”}}
set gList to filterListByClass(aList, “boolean”)
–> {false}

on filterListByClass(aList as list, aClass as string)
  set classNameList to getClassNameConsideringSynonyms(aClass) of me
  
  
set newList to {}
  
repeat with i in aList
    set j to contents of i
    
set myClass to (class of j) as string
    
if myClass is in classNameList then
      set the end of newList to j
    end if
  end repeat
  
return newList
end filterListByClass

on getClassNameConsideringSynonyms(aClassString)
  set smallClass to ((current application’s NSString’s stringWithString:aClassString)’s lowercaseString()) as string
  
set classRec to {|list|:{“list”}, |record|:{“record”}, |number|:{“number”, “integer”, “real”}, |integer|:{“integer”}, |real|:{“real”}, |text|:{“text”, “string”, “unicode text”}, |string|:{“text”, “string”, “unicode text”}, |unicode text|:{“text”, “string”, “unicode text”}, |boolean|:{“boolean”}}
  
set classDict to current application’s NSDictionary’s dictionaryWithDictionary:classRec
  
set aVal to (classDict’s valueForKey:smallClass) as list
  
if aVal = {missing value} then error “Unexpected class name string”
  
return aVal
end getClassNameConsideringSynonyms

★Click Here to Open This Script 

2017/04/11 NSMutableArrayの特定要素の書き換え

NSMutableArray中の特定要素を書き換えるAppleScriptです。

NSMutableArrayの内容をpredicateで条件を指定してフィルタリングする処理はAppleScriptでも頻繁に使うようになってきました。他のSQLデータベースに依存する必要もなく、処理速度も速いため非常に有効な手段です。

ただ、抽出処理は行えるものの、当該アイテムの書き換えを行って、もとのNSMutableArrayに書き戻せないとデータベース的な運用はできません。

そこで、簡単なサンプルScriptを作ってみて、Array中の特定要素を検索して書き換える処理を書いてみました。

AppleScript名:リスト中の指定アイテムを置き換える
– Created 2017-04-11 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4579

set anArray to current application’s NSMutableArray’s arrayWithArray:{5, 2, 1, 3, 4}
anArray’s replaceObjectsAtIndexes:(current application’s NSIndexSet’s indexSetWithIndex:0) withObjects:{-1}
return anArray as list
–> {-1, 2, 1, 3, 4}

★Click Here to Open This Script 

AppleScript名:レコード入りリスト中の指定アイテムを置き換える
– Created 2017-04-11 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4579

set anArray to current application’s NSMutableArray’s arrayWithArray:{{aLabel:0, bLabel:2, cLabel:100}, {aLabel:2, bLabel:3, cLabel:1}}
anArray’s replaceObjectsAtIndexes:(current application’s NSIndexSet’s indexSetWithIndex:0) withObjects:{{aLabel:-1, bLabel:-2, cLabel:-3}}
return anArray as list
–>  {{cLabel:-3, aLabel:-1, bLabel:-2}, {cLabel:1, aLabel:2, bLabel:3}}

★Click Here to Open This Script 

AppleScript名:リスト中の指定アイテムを置き換える(登場アイテム番号自動検索)
– Created 2017-04-11 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4579

set aTargValue to 2
set aNewValue to -100
set anArray to current application’s NSMutableArray’s arrayWithArray:{5, 2, 1, 3, 4}
set aInd to anArray’s indexOfObject:aTargValue
anArray’s replaceObjectsAtIndexes:(current application’s NSIndexSet’s indexSetWithIndex:aInd) withObjects:{aNewValue}
return anArray as list
–>  {5, -100, 1, 3, 4}

★Click Here to Open This Script 

AppleScript名:レコード入りリスト中の指定アイテムを置き換える(登場アイテム番号自動検索)
– Created 2017-04-11 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4579

set aTargValue to {aLabel:2, bLabel:3, cLabel:1}
set aNewValue to {aLabel:-1, bLabel:-2, cLabel:-3}

set anArray to current application’s NSMutableArray’s arrayWithArray:{{aLabel:0, bLabel:2, cLabel:100}, {aLabel:2, bLabel:3, cLabel:1}, {aLabel:0, bLabel:0, cLabel:0}}
set aInd to anArray’s indexOfObject:aTargValue

anArray’s replaceObjectsAtIndexes:(current application’s NSIndexSet’s indexSetWithIndex:aInd) withObjects:{aNewValue}
return anArray as list
–>  {{cLabel:100, aLabel:0, bLabel:2}, {cLabel:-3, aLabel:-1, bLabel:-2}, {cLabel:0, aLabel:0, bLabel:0}}

★Click Here to Open This Script 

2017/03/01 NSSet/NSMutableSetの演算

NSSet/NSMutableSetが集合を表すClassであることをあとから知り、集合同士の演算の例を調べてみました。こういう地道な調査があとでジワジワ効いてくるのです。

set1.png

AppleScript名:補集合(set difference)
– Created 2017-02-24 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4487

set aArray to current application’s NSArray’s arrayWithArray:{1, 2, 3, 4}
set bArray to current application’s NSArray’s arrayWithArray:{3, 5, 6, 7}

set aSet to current application’s NSMutableSet’s alloc()’s initWithArray:aArray
set bSet to current application’s NSMutableSet’s alloc()’s initWithArray:bArray

aSet’s minusSet:bSet –補集合
aSet’s allObjects() as list
–>  {2, 1, 4}

★Click Here to Open This Script 

set2.png

AppleScript名:積集合(product)
– Created 2017-02-24 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4487

set aArray to current application’s NSArray’s arrayWithArray:{1, 2, 3, 4}
set bArray to current application’s NSArray’s arrayWithArray:{3, 5, 6, 7}

set aSet to current application’s NSMutableSet’s alloc()’s initWithArray:aArray
set bSet to current application’s NSMutableSet’s alloc()’s initWithArray:bArray

aSet’s intersectSet:bSet –積集合
aSet’s allObjects() as list
–>  {3}

★Click Here to Open This Script 

set3.png

AppleScript名:和集合(union)
– Created 2017-02-24 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4487

set aArray to current application’s NSArray’s arrayWithArray:{1, 2, 3, 4}
set bArray to current application’s NSArray’s arrayWithArray:{3, 5, 6, 7}

set aSet to current application’s NSMutableSet’s alloc()’s initWithArray:aArray
set bSet to current application’s NSMutableSet’s alloc()’s initWithArray:bArray

aSet’s unionSet:bSet –和集合
aSet’s allObjects() as list
–>  {5, 1, 6, 2, 7, 3, 4}

★Click Here to Open This Script 

2017/01/28 2つのパスの相対パスを求める

2つのPOSIX pathの相対位置を求めるAppleScriptです。aFileの位置から見た、bFileの相対パス表記を求めます。

画像をDropboxにアップロードして、そのリンクをMarkdown書類に記述していました。

実験的な試みであったものの、いちいちWeb上の画像を読みに行くため処理が遅く、決まったマシンでしか作業しないのでクラウドに画像を置いている意味がほとんどありませんでした。

結局、「ローカルに置けばいいじゃないか!」ということに。

指定フォルダ以下のMarkdown書類をSpotlightで抽出し、Markdown書類のテキストエンコーディングを自動判定して本文を抽出、本文中から画像リンクURLを抽出して所定のフォルダに画像をダウンロードするまでは簡単にありもののAppleScriptの組み合わせで書けたものの、問題は画像リンクの書き換え。

あらたにローカルにダウンロードした画像へのリンクを求める際に、MarkDown書類からの相対パスを指定する必要がありました。かるく調べた範囲では、2つのパスの相対パスを求めるmethodやプログラムが見つからなかったので、AppleScriptで書いてみました。

書いたあとで、Objective-Cなど他の言語の実装例もいろいろ見つけましたが、誰がどのような言語処理系で書いてもだいたい同じ処理内容でした。

# 自分が使っているMarkdownエディタである「MacDown」では画像リンク時の相対指定が効かなかったので、本ルーチンはオクラ入りに(^ー^;

AppleScript名:2つのパスの相対パスを求める
– Created 2017-01-28 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4415

set aFile to “/Users/me/Documents/Book1/0900 Command Reference/1000 Command References/1010 tell/1010 tell.md”
set bFile to “/Users/me/Documents/Book1/9999_images/002-640×427.png”

set relativePath to calcRelativePath(aFile, bFile) of me
–>  ”……/9999_images/002-640×427.png”

on calcRelativePath(aPOSIXfile, bPOSIXfile)
  set aStr to text 2 thru -1 of aPOSIXfile
  
set bStr to text 2 thru -1 of bPOSIXfile
  
  
set aList to parseByDelim(aStr, “/”) of me
  
set bList to parseByDelim(bStr, “/”) of me
  
  
set aLen to length of aList
  
set bLen to length of bList
  
  
if aLen bLen then
    copy aLen to aMax
  else
    copy bLen to aMax
  end if
  
  
repeat with i from 1 to aMax
    set aTmp to contents of item i of aList
    
set bTmp to contents of item i of bList
    
    
if aTmp is not equal to bTmp then
      exit repeat
    end if
  end repeat
  
  
set bbList to items i thru -1 of bList
  
set aaItem to (length of aList) - i
  
  
set tmpStr to “”
  
repeat with ii from 1 to aaItem
    set tmpStr to tmpStr & “..”
  end repeat
  
  
set allRes to tmpStr & “/” & retStrFromArrayWithDelimiter(bbList, “/”) of me
  
return allRes
end calcRelativePath

on retStrFromArrayWithDelimiter(aList, aDelim)
  set anArray to current application’s NSArray’s arrayWithArray:aList
  
set aRes to anArray’s componentsJoinedByString:aDelim
  
return aRes as text
end retStrFromArrayWithDelimiter

on parseByDelim(aData, aDelim)
  set aText to current application’s NSString’s stringWithString:aData
  
set aList to aText’s componentsSeparatedByString:aDelim
  
return aList as list
end parseByDelim

★Click Here to Open This Script 

2017/01/27 ASOCの美しい書き方?

AppleScriptからCocoaの機能を呼び出すことができるようになり、AppleScript本来の簡潔な記述はしにくくなりました。

ASOCの記述方法について、どのようなアプローチが可能かをまとめてみました。

(1)は、ふだん書いているやり方です。一番安全で確実にAppleScriptの処理系に解釈されるので、トラブル回避のためにこの書き方をしています。ただし、1行がものすごく長くなるので、本Blogに掲載したときの「見た目」がよろしくありません(エディタの上では問題ないのですが、、、)

(2)は、Xcode上のAppleScriptでよくやる書き方です。インデントを基本としたAppleScriptらしいスタイルにはなっているものの、書かなくてはならない情報が多く、やや面倒と感じます。

(3)は、(2)でインデントの段数の増えすぎたことに対する改良とでも呼ぶべきものでしょうか。「its」を毎回書く必要があることに対し、抵抗感があるかないかが問題でしょう。

(4)は、Script Debugger 6のテンプレートに入っていた記述で、なかなかいい手だと思います(Xcode上でもやっていましたが)。ただ、プログラムが長く複雑になると冒頭のProperty宣言部分がどんどん長くなるという問題もあります。

サンプルとして書くのであれば、(4)あたりを検討すべきなんでしょう。

AppleScript名:(1)quick style
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4412

set a to (current application’s NSString’s stringWithString:“aaaaa”) as string
–>  ”aaaaa”

★Click Here to Open This Script 

AppleScript名:(2)xcode style
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4412

tell current application
  tell class “NSString”
    set a to (its stringWithString:“aaaaa”) as string
  end tell
end tell
–>  ”aaaaa”

★Click Here to Open This Script 

AppleScript名:(3)layer style
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4412

tell current application’s NSString
  set a to (its stringWithString:“aaaa”) as string
end tell
–>  ”aaaa”

★Click Here to Open This Script 

AppleScript名:(4)mark alldritt style
use AppleScript version “2.4″
use framework “Foundation”
use scripting additions
–http://piyocast.com/as/archives/4412

property NSString : a reference to current application’s NSString

set a to (NSString’s stringWithString:“aaaa”) as string
–>  ”aaaa”

★Click Here to Open This Script 

実際に(4)のスタイルで書いてみたら、NSMakeRangeやNSMaxRangeなどのObjective-Cのメソッドでない部分を同じスタイルに統一できなくて(current application’sを省略できなくて)、化けきれていない感じが、、、、

あとは、スクリプトエディタ上だとほとんど構文要素の変化に乏しく、メリハリがない(構文要素カラーリングの効果がなく)ので書く方にとって読みにくくなるような気がします。

macOS標準搭載のスクリプトエディタでは、Cocoaのclass名もmethod名も同じ色で表示されてしまいますが、ASObjC Explorer 4やScript Debugger 6ではmethod名を別の色で表示する機能が実装されているため、やはりそうしたツールがないと辛い感じです。

mi.png

AppleScript名:テキストエディタ「mi」で選択中のテキストからHTMLタグを外して逆順に並べなおす
– Created 2017-01-27 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4412

property NSString : a reference to current application’s NSString
property NSAttributedString : a reference to current application’s NSAttributedString
property NSArray : a reference to current application’s NSArray
property NSMutableArray : a reference to current application’s NSMutableArray
property NSUTF16StringEncoding : a reference to current application’s NSUTF16StringEncoding

tell application “mi”
  tell document 1
    set aText to selection
  end tell
end tell

–選択部分のテキストからHTMLのタグを外す
set anNSString to NSString’s stringWithString:aText
set theData to anNSString’s dataUsingEncoding:(NSUTF16StringEncoding)
set styledString to NSAttributedString’s alloc()’s initWithHTML:theData documentAttributes:(missing value)
set plainText to (styledString’s |string|())

–テキストを行ごとにparseしてNSArrayに
set anArray to NSMutableArray’s alloc()’s init()
set aRange to current application’s NSMakeRange(0, plainText’s |length|())

repeat while aRange’s |length|() > 0
  set subRange to plainText’s lineRangeForRange:(current application’s NSMakeRange(aRange’s location(), 0))
  
  
–行が改行コードまで取得されるので、改行コードを除外するように微調整
  
copy subRange to tmpRange
  
set tmpRange’s |length| to ((subRange’s |length|()) - 1) –微調整
  
set aLine to plainText’s substringWithRange:tmpRange
  
anArray’s addObject:aLine
  
  
set aRange’s location to (current application’s NSMaxRange(subRange))
  
set aRange’s |length| to ((aRange’s |length|()) - (subRange’s |length|()))
end repeat

–NSArrayを逆順に(reverse order array)
set outArray to (anArray’s reverseObjectEnumerator())’s allObjects()

–NSArray to string with line delimiter
set outStr to retStrFromArrayWithDelimiter(outArray, return) of me

–リストを指定デリミタをはさんでテキスト化
on retStrFromArrayWithDelimiter(aList, aDelim)
  set anArray to NSArray’s arrayWithArray:aList
  
set aRes to anArray’s componentsJoinedByString:aDelim
  
return aRes as text
end retStrFromArrayWithDelimiter

★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/12/13 顔認識結果のデータを加工

Microsoftの顔認識APIを呼び出して、その結果を加工して表情認識APIを呼び出すために、顔認識APIの実行結果を加工するAppleScriptです。

まだ表情認識APIは呼び出せていませんが、その途中で作ったものです。

典型的なデータ加工の処理なので、ほかにも(もう少しうまい)やり方がありそうです。

AppleScript名:顔認識結果のデータを加工
– Created 2016-12-13 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
–http://piyocast.com/as/archives/4357

set aList to current application’s NSArray’s arrayWithArray:{{faceId:"c49883a0-c4ee-4671-ad06-6f7f8370ab20", faceRectangle:{top:68, width:40, |left|:336, height:40}}, {faceId:"93459b48-4c19-4cc7-85e6-b38b8d0fc73b", faceRectangle:{top:81, width:38, |left|:92, height:38}}, {faceId:"9c796619-c654-47dd-b7b0-c382dc6bda84", faceRectangle:{top:63, width:38, |left|:214, height:38}}, {faceId:"94a2ceec-c089-494c-982e-a213579342b6", faceRectangle:{top:92, width:38, |left|:462, height:38}}, {faceId:"18e8dad0-fe46-4c5c-81c3-3ba483129512", faceRectangle:{top:89, width:37, |left|:700, height:37}}, {faceId:"4031f4bd-4e47-4050-89a0-f66e2e4e9142", faceRectangle:{top:45, width:37, |left|:570, height:37}}}

set aRes to retFaceArray(aList) of me
–>  "336,68,40,40;92,81,38,38;214,63,38,38;462,92,38,38;700,89,37,37;570,45,37,37"

on retFaceArray(aList)
  set a1Res to (aList’s valueForKeyPath:"faceRectangle.top") as list
  
set a2Res to (aList’s valueForKeyPath:"faceRectangle.width") as list
  
set a3Res to (aList’s valueForKeyPath:"faceRectangle.left") as list
  
set a4Res to (aList’s valueForKeyPath:"faceRectangle.height") as list
  
  
set aLen to length of a1Res
  
set aRes to {}
  
  
repeat with i from 1 to aLen
    set aLeft to (contents of item i of a3Res) as string
    
set aTop to (contents of item i of a1Res) as string
    
set aWidth to (contents of item i of a2Res) as string
    
set aHeight to (contents of item i of a4Res) as string
    
set tmpRes to aLeft & "," & aTop & "," & aWidth & "," & aHeight
    
set the end of aRes to tmpRes
  end repeat
  
  
set allRes to retStrFromArrayWithDelimiter(aRes, ";") of me
  
  
return allRes
end retFaceArray

–リストを指定デリミタをはさんでテキスト化
on retStrFromArrayWithDelimiter(aList, aDelim)
  set anArray to current application’s NSArray’s arrayWithArray:aList
  
set aRes to anArray’s componentsJoinedByString:aDelim
  
return aRes as text
end retStrFromArrayWithDelimiter

★Click Here to Open This Script 

2016/10/30 Listのrecordをエンコーディングしてplist文字列にする

Listでまとめたrecordをエンコーディングしてplist文字列にするAppleScriptです。

ただのrecordでもなんでもいいのですが、plistのファイルに書き込むのではなく、文字列として取得します。

AppleScriptをTerminal上で呼び出して、複数のAppleScript同士でデータをやりとりする場合に、リストやレコードをそのままやりとりすることは困難です。そこで、plistの文字列にして標準出力に出すことを検討してみました。

ご注意:本Scriptはdeprecatedなmethodを使っているため、アップデート版を利用してください。

AppleScript名:Listのrecordをエンコーディングしてplist文字列にする
– Created 2016-10-30 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4295

set aList to {{theName:“サウンドトラック”, numberOfTimes:1721}, {theName:“ロック”, numberOfTimes:942}}

–2D Arrayをplistの文字列にエンコードする
set anArray to current application’s NSArray’s arrayWithObject:aList
set pListData to current application’s NSPropertyListSerialization’s dataFromPropertyList:anArray |format|:(current application’s NSPropertyListXMLFormat_v1_0) errorDescription:(missing value)
set bStr to (current application’s NSString’s alloc()’s initWithData:pListData encoding:(current application’s NSUTF8StringEncoding)) as string

(*
–>  ”<?xml version=\”1.0\” encoding=\”UTF-8\”?>
<!DOCTYPE plist PUBLIC \”-//Apple//DTD PLIST 1.0//EN\” \”http://www.apple.com/DTDs/PropertyList-1.0.dtd\”>
<plist version=\”1.0\”>
<array>
  <array>
    <dict>
      <key>numberOfTimes</key>
      <integer>1721</integer>
      <key>theName</key>
      <string>サウンドトラック</string>
    </dict>
    <dict>
      <key>numberOfTimes</key>
      <integer>942</integer>
      <key>theName</key>
      <string>ロック</string>
    </dict>
  </array>
</array>
</plist>

*)

★Click Here to Open This Script 

2016/10/25 ムービー系の拡張子からFile Format UTIを取得する v2, v3

ムービー系のファイルの拡張子から、File Format UTIを取得するAppleScriptの改良版です。

Shane Stanleyから指摘があって、

(1)com.apple.quicktime-movieと current application’s AVFileTypeQuickTimeMovieは同じものなので変換する必要はないよ(なんとなく、そうじゃないかとは思ってました ^ー^;;)

(2)BridgePlus v1.3.2に拡張子からUTIを求めるメソッドが用意してあるよ(!!!)

というわけで、(1)を反映させたv2、(2)まで反映させたv3を作成してみましたが、v3にいたってはたったの1行。どこかにもっとスマートな解決策が転がっていると思っていましたが、ここまでスマートになるとは(^ー^;;;

一応、試した範囲ではv1もv2もv3も実行結果は同じです。

AppleScript名:ムービー系の拡張子からFile Format UTIを取得する v2
– Created 2016-10-24 by Takaaki Naganoya
– Modified 2016-10-25 by Takaaki Naganoya
– Modified 2016-10-25 by Shane Stanley
– 2016 Piyomaru Software
use AppleScript version “2.4″
use framework “Foundation”
use framework “AVFoundation”
use framework “AppKit”
use scripting additions
–http://piyocast.com/as/archives/4287

set aRes to retFileFormatUTI(“mov”) of me
–>  (NSString) “com.apple.quicktime-movie”

set aRes to retFileFormatUTI(“mp4″) of me
–>  (NSString) “public.mpeg-4″

set aRes to retFileFormatUTI(“m4v”) of me
–>  (NSString) “com.apple.m4v-video”

set aRes to retFileFormatUTI(“m4a”) of me
–>  (NSString) “com.apple.m4a-audio”

set aRes to retFileFormatUTI(“3gp”) of me
–>  (NSString) “public.3gpp”

set aRes to retFileFormatUTI(“3gp2″) of me
–>  (NSString) “public.3gpp2″

set aRes to retFileFormatUTI(“caf”) of me
–>  (NSString) “com.apple.coreaudio-format”

set aRes to retFileFormatUTI(“wav”) of me
–>  (NSString) “com.microsoft.waveform-audio”

set aRes to retFileFormatUTI(“aif”) of me
–>  (NSString) “public.aifc-audio”

set aRes to retFileFormatUTI(“aifc”) of me
–>  (NSString) “public.aifc-audio”

set aRes to retFileFormatUTI(“amr”) of me
–>  (NSString) “org.3gpp.adaptive-multi-rate-audio”

set aRes to retFileFormatUTI(“mp3″) of me
–>  (NSString) “public.mp3″

set aRes to retFileFormatUTI(“au”) of me
–>  (NSString) “public.au-audio”

set aRes to retFileFormatUTI(“ac3″) of me
–>  (NSString) “public.ac3-audio”

on retFileFormatUTI(aExt)
  set theWorkspace to current application’s NSWorkspace’s sharedWorkspace()
  
set valList to {“com.apple.quicktime-movie”, “public.mpeg-4″, “com.apple.m4v-video”, “com.apple.m4a-audio”, “public.3gpp”, “public.3gpp2″, “com.apple.coreaudio-format”, “com.microsoft.waveform-audio”, “public.aiff-audio”, “public.aifc-audio”, “org.3gpp.adaptive-multi-rate-audio”, “public.mp3″, “public.au-audio”, “public.ac3-audio”}
  
repeat with aUTI in valList
    if (theWorkspace’s filenameExtension:aExt isValidForType:aUTI) as boolean then
      return (current application’s NSString’s stringWithString:aUTI)
    end if
  end repeat
  
error “Invalid Constant String”
end retFileFormatUTI

★Click Here to Open This Script 

AppleScript名:ムービー系の拡張子からFile Format UTIを取得する v3
– Created 2016-10-24 by Takaaki Naganoya
– Modified 2016-10-25 by Shane Stanley
– 2016 Piyomaru Software
use AppleScript version “2.4″
use framework “Foundation”
use framework “AVFoundation”
use scripting additions
use BridgePlus : script “BridgePlus” –Version 1.3.2以降
load framework

–http://piyocast.com/as/archives/4287

set aRes to retFileFormatUTI(“mov”) of me
–>  (NSString) “com.apple.quicktime-movie”

set aRes to retFileFormatUTI(“mp4″) of me
–>  (NSString) “public.mpeg-4″

set aRes to retFileFormatUTI(“m4v”) of me
–>  (NSString) “com.apple.m4v-video”

set aRes to retFileFormatUTI(“m4a”) of me
–>  (NSString) “com.apple.m4a-audio”

set aRes to retFileFormatUTI(“3gp”) of me
–>  (NSString) “public.3gpp”

set aRes to retFileFormatUTI(“3gp2″) of me
–>  (NSString) “public.3gpp2″

set aRes to retFileFormatUTI(“caf”) of me
–>  (NSString) “com.apple.coreaudio-format”

set aRes to retFileFormatUTI(“wav”) of me
–>  (NSString) “com.microsoft.waveform-audio”

set aRes to retFileFormatUTI(“aif”) of me
–>  (NSString) “public.aifc-audio”

set aRes to retFileFormatUTI(“aifc”) of me
–>  (NSString) “public.aifc-audio”

set aRes to retFileFormatUTI(“amr”) of me
–>  (NSString) “org.3gpp.adaptive-multi-rate-audio”

set aRes to retFileFormatUTI(“mp3″) of me
–>  (NSString) “public.mp3″

set aRes to retFileFormatUTI(“au”) of me
–>  (NSString) “public.au-audio”

set aRes to retFileFormatUTI(“ac3″) of me
–>  (NSString) “public.ac3-audio”

on retFileFormatUTI(aExt as string)
  return (current application’s SMSForder’s UTIForExtension:aExt)
end retFileFormatUTI

★Click Here to Open This Script 

2016/10/24 指定ムービーを指定形式に変換する

指定のムービーファイルを指定形式に変換するAppleScriptです。

オリジナルはShane StanleyがAppleScript Users-MLに投稿したものですが、変換(エクスポート)形式が固定で指定されていました(ムービーからオーディオ部のみ書き出し)。

たいへんに有用性が高いものの、エクスポート形式固定のままでは使い勝手がいまひとつだったので、形式を可変指定できるように改修してみました。

エクスポート形式で指定可能なものは、このように一覧を取得できます。この中から選択してください。

本Scriptをそのまま実行すると、指定のムービーファイルから音声部分のみ、同一ファイル名で拡張子を「m4a」にした別ファイルをオリジナルのファイルと同一フォルダ内に書き出します。オリジナルのファイルは消去せずにそのまま残す設定です。

AppleScript名:指定ムービーを指定形式に変換する
– Created 2016-10-24 by Shane Stanley
– Modified 2016-10-24 by Takaaki Naganoya
use AppleScript version “2.4″
use framework “Foundation”
use framework “AVFoundation”
use scripting additions
–http://piyocast.com/as/archives/4286

set posixPath to POSIX path of (choose file with prompt “Choose a Movie file:”)
my convertMovieAt:posixPath ToType:“AVAssetExportPresetAppleM4A” withExtension:“m4a” deleteOriginal:false

–指定のムービーを、指定の書き出しプリセットで、指定のファイル形式で、オリジナルファイルを削除するかどうか指定しつつ書き出し
on convertMovieAt:(posixPath as string) ToType:(assetTypeStr as string) withExtension:(aExt as string) deleteOriginal:(deleteFlag as boolean)
  set theURL to current application’s |NSURL|’s fileURLWithPath:posixPath
  
  
set aPreset to current application’s NSString’s stringWithString:assetTypeStr
  
  
– set destination to use same path, different extension
  
set destURL to theURL’s URLByDeletingPathExtension()’s URLByAppendingPathExtension:aExt
  
set theAsset to current application’s AVAsset’s assetWithURL:theURL
  
  
– check asset can be converted
  
set allowedPresets to current application’s AVAssetExportSession’s exportPresetsCompatibleWithAsset:theAsset
  
if (allowedPresets’s containsObject:aPreset) as boolean is false then
    error “Can’t export this file as an .” & aExt & ” file.”
  end if
  
  
– set up export session
  
set fileTypeUTIstr to retFileFormatUTI(aExt) of me –ファイル拡張子からFile Type UTIを求める
  
set theSession to current application’s AVAssetExportSession’s exportSessionWithAsset:theAsset presetName:aPreset
  
theSession’s setOutputFileType:fileTypeUTIstr
  
theSession’s setOutputURL:destURL
  
  
– begin export and poll for completion
  
theSession’s exportAsynchronouslyWithCompletionHandler:(missing value)
  
repeat
    set theStatus to theSession’s status() as integer
    
if theStatus < 3 then
      delay 0.2
    else
      exit repeat
    end if
  end repeat
  
  
– throw error if it failed
  
if theStatus = (current application’s AVAssetExportSessionStatusFailed) as integer then
    error (theSession’s |error|()’s localizedDescription() as text)
  end if
  
  
– delete original if required
  
if deleteFlag then
    current application’s NSFileManager’s defaultManager()’s removeItemAtURL:theURL |error|:(missing value)
  end if
end convertMovieAt:ToType:withExtension:deleteOriginal:

on retFileFormatUTI(aExt as string)
  set aUTIstr to getUTIfromNameExtension(aExt) of me
  
set keyList to {current application’s AVFileTypeQuickTimeMovie, current application’s AVFileTypeMPEG4, current application’s AVFileTypeAppleM4V, current application’s AVFileTypeAppleM4A, current application’s AVFileType3GPP, current application’s AVFileType3GPP2, current application’s AVFileTypeCoreAudioFormat, current application’s AVFileTypeWAVE, current application’s AVFileTypeAIFF, current application’s AVFileTypeAIFC, current application’s AVFileTypeAMR, current application’s AVFileTypeMPEGLayer3, current application’s AVFileTypeSunAU, current application’s AVFileTypeAC3}
  
set valList to {“com.apple.quicktime-movie”, “public.mpeg-4″, “com.apple.m4v-video”, “com.apple.m4a-audio”, “public.3gpp”, “public.3gpp2″, “com.apple.coreaudio-format”, “com.microsoft.waveform-audio”, “public.aiff-audio”, “public.aifc-audio”, “org.3gpp.adaptive-multi-rate-audio”, “public.mp3″, “public.au-audio”, “public.ac3-audio”}
  
if aUTIstr is not in valList then
    error “Invalid Constant String”
  end if
  
  
set aPos to offsetOf(valList, aUTIstr) of me
  
set cRes to contents of item aPos of keyList
  
return cRes
end retFileFormatUTI

on offsetOf(aList as list, aTarg)
  set aArray to current application’s NSArray’s arrayWithArray:aList
  
set aIndex to (aArray’s indexOfObject:aTarg) as number
  
return (aIndex + 1)
end offsetOf

–ファイル拡張子からUTI文字列を取得する
on getUTIfromNameExtension(anExtStr as string)
  set aTempPath to (POSIX path of (path to temporary items from user domain))
  
set aTempName to (current application’s NSUUID’s UUID()’s UUIDString()) as string
  
set bTempPath to (aTempPath & aTempName & “.” & anExtStr)
  
do shell script “touch “ & quoted form of bTempPath
  
set utiStr to current application’s NSWorkspace’s sharedWorkspace()’s typeOfFile:bTempPath |error|:(missing value)
  
do shell script “rm -f “ & quoted form of bTempPath
  
return utiStr as string
end getUTIfromNameExtension

★Click Here to Open This Script 

2016/10/24 ムービー系の拡張子からFile Format UTIを取得する

ムービー系のファイル拡張子(movとか)から、File Format UTI(AVFileTypeQuickTimeMovieとか)を取得するAppleScriptです。

filetypeuti.jpg

だいたい予想どおりの動きをしていますが、下位ルーチン(ファイル拡張子からUTIを取得する)の動作の問題か、AIFF audioとAIFC audioが同一視されています(どちらもAIFC=public.aifc-audioになる)。AIFFとAIFCの区別ができていない点にのみ注意を要します。

AppleScript名:ムービー系の拡張子からFile Format UTIを取得する
– Created 2016-10-24 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version "2.4"
use framework "Foundation"
use framework "AVFoundation"
use framework "AppKit"
use scripting additions
–http://piyocast.com/as/archives/4283

set aRes to retFileFormatUTI("mov") of me
–>  (NSString) "com.apple.quicktime-movie"

set aRes to retFileFormatUTI("mp4") of me
–>  (NSString) "public.mpeg-4"

set aRes to retFileFormatUTI("m4v") of me
–>  (NSString) "com.apple.m4v-video"

set aRes to retFileFormatUTI("m4a") of me
–>  (NSString) "com.apple.m4a-audio"

set aRes to retFileFormatUTI("3gp") of me
–>  (NSString) "public.3gpp"

set aRes to retFileFormatUTI("3gp2") of me
–>  (NSString) "public.3gpp2"

set aRes to retFileFormatUTI("caf") of me
–>  (NSString) "com.apple.coreaudio-format"

set aRes to retFileFormatUTI("wav") of me
–>  (NSString) "com.microsoft.waveform-audio"

set aRes to retFileFormatUTI("aif") of me
–>  (NSString) "public.aifc-audio"

set aRes to retFileFormatUTI("aifc") of me
–>  (NSString) "public.aifc-audio"

set aRes to retFileFormatUTI("amr") of me
–>  (NSString) "org.3gpp.adaptive-multi-rate-audio"

set aRes to retFileFormatUTI("mp3") of me
–>  (NSString) "public.mp3"

set aRes to retFileFormatUTI("au") of me
–>  (NSString) "public.au-audio"

set aRes to retFileFormatUTI("ac3") of me
–>  (NSString) "public.ac3-audio"

on retFileFormatUTI(aExt as string)
  set aUTIstr to getUTIfromNameExtension(aExt) of me
  
set keyList to {current application’s AVFileTypeQuickTimeMovie, current application’s AVFileTypeMPEG4, current application’s AVFileTypeAppleM4V, current application’s AVFileTypeAppleM4A, current application’s AVFileType3GPP, current application’s AVFileType3GPP2, current application’s AVFileTypeCoreAudioFormat, current application’s AVFileTypeWAVE, current application’s AVFileTypeAIFF, current application’s AVFileTypeAIFC, current application’s AVFileTypeAMR, current application’s AVFileTypeMPEGLayer3, current application’s AVFileTypeSunAU, current application’s AVFileTypeAC3}
  
set valList to {"com.apple.quicktime-movie", "public.mpeg-4", "com.apple.m4v-video", "com.apple.m4a-audio", "public.3gpp", "public.3gpp2", "com.apple.coreaudio-format", "com.microsoft.waveform-audio", "public.aiff-audio", "public.aifc-audio", "org.3gpp.adaptive-multi-rate-audio", "public.mp3", "public.au-audio", "public.ac3-audio"}
  
if aUTIstr is not in valList then
    error "Invalid Constant String"
  end if
  
  
set aPos to offsetOf(valList, aUTIstr) of me
  
set cRes to contents of item aPos of keyList
  
return cRes
end retFileFormatUTI

on offsetOf(aList as list, aTarg)
  set aArray to current application’s NSArray’s arrayWithArray:aList
  
set aIndex to (aArray’s indexOfObject:aTarg) as number
  
return (aIndex + 1)
end offsetOf

–ファイル拡張子からUTI文字列を取得する
on getUTIfromNameExtension(anExtStr as string)
  set aTempPath to (POSIX path of (path to temporary items from user domain))
  
set aTempName to (current application’s NSUUID’s UUID()’s UUIDString()) as string
  
set bTempPath to (aTempPath & aTempName & "." & anExtStr)
  
do shell script "touch " & quoted form of bTempPath
  
set utiStr to current application’s NSWorkspace’s sharedWorkspace()’s typeOfFile:bTempPath |error|:(missing value)
  
do shell script "rm -f " & quoted form of bTempPath
  
return utiStr as string
end getUTIfromNameExtension

★Click Here to Open This Script 

2016/10/17 リマインダーで指定のリスト内の終わっていない項目をテキストで返す

リマインダー(Reminders.app)の指定名称のリスト内で、まだ終わっていない(completedがfalseな)項目をテキストで返すAppleScriptです。

リマインダーでさまざまなToDo管理を行っており、その一環としてマンガの発売予定日をまとめたリストがあるのですが、

rem1_resized.png

項目を選んでコピーし、普通にテキストエディタ(ここではmiを利用)にペーストすると、

rem2_resized.png

のようになります。親切というか、出来過ぎというか、不必要な情報まで入ってくるので、もっとシンプルに名称だけ取得してくれたほうが使い勝手がいいと思います(個人の意見です)。

そこで、さくっとAppleScriptを組んで件名(name)をテキストとして連結して取得できるようにしてみました。

AppleScript名:リマインダーで指定のリスト内の終わっていない項目をテキストで返す
– Created 2015-10-14 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4278

tell application “Reminders”
  tell list “マンガの発売予定日”
    set rList to name of every reminder whose completed is false
    
–リマインド項目は以下のように返ってくる
    
–>{{due date:date “2016年10月19日水曜日 12:00:00″, modification date:date “2016年10月5日水曜日 11:08:23″, class:reminder, body:missing value, completed:false, id:”x-apple-reminder://05A1CBEE-0737-47A7-ABB9-9AF582A604AF”, creation date:date “2016年10月5日水曜日 11:05:36″, name:”87 Clockers 9巻 2016/10/19″, completion date:missing value, container:list id “FA4B1A00-E1AD-4E81-921F-FDCBC7191FA3″ of application “Reminders”, priority:0, remind me date:date “2016年10月19日水曜日 12:00:00″}…. }
  end tell
end tell

set aRes to retStrFromArrayWithDelimiter(rList, return) of me
–>
(*
“87 Clockers 9巻 2016/10/19
マリアージュ 神の雫最終章 5巻 2016/10/21
女騎士、経理になる。 3巻 2016/10/24
王様達のヴァイキング 11巻 2016/10/28
アオイホノオ 16巻 2016/11/12
ピアノのムシ 9巻 2016/11/16
機動戦士Zガンダム Define 12巻 2016/11/19
MIX 10巻 2016/12/8
大砲とスタンプ 6巻 2016/12/21
スティーブス 6巻 2017/1/13
甘城ブリリアントパーク 9巻 2017/2/15
宇宙兄弟 30巻 2017/2/24
ヒーローカンパニー 10巻 2017/6/6″
*)

–リストを指定デリミタをはさんでテキスト化
on retStrFromArrayWithDelimiter(aList, aDelim)
  set anArray to current application’s NSArray’s arrayWithArray:aList
  
set aRes to anArray’s componentsJoinedByString:aDelim
  
return aRes as text
end retStrFromArrayWithDelimiter

★Click Here to Open This Script 

2016/09/20 連番JPEGファイルを読み込んで連結したPDFを作成(新規作成)

連番JPEG画像を番号順にソートして、順次連結したPDFを新規作成するAppleScriptです。

jpeg_catfiles.jpg

このような連番画像を連結して任意のファイル名のPDFに合成します。出来上がるPDFは、元のJPEGファイルの圧縮度に応じて大きくなります。

新規作成よりも、すでに存在しているPDFにJPEGファイルを連結するほうが実用的だと思います。

AppleScript名:連番JPEGファイルを読み込んで連結したPDFを作成(新規作成)
– Created 2016-09-20 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “QuartzCore”
use framework “Quartz”
use framework “AppKit”

set aExt to “.jpg”
set aFol to choose folder
set fList to getFilePathList(aFol, aExt) of me
set f2List to my sort1DList:fList ascOrder:true –sort by ascending

set newFile to POSIX path of (choose file name with prompt “新規PDFファイルの名称を選択”)
set newFilePath to current application’s NSString’s stringWithString:newFile

–Make Blank PDF
set aPDFdoc to current application’s PDFDocument’s alloc()’s init()

set pageNum to 0

repeat with i in f2List
  set j to contents of i
  
set aURL to (current application’s |NSURL|’s fileURLWithPath:j)
  
set bImg to (current application’s NSImage’s alloc()’s initWithContentsOfURL:aURL)
  (
aPDFdoc’s insertPage:(current application’s PDFPage’s alloc()’s initWithImage:bImg) atIndex:pageNum)
  
set pageNum to pageNum + 1
end repeat

aPDFdoc’s writeToFile:newFilePath

–ASOCで指定フォルダのファイルパス一覧取得(拡張子指定つき)
on getFilePathList(aFol, aExt)
  set aPath to current application’s NSString’s stringWithString:(POSIX path of aFol)
  
set aFM to current application’s NSFileManager’s defaultManager()
  
set nameList to (aFM’s contentsOfDirectoryAtPath:aPath |error|:(missing value)) as list
  
set anArray to current application’s NSMutableArray’s alloc()’s init()
  
  
repeat with i in nameList
    set j to i as text
    
if (j ends with aExt) and (j does not start with “.”) then –exclude invisible files
      set newPath to (aPath’s stringByAppendingString:j)
      (
anArray’s addObject:newPath)
    end if
  end repeat
  
  
return anArray as list
end getFilePathList

–1D List(文字)をsort / ascOrderがtrueだと昇順ソート、falseだと降順ソート
on sort1DList:theList ascOrder:aBool
  set aDdesc to current application’s NSSortDescriptor’s sortDescriptorWithKey:“self” ascending:aBool selector:“localizedCaseInsensitiveCompare:”
  
set theArray to current application’s NSArray’s arrayWithArray:theList
  
return (theArray’s sortedArrayUsingDescriptors:{aDdesc}) as list
end sort1DList:ascOrder:

★Click Here to Open This Script 

2016/07/28 Bookフォルダリネーム

書籍の原稿管理用のAppleScriptです。原稿は章ごとにフォルダ分けしており、

 9999 フォルダ名あるいはファイル名

といったように、仮想ノンブルを前に付けています。この仮想ノンブルで前後関係を制御しており、基本的に数字の大小だけが重要です。

whole.png

最後に1冊分PDFにまとめてビルドするAppleScriptで、書き出し対象のフォルダ以下のMarkdownファイルとPagesのファイルをピックアップし、ファイル名でソート。ソート順は仮想ノンブルによってコントロールされることになります。

Finderの並べ替えを使っているので、シンプルでわかりやすく、使い勝手もよいのですが……ただひとつ難点が。

途中にコンテンツを挿入したい場合にも仮想ノンブルでコントロールしつつ、途中に入るように操作するのですが……場合によってはフォルダおよび書類の仮想ノンブル部分だけリネームする必要が出てきます。

とくに、目下改定作業中の「AppleScript最新リファレンス」のコマンドリファレンス部分は、初版から大幅に改定・補充を行っているため、ひんぱんにリネームが発生して大変です(ーー;;

そこで、こんなScriptを組んでリネーム作業だけ自動化してみました。

Finder上でフォルダを選択して本Scriptを実行すると、選択中のフォルダ名から仮想ノンブルの番号を取得し、

before2.png

内包するフォルダに入っているファイルについても、すべて仮想ノンブル部分を書き換えます。

after2.png

AppleScript名:Bookフォルダリネーム
– Created 2016-07-28 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use framework “Foundation”
use scripting additions
# http://piyocast.com/as/archives/4177

set aStep to 10

tell application “Finder”
  set aSel to (selection as alias list)
end tell
if aSel = {} or aSel = “” then return
set aFol to first item of aSel

tell application “Finder”
  –選択中のフォルダの名称を取得
  
set aName to name of aFol
  
  
–選択中のフォルダに入っている書類を取得
  
tell folder (aFol as string)
    set fList to name of every file as alias list
  end tell
end tell

set {startNumStr, folderNameStr} to parseNumAndString(aName) of me
set startNum to startNumStr as integer

set aRes to sort1DListOrder(fList, true) of me –昇順でファイル名をソート

set aFolStr to aFol as string
set aCount to startNumStr
tell application “Finder”
  repeat with i in aRes
    set anAlias to (aFolStr & (i as string)) as alias
    
set tmpName to name of anAlias
    
    
set {curNum, curName} to parseNumAndString(tmpName) of me
    
set newName to (retZeroPaddingText(aCount, length of curNum) of me) & ” “ & curName
    
    
set name of anAlias to newName
    
    
set aCount to aCount + aStep
  end repeat
end tell

–文字列から先頭にあるとおぼしき数値と、スペースで区切られた後続の文字列を分離する
on parseNumAndString(aName)
  set aOffset to offset of ” “ in aName
  
if aOffset = 0 then error “Illigal Format”
  
set aNum to text 1 thru (aOffset - 1) of aName
  
set aText to text (aOffset + 1) thru -1 of aName
  
return {aNum, aText}
end parseNumAndString

–1D Listをsort / ascOrderがtrueだと昇順ソート、falseだと降順ソート
on sort1DListOrder(theList, aBool)
  set aDdesc to current application’s NSSortDescriptor’s sortDescriptorWithKey:“self” ascending:aBool selector:“localizedCaseInsensitiveCompare:”
  
set theArray to current application’s NSArray’s arrayWithArray:theList
  
return (theArray’s sortedArrayUsingDescriptors:{aDdesc}) as list
end sort1DListOrder

on retZeroPaddingText(aNum as integer, aDigitNum as integer)
  if aNum > (((10 ^ aDigitNum) as integer) - 1) then return “” –Range Check
  
set aFormatter to current application’s NSNumberFormatter’s alloc()’s init()
  
aFormatter’s setUsesGroupingSeparator:false
  
aFormatter’s setAllowsFloats:false
  
aFormatter’s setMaximumIntegerDigits:aDigitNum
  
aFormatter’s setMinimumIntegerDigits:aDigitNum
  
aFormatter’s setPaddingCharacter:“0″
  
set aStr to aFormatter’s stringFromNumber:(current application’s NSNumber’s numberWithFloat:aNum)
  
return aStr as string
end retZeroPaddingText

★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 

2016/05/12 表計算ソフトの選択範囲のデータをMarkdown形式の表テキストに変換する

「技術書典」に向けてAppleScript本をMarkdownエディタ「MacDown」で快調に執筆中の今日このごろ。世の中にあふれるMarkdownエディタから厳選してMacDownに行き着きました。

md0.png

一応、最低限レベルながらAppleScriptに対応しているMarkdownエディタは「MacDown」だけでした。

AppleScript対応/非対応を問わず、メジャーなエディタを調べてこれが一番よかったのは……本Blogに掲載している色分けプログラムリスト(HTML)を本文中にペーストして、テキストとしてではなくそのままHTMLとして解釈して表にレンダリングしてくれたこと(仕様的に微妙な点なので、将来にわたって維持されるかどうかは不明)。

md11_resized.png

他のエディタでは本文中にHTMLのタグを入れるという手は使えませんでした(Tableタグだけで実験したので、もしかするともうちょっといろいろやってみると認識されたのかも??)。

そんな、自分のニーズにばっちり合っているMacDownですが、使っているうちにいろいろ山のように不満も出てくるもので、そのうちのひとつに「表データを入れにくい」というものがありました。

スクラッチでゼロから表を起こすのには、Markdown書式の軽量マークアップの表タグはとてもいいと思うのですが、「ありもののデータがすでに存在している」場合には、少々困ってしまうところです。

あまりにあまねく誰もが直面する問題なので、すでにどこかで誰かが同じような変換ツールを書いている可能性が高いですが、この程度ならたいした手間はかからないので、AppleScriptで書いてしまいました。

まずは、Markdown書式における表の作り方を確認。

header1|header2|header3
—|—|—
col1|col2|col3
col4|col5|col6
col7|col8|col9

最低限、こんな風に書いておけば表として認識されるようです(MacDown v0.6上で確認。他のエディタでは傾向が違うかも)。

md00_resized.png

ヘルプファイルやサンプルなどではもっとスペースを入れてきれいに書いてありましたが、そんなきれいに書く必要はないことがわかりました。

excel1.png

まずは、Microsoft Excel版(バージョン14.6.3にて確認)。Excel上のデータを選択してAppleScriptを実行すると、Markdown形式の表データがクリップボードに転送されます。MacDownにはselection(選択範囲)という予約語がないので、選択範囲に対して転送するという手が使えないため、クリップボードに転送しておいてユーザーにペースト動作を行ってもらおうというところです。

numbers1_resized.png

次に、Numbers(v3.6.2)版。Numbersのドキュメント上でデータを選択しておくと、Markdown形式の表データがクリップボードに転送されます。Excelバージョンのプログラムを流用するために、Excelと同様のデータの取得方法を行うルーチンをもってきました(すでに作ってあった)。Numbersの選択範囲のデータの返し方がアレでナニなのでやや苦労していますが、サブルーチンの再利用ですぐに差し替えできました。

md10_resized.png

AppleScript名:Excelの選択範囲を取得してMarkdown書式の表に変換する
– Created 2016-05-12 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

set aSeparator to “|”
–set aCell to “—:” –Align Right
set aCell to “:—:” –Align Center
–set aCell to “:—” –Align Left

set aLineTerminator to return

tell application “Microsoft Excel”
  set aSel to string value of selection
end tell

if aSel = “” then
  tell current application
    display dialog “Excelのワークシート上で何も選択されていません” with icon 2 buttons {“OK”} default button 1 with title “選択範囲エラー”
    
return
  end tell
end if

–選択範囲の横幅チェック、ヘッダー部のデータ作成
set colNum to length of (contents of first item of aSel)
set sepList to {}
repeat colNum times
  set end of sepList to aCell
end repeat

–ヘッダーセパレータを配列の2番目に挿入する
set newList to insListItem(aSel, sepList, 2) of me

–Markdown書式の表を文字列で作成し、クリップボードに転送する
set outStr to “”
repeat with i in newList
  set outStr to outStr & retStrFromArrayWithDelimiter(contents of i, aSeparator) of me & aLineTerminator
end repeat

set the clipboard to outStr –クリップボードに転送

–指定のリスト(2次元配列)に、要素(1次元配列)を、指定箇所(1はじまり)にインサートする
on insListItem(aList as list, insList as list, insertItemNum as integer)
  set newList to {}
  
set itemCounter to 1
  
repeat with i in aList
    if itemCounter = insertItemNum then
      set the end of newList to insList
    end if
    
set end of newList to (contents of i)
    
set itemCounter to itemCounter + 1
  end repeat
  
return newList
end insListItem

–リストを指定デリミタをはさんでテキスト化
on retStrFromArrayWithDelimiter(aList as list, aDelim as string)
  set anArray to current application’s NSArray’s arrayWithArray:aList
  
set aRes to anArray’s componentsJoinedByString:aDelim
  
return aRes as text
end retStrFromArrayWithDelimiter

on retArrowText(aList as list, aDelim as string) –自分のASでよく使うハンドラ名称なので、同じものを用意
  return my retStrFromArrayWithDelimiter(aList, aDelim)
end retArrowText

★Click Here to Open This Script 

AppleScript名:Numbersの選択範囲を取得してMarkdown書式の表に変換する
– Created 2016-05-12 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

set aSeparator to “|”
–set aCell to “—:” –Align Right
set aCell to “:—:” –Align Center
–set aCell to “:—” –Align Left

set aLineTerminator to return

–Numbersの選択範囲からExcel仕様で2Dリストを返す
set aSel to get2DListFromNumbersSelection() of me

if aSel = “” then
  tell current application
    display dialog “Numbersのワークシート上で何も選択されていません” with icon 2 buttons {“OK”} default button 1 with title “選択範囲エラー”
    
return
  end tell
end if

–選択範囲の横幅チェック、ヘッダー部のデータ作成
set colNum to length of (contents of first item of aSel)
set sepList to {}
repeat colNum times
  set end of sepList to aCell
end repeat

–ヘッダーセパレータを配列の2番目に挿入する
set newList to insListItem(aSel, sepList, 2) of me

–Markdown書式の表を文字列で作成し、クリップボードに転送する
set outStr to “”
repeat with i in newList
  set outStr to outStr & retStrFromArrayWithDelimiter(contents of i, aSeparator) of me & aLineTerminator
end repeat

set the clipboard to outStr –クリップボードに転送

–指定のリスト(2次元配列)に、要素(1次元配列)を、指定箇所(1はじまり)にインサートする
on insListItem(aList as list, insList as list, insertItemNum as integer)
  set newList to {}
  
set itemCounter to 1
  
repeat with i in aList
    if itemCounter = insertItemNum then
      set the end of newList to insList
    end if
    
set end of newList to (contents of i)
    
set itemCounter to itemCounter + 1
  end repeat
  
return newList
end insListItem

–リストを指定デリミタをはさんでテキスト化
on retStrFromArrayWithDelimiter(aList as list, aDelim as string)
  set anArray to current application’s NSArray’s arrayWithArray:aList
  
set aRes to anArray’s componentsJoinedByString:aDelim
  
return aRes as text
end retStrFromArrayWithDelimiter

on retArrowText(aList as list, aDelim as string) –自分のASでよく使うハンドラ名称なので、同じものを用意
  return my retStrFromArrayWithDelimiter(aList, aDelim)
end retArrowText

–テキストを指定デリミタでリスト化
on parseByDelim(aData, aDelim)
  set aText to current application’s NSString’s stringWithString:aData
  
set aList to aText’s componentsSeparatedByString:aDelim
  
return aList as list
end parseByDelim

on get2DListFromNumbersSelection()
  –Numbersで選択範囲を縦に区切ったリストを返す
  
tell application “Numbers”
    tell document 1
      tell sheet 1
        tell table 1
          try
            set selList to value of every cell of selection range –選択範囲のデータを取得
          on error
            return “” –何も選択されてなかった場合
          end try
          
          
set selName to name of selection range –選択範囲のrange情報を取得
          
set {s1, s2} to parseByDelim(selName, “:”) of me
          
          
–始点の情報を取得する
          
set s1Row to (address of row of range s1) as integer
          
set s1Column to (address of column of range s1) as integer
          
          
–終点の情報を取得する
          
set s2Row to (address of row of range s2) as integer
          
set s2Column to (address of column of range s2) as integer
          
          
–選択範囲の情報を取得する
          
set selHeight to s2Row - s1Row + 1 –高さ(Height of selection range)
          
set selWidth to s2Column - s1Column + 1 –幅(Width of selection range)
          
        end tell
      end tell
    end tell
  end tell
  
  
set aLen to length of selList
  
set aaLen to selHeight
  
  
set bList to {}
  
repeat with i from 1 to aaLen
    set aHoriList to {}
    
    
repeat with ii from 1 to selWidth
      set j1 to ii + (i - 1) * selWidth
      
set tmpCon to contents of item j1 of selList
      
      
set aClass to class of tmpCon
      
if aClass = number or aClass = integer or aClass = real then
        set tmpCon to tmpCon as integer
      end if
      
      
set the end of aHoriList to tmpCon
    end repeat
    
    
set the end of bList to aHoriList
  end repeat
  
  
return bList
  
end get2DListFromNumbersSelection

★Click Here to Open This Script 

2016/03/11 shellの出力結果をスペースでparseする

shellの出力結果を利用する際に、スペースでparseするAppleScriptです。

連続するスペースキャラクターでparseしつつ不要なゴミを削除して必要な項目だけ取得するというものです。

AppleScript名:shellの出力結果をスペースでparseする
– Created 2016-03-11 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"

set a to "3 0×4248387 1920×1200 0 0 -1920 -1200 0 [main]"

set aStr to (current application’s NSString’s stringWithString:a)
–> (NSString) "3 0×4248387 1920×1200 0 0 -1920 -1200 0 [main]"

set aLine to (aStr’s componentsSeparatedByString:" ")
–> (NSArray) {"3", "", "0×4248387", "", "", "", "", "1920×1200", "", "", "", "", "", "0", "", "", "", "", "0", "", "-1920", "", "-1200", "", "", "", "", "", "0", "", "", "", "[main]"}

(aLine’s removeObject:"")
–> (NSArray) {"3", "0×4248387", "1920×1200", "0", "0", "-1920", "-1200", "0", "[main]"}

set bList to aLine as list
–>  {"3", "0×4248387", "1920×1200", "0", "0", "-1920", "-1200", "0", "[main]"}

★Click Here to Open This Script 

2016/02/28 Safariの履歴を読んでトップ10をテキストで出力する

SafariのHistory.plistを読み込んで、アクセス回数のトップ10をテキスト出力するAppleScriptです。

Safari 9.1+OS X 10.11環境を前提としています。plistの内容については、Safariのバージョンごとに変更が加わってもおかしくないので、メジャーバージョンアップ時には動作確認する必要があることでしょう。

他のアプリケーションの.plistファイルにアクセスするのはかなり野蛮な処理ですが(Sandbox環境では許可されない)、場合によっては有用なこともあるでしょう。

ちなみに、Scriptの実行例はサブマシン(MacBook Air 2011)で実行したものです。

AppleScript名:Safariの履歴を読んでトップ10をテキストで出力する
– Created 2016-02-27 by Takaaki Naganoya
– Piyomaru Software 2016
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

set libPath to (POSIX path of (path to library folder from user domain)) & “Safari/History.plist”
set aRec to retDictFromPlist(libPath) of me
set bRecList to aRec’s valueForKeyPath:“WebHistoryDates”

set bArray to sortRecListByLabel(bRecList, “visitCount”, false) of me
set outStr to “”
set aCount to 0

repeat with i from 0 to 100
  set anItem to (bArray’s objectAtIndex:i)
  
  
set aURL to (anItem’s valueForKeyPath:“”) as string
  
set aTitle to (anItem’s valueForKeyPath:“title”) as string
  
set aDispTItle to (anItem’s valueForKeyPath:“displayTitle”) as string
  
set aVisitCount to (anItem’s valueForKeyPath:“visitCount”) as string
  
set redirectURLs to (anItem’s valueForKeyPath:“redirectURLs”) as list
  
set lastVisitedDate to (anItem’s valueForKeyPath:“lastVisitedDate”) as string
  
  
if aURL does not start with “http://piyocast.com” then –自分のところのサイト関連を除外
    set aStr to ((aCount + 1) as string) & “:(” & (aVisitCount as string) & “)” & aTitle & return
    
set outStr to outStr & aStr
    
    
set aCount to aCount + 1
    
if aCount = 10 then exit repeat
    
  end if
  
  
end repeat

return outStr

(*
–>  ”1:(7657)radiko.jp
2:(608)Apple
3:(416)ソフトバンクWi-Fiスポット
4:(156)公式Apple Store(日本)- iPad Air、MacBook Pro、ほかにもたくさん
5:(125)Yahoo! JAPAN
6:(108)Mac整備済製品 - Apple Store (Japan)
7:(54)Apple-Style \”アップルなBlogをリンク\”
8:(44)PC Watch
9:(31)Facebook
10:(28)アップル - Mac Pro

*)

–Read plist
on retDictFromPlist(aPath as text)
  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
end retDictFromPlist

–リストに入れたレコードを、指定の属性ラベルの値でソート
on sortRecListByLabel(aRecList, aLabelStr, ascendF)
  set aArray to current application’s NSArray’s arrayWithArray:aRecList
  
set sortDesc to current application’s NSSortDescriptor’s alloc()’s initWithKey:aLabelStr ascending:ascendF
  
set sortDescArray to current application’s NSArray’s arrayWithObjects:sortDesc
  
set sortedArray to aArray’s sortedArrayUsingDescriptors:sortDescArray
  
return sortedArray
end sortRecListByLabel

★Click Here to Open This Script 

2016/02/13 文字列で与えたシェルコマンドを実行する

文字列で指定したシェルコマンド(例:pwd)をNSTaskで実行して結果を返すAppleScriptです。

do shell scriptとほぼ同じ内容で、実行途中に出力される文字列などについては無視して、結果だけ返しています。

AppleScript名:文字列で与えたシェルコマンドを実行する
– Created 2016-02-13 by Takaaki Naganoya
– 2016 Piyomaru Software
–以下を参照:
–http://stackoverflow.com/questions/412562/execute-a-terminal-command-from-a-cocoa-app
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

set aRes to (runCommandString(“pwd”) of me) as string
–>  ”/”

set bRes to (runCommandString(“cd & ls -la”) of me) as string

set cRes to (runCommandStringAtJapaneseEnv(“cd & ls -la”) of me) as string

–Lock Screen
–runCommandAtPath(”/System/Library/CoreServices/Menu Extras/User.menu/Contents/Resources/CGSession”, “-suspend”) of me

–文字列で与えたシェルコマンドを実行する
on runCommandString(commandStr as string)
  set aPipe to current application’s NSPipe’s pipe()
  
set aTask to current application’s NSTask’s alloc()’s init()
  
aTask’s setLaunchPath:“/bin/sh”
  
aTask’s setArguments:{“-c”, current application’s NSString’s stringWithFormat_(“%@”, commandStr)} –stringWithFormatはASOCに書き換え時の要注意点
  
aTask’s setStandardOutput:aPipe
  
set aFile to aPipe’s fileHandleForReading()
  
aTask’s |launch|()
  
return current application’s NSString’s alloc()’s initWithData:(aFile’s readDataToEndOfFile()) encoding:(current application’s NSUTF8StringEncoding)
end runCommandString

–日本語環境で、文字列で与えたシェルコマンドを実行する。結果に日本語文字列を含む出力を取得して返す
on runCommandStringAtJapaneseEnv(commandStr as string)
  set jComStr to “export LANG=en_US.UTF-8;” & commandStr
  
set jRes to runCommandString(jComStr) of me
  
return jRes
end runCommandStringAtJapaneseEnv

–指定したパスのシェルコマンドを実行する(結果などは取得しない)
on runCommandAtPath(commandPath as string, anArgument as string)
  set aTask to current application’s NSTask’s alloc()’s init()
  
aTask’s setLaunchPath:commandPath
  
aTask’s setArguments:(current application’s NSArray’s arrayWithObject:anArgument)
  
aTask’s |launch|()
end runCommandAtPath

★Click Here to Open This Script 

2016/01/10 レコードのリストからラベル値のみ抽出してユニーク化してソート v2

Cocoaの機能を用いて、レコードのリストからラベル値のみ抽出して、ユニーク化してソートして返すAppleScriptです。

Shane Stanleyから指摘があって、「そこもっと短くかけるよ〜」ということで、大幅に短くなりました(Thanks !!)。NSMutableSetを使うのは、とってもいい方法だと思います。

AppleScript名:レコードのリストからラベル値のみ抽出してユニーク化してソート v2
– Created 2016-01-08 by Takaaki Naganoya
– Modified 2016-01-08 by Shane Stanley (retEveryKeys)
– 2016 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"

set aDic to {{field1:"test 10", field2:"test 20"}, {field1:"test 11", field2:"test 21"}, {field1:"test 12", field2:"test 22"}, {field1:"test 13", field2:"test 23"}, {field1:"test 14", field2:"test 24"}, {field1:"test 15", field2:"test 25"}, {field1:"test 16", field2:"test 26"}, {field1:"test 17", field2:"test 27"}, {field1:"test 18", field2:"test 28"}, {field1:"test 19", field2:"test 29"}, {field1:"test 10", field2:"test 20"}, {field1:"test 11", field2:"test 21"}, {field1:"test 12", field2:"test 22"}, {field1:"test 13", field2:"test 23"}, {field1:"test 14", field2:"test 24"}, {field1:"test 15", field2:"test 25"}, {field1:"test 16", field2:"test 26"}, {field1:"test 17", field2:"test 27"}, {field1:"test 18", field2:"test 28"}, {field1:"test 19", field3:"test 29"}}

set theDataSource to current application’s NSMutableArray’s arrayWithArray:aDic
set kList to retEveryKeys(theDataSource) of me
–>  {"field1", "field2", "field3"}

–リストになったRecordのすべてのアイテムのkey値を取得してユニーク化してソートして返す
on retEveryKeys(aDic)
  set tempSet to current application’s NSMutableSet’s |set|()
  
repeat with aDict in aDic
    (tempSet’s addObjectsFromArray:(aDict’s allKeys()))
  end repeat
  
return (tempSet’s allObjects()’s sortedArrayUsingSelector:"compare:") as list
end retEveryKeys

★Click Here to Open This Script 

2016/01/08 レコードのリストからラベル値のみ抽出してユニーク化してソート

Cocoaの機能を用いて、レコードのリストからラベル値のみ抽出して、ユニーク化してソートして返すAppleScriptです。

AppleScript名:レコードのリストからラベル値のみ抽出してユニーク化してソート
– Created 2016-01-08 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"

set aDic to {{field1:"test 10", field2:"test 20"}, {field1:"test 11", field2:"test 21"}, {field1:"test 12", field2:"test 22"}, {field1:"test 13", field2:"test 23"}, {field1:"test 14", field2:"test 24"}, {field1:"test 15", field2:"test 25"}, {field1:"test 16", field2:"test 26"}, {field1:"test 17", field2:"test 27"}, {field1:"test 18", field2:"test 28"}, {field1:"test 19", field2:"test 29"}, {field1:"test 10", field2:"test 20"}, {field1:"test 11", field2:"test 21"}, {field1:"test 12", field2:"test 22"}, {field1:"test 13", field2:"test 23"}, {field1:"test 14", field2:"test 24"}, {field1:"test 15", field2:"test 25"}, {field1:"test 16", field2:"test 26"}, {field1:"test 17", field2:"test 27"}, {field1:"test 18", field2:"test 28"}, {field1:"test 19", field3:"test 29"}}

set theDataSource to current application’s NSMutableArray’s arrayWithArray:aDic
set kList to retEveryKeys(theDataSource) of me
–>  {"field1", "field2", "field3"}

–リストになったRecordのすべてのアイテムのkey値を取得してユニーク化してソートして返す
on retEveryKeys(aDic)
  set aLen to aDic’s |count|()
  
set tmpKeys to {}
  
  
repeat with i from 0 to (aLen - 1)
    set aRec to (aDic’s objectAtIndex:i)
    
set keyList to (aRec’s allKeys()) as list
    
set tmpKeys to tmpKeys & keyList
  end repeat
  
  
set aRes to uniquifyAndSort1DList(tmpKeys, true) of me
  
return aRes
end retEveryKeys

–1D Listをユニーク化してソート
on uniquifyAndSort1DList(theList, 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 cArray as list
  
return bList
end uniquifyAndSort1DList

★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/09 Safariと同じUser Agent文字列を組み立てる

Cocoaの機能を用いて、SafariのUser Agent名が返す文字列と同じものを生成するAppleScriptです。

さまざまな用途のために、AppleScriptでカスタムWebブラウザを作ることがあります。その際に、Safariと同じUser Agent名をWebサーバー側に返すことができるとメリットがあります。正確にいえば、Safariのフリをしてブラウズを行うことで、さまざまなトラブルを回避できます。

取得にあたっては、Safariを起動してSafariからUser Agent名を取るのではなく、WebKitが返すUser Agent情報に(未起動状態の)SafariのバージョンやビルドNo.をつないで生成しています。ビルドNo.についてはメジャーNo.の下3桁だけが出力されており、少々不思議なところですが、実物のSafariと合うように調整しています。

一応本ルーチンが正常に文字列を生成できているかのテストのために、SafariからUser Agent名を取得して比較を行い、同一の文字列が生成できていることを確認しています(実際に使うときには削除)。

AppleScript名:SafariのUser Agent文字を組み立てる
– Created 2015-11-09 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “Webkit”

set aRes to getSafariUserAgentString() of me
–>  ”Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_2) AppleWebKit/601.3.6 (KHTML, like Gecko) Version/9.0.2 Safari/601.3.6″

–ほんまもんのSafariからUser Agentを取得してテスト
tell application “Safari”
  tell document 1
    set dRes to do JavaScript “navigator.userAgent”
    
–>  ”Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_2) AppleWebKit/601.3.6 (KHTML, like Gecko) Version/9.0.2 Safari/601.3.6″
  end tell
end tell

return (aRes = dRes)
–>  true

–Safariと同じUser Agentの文字列を取得する
on getSafariUserAgentString()
  set aBundle to current application’s NSBundle’s bundleWithIdentifier:“com.apple.Safari”
  
set aVer to (aBundle’s infoDictionary()’s objectForKey:“CFBundleShortVersionString”) as text
  
–>  ”9.0.2″
  
  
set aBuildNo to (aBundle’s infoDictionary()’s objectForKey:“CFBundleVersion”) as text
  
–>  ”11601.3.6″
  
set {v1, v2, v3} to parseVersionNumber(aBuildNo) of me
  
set aV1 to current application’s NSString’s stringWithString:v1
  
set bV1 to aV1’s substringFromIndex:2 –3 characters from tail –?????
  
–> “601″
  
  
set bStr to retStrFromArrayWithDelimiter({bV1, v2, v3}, “.”) of me
  
  
set aWebView to current application’s WebView’s alloc()’s init()
  
set aRes to (aWebView’s stringByEvaluatingJavaScriptFromString:“navigator.userAgent”) as text
  
–>  ”Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_2) AppleWebKit/601.3.6 (KHTML, like Gecko)”
  
  
return (aRes & ” Version/” & aVer & ” Safari/” & bStr)
  
end getSafariUserAgentString

–バージョン番号文字列からメジャーバージョンを取り出し数値として返す
on parseVersionNumber(a)
  set aStr to current application’s NSString’s stringWithString:a
  
set aRes to (aStr’s componentsSeparatedByString:“.”)
  
set bRes to aRes’s allObjects()
  
return bRes as list
end parseVersionNumber

–リストを指定デリミタをはさんでテキスト化
on retStrFromArrayWithDelimiter(aList, aDelim)
  set anArray to current application’s NSArray’s arrayWithArray:aList
  
set aRes to anArray’s componentsJoinedByString:aDelim
  
return aRes as text
end retStrFromArrayWithDelimiter

★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/10/14 テキストとリストの変換

Cocoaの機能を用いて、listとtextの相互変換を行うAppleScriptです。

List -> Text

リストをそのままテキストにするもの(できるけど)ではなく、{”a”,”b”,”c”} のようなリストを、”a–> b–> c”のようにデリミタ(区切り)文字を介して連結する処理です。最初はそれだけの目的のために作ったルーチンでした。

この「項目間を→でつなぐ」という目的で作ったサブルーチン(retArrowText)が便利で他の用途に転用しまくったため、すっかり自分ローカルの方言になってしまいました。今後は「retStrFromArrayWithDelimiter」を使っていくつもりです。

Text -> List

テキストを指定デリミタ(改行など)で区切ってリストにするものです。ついでなので作っておきました。自分ローカルで「parseByDelim」と呼んでいたもので、こちらの名前はそれほどおかしくないので、このまま行こうかと。

AppleScript名:ASOCでテキストとリストの変換
– Created 2015-10-14 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

set aList to {“あああああ”, “いいいいいいいい”, “ううううううううううう”, “えええええええええええ”, “おおおおおおおおおお”}
set aRes to my retStrFromArrayWithDelimiter(aList, return)
(*
“あああああ
いいいいいいいい
ううううううううううう
えええええええええええ
おおおおおおおおおお”
*)

set bList to my parseByDelim(aRes, return)
–>  {”あああああ”, “いいいいいいいい”, “ううううううううううう”, “えええええええええええ”, “おおおおおおおおおお”}

–リストを指定デリミタをはさんでテキスト化
on retStrFromArrayWithDelimiter(aList, aDelim)
  set anArray to current application’s NSArray’s arrayWithArray:aList
  
set aRes to anArray’s componentsJoinedByString:aDelim
  
return aRes as text
end retStrFromArrayWithDelimiter

on retArrowText(aList, aDelim) –自分のASでよく使うハンドラ名称なので、同じものを用意
  return my retStrFromArrayWithDelimiter(aList, aDelim)
end retArrowText

–テキストを指定デリミタでリスト化
on parseByDelim(aData, aDelim)
  set aText to current application’s NSString’s stringWithString:aData
  
set aList to aText’s componentsSeparatedByString:aDelim
  
return aList as list
end parseByDelim

★Click Here to Open This Script 

2015/09/30 Unicodeの文字をNormalizeする

Cocoaの機能を用いて、NSStringの文字列をNormalizeするAppleScriptです。

たまたま仕事で、PDFの内容(テキスト)をparseしてデータを取り出して文字コードの範囲チェックをしていたところ・・・特定の文字のコード判定が思ったようにできず・・・調べてみたら、濁点つきのひらがな&カタカナがひっかかっていることが判明。これはNormalizeしてから処理するしかないだろーと考え、やり方を一通り調べてみました。

処理した結果がどうなっているかを調べるために、hexdumpのルーチンをつけています。

Normalizeしてからコード判定してみたら、問題なくできました。

AppleScript名:Unicodeの文字をNormalizeする
– Created 2015-09-30 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

–Reference:
–http://akisute.com/2010/05/utf-8-normalize.html
–http://nomenclator.la.coocan.jp/unicode/normalization.htm

set a to “がぎぐげご”
set aStr to current application’s NSString’s stringWithString:a
log hexDumpString(aStr)
–> {”E3″, “81″, “8C”, “E3″, “81″, “8E”, “E3″, “81″, “90″, “E3″, “81″, “92″, “E3″, “81″, “94″}

–NFD
set aNFD to aStr’s decomposedStringWithCanonicalMapping()
–>  (NSString) “がぎぐげご”
log hexDumpString(aNFD)
–> {”E3″, “81″, “8B”, “E3″, “82″, “99″, “E3″, “81″, “8D”, “E3″, “82″, “99″, “E3″, “81″, “8F”, “E3″, “82″, “99″, “E3″, “81″, “91″, “E3″, “82″, “99″, “E3″, “81″, “93″, “E3″, “82″, “99″}

–NFKD
set aNFKD to aStr’s decomposedStringWithCompatibilityMapping()
–>  (NSString) “がぎぐげご”
log hexDumpString(aNFKD)
–> {”E3″, “81″, “8B”, “E3″, “82″, “99″, “E3″, “81″, “8D”, “E3″, “82″, “99″, “E3″, “81″, “8F”, “E3″, “82″, “99″, “E3″, “81″, “91″, “E3″, “82″, “99″, “E3″, “81″, “93″, “E3″, “82″, “99″}

–NFC
set aNFC to aStr’s precomposedStringWithCanonicalMapping()
–>  (NSString) “がぎぐげご”
log hexDumpString(aNFC)
–> {”E3″, “81″, “8C”, “E3″, “81″, “8E”, “E3″, “81″, “90″, “E3″, “81″, “92″, “E3″, “81″, “94″}

–NFKC
set aNFKC to aStr’s precomposedStringWithCompatibilityMapping()
–>  (NSString) “がぎぐげご”
log hexDumpString(aNFKC)
–> {”E3″, “81″, “8C”, “E3″, “81″, “8E”, “E3″, “81″, “90″, “E3″, “81″, “92″, “E3″, “81″, “94″}

–NSStringをhexdumpする
on hexDumpString(theNSString)
  set theNSData to theNSString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set theString to (theNSData’s |description|()’s uppercaseString())
  
  
–Remove “< " ">” characters in head and tail
  
set tLength to (theString’s |length|()) - 2
  
set aRange to current application’s NSMakeRange(1, tLength)
  
set theString2 to theString’s substringWithRange:aRange
  
  
–Replace Space Characters
  
set aString to current application’s NSString’s stringWithString:theString2
  
set bString to aString’s stringByReplacingOccurrencesOfString:” “ withString:“”
  
  
set aResList to splitString(bString, 2)
  
–> {”E3″, “81″, “82″, “E3″, “81″, “84″, “E3″, “81″, “86″, “E3″, “81″, “88″, “E3″, “81″, “8A”}
  
  
return aResList
  
end hexDumpString

–Split NSString in specified aNum characters
on splitString(aText, aNum)
  
  
set aStr to current application’s NSString’s stringWithString:aText
  
if aStr’s |length|() aNum then return aText
  
  
set anArray to current application’s NSMutableArray’s new()
  
set mStr to current application’s NSMutableString’s stringWithString:aStr
  
  
set aRange to current application’s NSMakeRange(0, aNum)
  
  
repeat while (mStr’s |length|()) > 0
    if (mStr’s |length|()) < aNum then
      anArray’s addObject:(current application’s NSString’s stringWithString:mStr)
      
mStr’s deleteCharactersInRange:(current application’s NSMakeRange(0, mStr’s |length|()))
    else
      anArray’s addObject:(mStr’s substringWithRange:aRange)
      
mStr’s deleteCharactersInRange:aRange
    end if
  end repeat
  
  
return (current application’s NSArray’s arrayWithArray:anArray) as list
  
end splitString

★Click Here to Open This Script 

AppleScript名:Unicodeの文字を各方法でNormalizeして文字数をカウント
– Created 2015-10-02 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"

–Reference:
–http://akisute.com/2010/05/utf-8-normalize.html
–http://nomenclator.la.coocan.jp/unicode/normalization.htm

set aRes to normalizeStrAndRetEachLength("がぎぐげご")
–> {origLen:5, NFD:10, NFKD:10, NFC:5, NFKC:5}
set bRes to normalizeStrAndRetEachLength("ぱぴぷぺぽ")
–> {origLen:5, NFD:10, NFKD:10, NFC:5, NFKC:5}
set cRes to normalizeStrAndRetEachLength("㌀")
–> {origLen:1, NFD:1, NFKD:5, NFC:1, NFKC:4}
set dRes to normalizeStrAndRetEachLength("㍿")
–> {origLen:1, NFD:1, NFKD:4, NFC:1, NFKC:4}
set eRes to normalizeStrAndRetEachLength("Å")
–> {origLen:1, NFD:2, NFKD:2, NFC:1, NFKC:1}
set fRes to normalizeStrAndRetEachLength("…")
–> {origLen:1, NFD:1, NFKD:3, NFC:1, NFKC:3}
set gRes to normalizeStrAndRetEachLength("")
–> {origLen:1, NFD:1, NFKD:3, NFC:1, NFKC:3}
set hRes to normalizeStrAndRetEachLength("")
–> {origLen:1, NFD:1, NFKD:2, NFC:1, NFKC:2}
set iRes to normalizeStrAndRetEachLength("")
–> {origLen:1, NFD:1, NFKD:3, NFC:1, NFKC:3}

on normalizeStrAndRetEachLength(a)
  set aStr to current application’s NSString’s stringWithString:a
  
set origLenNum to aStr’s |length|()
  
set aNFD to aStr’s decomposedStringWithCanonicalMapping()’s |length|() –NFD
  
set aNFKD to aStr’s decomposedStringWithCompatibilityMapping()’s |length|() –NFKD
  
set aNFC to aStr’s precomposedStringWithCanonicalMapping()’s |length|() –NFC
  
set aNFKC to aStr’s precomposedStringWithCompatibilityMapping()’s |length|() –NFKC
  
return {origLen:origLenNum, NFD:aNFD, NFKD:aNFKD, NFC:aNFC, NFKC:aNFKC}
end normalizeStrAndRetEachLength

★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/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