Archive for the 'レコード (record)' Category

2017/11/18 指定文字の花文字を取得 v1.2

OS内にインストールされているフォントのうち収録グリフ数が8,000以上のフォントから50個をリストアップして、各フォントで花文字を作成してCotEditor上に花文字で新規ドキュメントを作成するAppleScriptです。

flowerchar3_resized.png

フォント50個に限定しているのは、大量にCotEditorでドキュメントを作成するとCotEditorのパフォーマンスが大幅に低下するためです(メモリの都合? 常識的な挙動なので問題ではありません。ドキュメントの開きすぎで)。

なお、OS内にインストールされているフォントの数が極端に少ない環境(条件に合致するフォントが50個ないとか)ではエラーになる可能性があります。

flowerchar2_resized.png

CotEditor上に50個の未保存のドキュメントができてしまうので、一括で破棄するAppleScriptを掲載しておきます。

tell application “CotEditor”
  tell every document
    close without saving
  end tell
end tell

★Click Here to Open This Script 

AppleScript名:指定文字の花文字を取得 v1.2
– Created 2017-11-18 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”
–http://piyocast.com/as/archives/4984

property NSArray : a reference to current application’s NSArray
property NSMutableDictionary : a reference to current application’s NSMutableDictionary
property NSBezierPath : a reference to current application’s NSBezierPath
property NSMutableParagraphStyle : a reference to current application’s NSMutableParagraphStyle
property NSPNGFileType : a reference to current application’s NSPNGFileType
property NSFontAttributeName : a reference to current application’s NSFontAttributeName
property NSKernAttributeName : a reference to current application’s NSKernAttributeName
property NSLigatureAttributeName : a reference to current application’s NSLigatureAttributeName
property NSFont : a reference to current application’s NSFont
property NSMutableAttributedString : a reference to current application’s NSMutableAttributedString
property NSUnderlineStyleAttributeName : a reference to current application’s NSUnderlineStyleAttributeName
property NSImage : a reference to current application’s NSImage
property NSParagraphStyleAttributeName : a reference to current application’s NSParagraphStyleAttributeName
property NSString : a reference to current application’s NSString
property NSForegroundColorAttributeName : a reference to current application’s NSForegroundColorAttributeName
property NSBitmapImageRep : a reference to current application’s NSBitmapImageRep
property NSColor : a reference to current application’s NSColor
property NSColorSpace : a reference to current application’s NSColorSpace
property NSFontManager : a reference to current application’s NSFontManager
property NSPredicate : a reference to current application’s NSPredicate

set aString to “ぴ”

set fRes to getEveryFontPSNameANdGlyphsNum() of me
set theArray to current application’s NSArray’s arrayWithArray:fRes
set thePred to current application’s NSPredicate’s predicateWithFormat:“fontNum > 8000″
set bArray to (theArray’s filteredArrayUsingPredicate:thePred) as list
set cArray to items 1 thru 50 of bArray

repeat with i in cArray
  set aFontName to contents of i
  
set fRes to getHanamojiStr(18, fontName of aFontName, aString, 0.7, true) of me
  
makeNewCotEditorDoc(fRes) of me
end repeat

–花文字文字列を計算して返す
on getHanamojiStr(aFontSize, aFontName, aString, aThread, incFontName)
  if length of aString is not equal to 1 then return false
  
  
set aThreadShould to 768 * aThread
  
  
set fillColor to NSColor’s whiteColor –塗り色
  
set bString to aString & ” “
  
set anAssrStr to makeRTFfromParameters(bString, aFontName, aFontSize, -2, (aFontSize * 1.2)) of me
  
set aSize to anAssrStr’s |size|()
  
  
if class of aSize = record then
    set attrStrWidth to width of aSize
    
set attrStrHeight to height of aSize
  else if class of aSize = list then –macOS 10.13.xのバグ回避
    copy aSize to {attrStrWidth, attrStrHeight}
  end if
  
  
set {xPos, yPos} to {0, 0}
  
  
–下地の画像を作成
  
set tmpImg1 to makeImageWithFilledColor(attrStrWidth, attrStrHeight, fillColor) of me
  
  
–下地の画像の上にAttributed Stringを描画
  
set tmpImg2 to drawAttributedStringsOnImage(tmpImg1, anAssrStr, xPos, yPos) of me
  
  
–NSImageからRaw画像を作成
  
set aRawimg to NSBitmapImageRep’s imageRepWithData:(tmpImg2’s TIFFRepresentation())
  
  
–画像から順次指定座標の色データを拾って花文字listに反映
  
set strList to {}
  
repeat with y from 1 to attrStrHeight - 1
    
    
set strListX to {}
    
repeat with x from 0 to attrStrWidth - 1
      set tmpCol to getColorFromRawImage(aRawimg, x, y) of me
      
      
if tmpCol is not equal to false then
        if tmpCol is not equal to {255, 255, 255} then
          copy tmpCol to {tmpR, tmpG, tmpB}
          
if (tmpR + tmpG + tmpB) < aThreadShould then
            set the end of strListX to aString
          else
            set the end of strListX to “ ”
          end if
        else
          set the end of strListX to “ ”
        end if
      end if
      
    end repeat
    
set the end of strList to strListX
  end repeat
  
  
–2D List→Text
  
set aRes to list2dToStringByUsingDelimiters(strList, “ ”, return) of me
  
  
if incFontName = true then
    set fName to getDisplayedNameOfFont(aFontName) of me
    
set aRes to “■” & fName & return & return & aRes
  end if
  
  
return aRes
end getHanamojiStr

–指定Raw画像中の指定座標のピクセルの色をRGBで取り出す
on getColorFromRawImage(aRawimg, x, y)
  set origColor to (aRawimg’s colorAtX:x y:y)
  
set srgbColSpace to NSColorSpace’s deviceRGBColorSpace
  
if srgbColSpace = missing value then return false
  
  
set aColor to (origColor’s colorUsingColorSpace:srgbColSpace)
  
  
set aRed to (aColor’s redComponent()) * 255
  
set aGreen to (aColor’s greenComponent()) * 255
  
set aBlue to (aColor’s blueComponent()) * 255
  
  
return {aRed as integer, aGreen as integer, aBlue as integer}
end getColorFromRawImage

–画像のうえに指定のスタイル付きテキストを描画して画像を返す
on drawAttributedStringsOnImage(anImage, anAssrStr, xPos, yPos)
  anImage’s lockFocus()
  
anAssrStr’s drawAtPoint:(current application’s NSMakePoint(xPos, yPos))
  
anImage’s unlockFocus()
  
return anImage
end drawAttributedStringsOnImage

–書式つきテキストを組み立てる
on makeRTFfromParameters(aStr as string, fontName as string, aFontSize as real, aKerning as real, aLineSpacing as real)
  set aVal1 to NSFont’s fontWithName:fontName |size|:aFontSize
  
set aKey1 to (NSFontAttributeName)
  
  
set aVal2 to NSColor’s blackColor()
  
set aKey2 to (NSForegroundColorAttributeName)
  
  
set aVal3 to aKerning
  
set akey3 to (NSKernAttributeName)
  
  
set aVal4 to 0
  
set akey4 to (NSUnderlineStyleAttributeName)
  
  
set aVal5 to 2 –all ligature ON
  
set akey5 to (NSLigatureAttributeName)
  
  
set aParagraphStyle to NSMutableParagraphStyle’s alloc()’s init()
  
aParagraphStyle’s setMinimumLineHeight:(aLineSpacing)
  
aParagraphStyle’s setMaximumLineHeight:(aLineSpacing)
  
set akey7 to (NSParagraphStyleAttributeName)
  
  
set keyList to {aKey1, aKey2, akey3, akey4, akey5, akey7}
  
set valList to {aVal1, aVal2, aVal3, aVal4, aVal5, aParagraphStyle}
  
set attrsDictionary to NSMutableDictionary’s dictionaryWithObjects:valList forKeys:keyList
  
  
set attrStr to NSMutableAttributedString’s alloc()’s initWithString:aStr attributes:attrsDictionary
  
return attrStr
end makeRTFfromParameters

–指定サイズの画像を作成し、背景を指定色で塗る
on makeImageWithFilledColor(aWidth, aHeight, fillColor)
  set anImage to NSImage’s alloc()’s initWithSize:(current application’s NSMakeSize(aWidth, aHeight))
  
  
anImage’s lockFocus()
  
set theRect to {{x:0, y:0}, {width:aWidth, height:aHeight}}
  
set theNSBezierPath to NSBezierPath’s bezierPath
  
theNSBezierPath’s appendBezierPathWithRect:theRect
  
fillColor’s |set|()
  
theNSBezierPath’s fill()
  
anImage’s unlockFocus()
  
  
return anImage
end makeImageWithFilledColor

–2D Listをアイテム間および行間のデリミタを個別に指定してテキスト変換
on list2dToStringByUsingDelimiters(aList, itemDelimiter, lineDelimiter)
  set outList to {}
  
repeat with i in aList
    set aStr to listToStringUsingTextItemDelimiter(i, itemDelimiter) of me
    
set the end of outList to aStr
  end repeat
  
  
set bStr to listToStringUsingTextItemDelimiter(outList, lineDelimiter) of me
  
return bStr
end list2dToStringByUsingDelimiters

on listToStringUsingTextItemDelimiter(sourceList, textItemDelimiter)
  set CocoaArray to NSArray’s arrayWithArray:sourceList
  
set CocoaString to CocoaArray’s componentsJoinedByString:textItemDelimiter
  
return (CocoaString as string)
end listToStringUsingTextItemDelimiter

on getEveryFontPSNameANdGlyphsNum()
  set aFontList to NSFontManager’s sharedFontManager()’s availableFonts()
  
set thePred to NSPredicate’s predicateWithFormat:“NOT SELF BEGINSWITH ’.’”
  
set aFontList to (aFontList’s filteredArrayUsingPredicate:thePred) as list
  
  
set aList to {}
  
repeat with i in aFontList
    set aName to contents of i
    
set aNum to countNumberOfGlyphsInFont(aName) of me
    
set the end of aList to {fontName:aName, fontNum:aNum}
  end repeat
  
  
return aList
end getEveryFontPSNameANdGlyphsNum

–指定Postscript名称のフォントに定義されている文字数を数えて返す
on countNumberOfGlyphsInFont(fontName)
  set aFont to current application’s NSFont’s fontWithName:fontName |size|:9.0
  
if aFont = missing value then return false
  
set aProp to aFont’s numberOfGlyphs()
  
return aProp
end countNumberOfGlyphsInFont

–フォントのPostScript NameからDisplayed Nameを取得
on getDisplayedNameOfFont(aName)
  set aFont to current application’s NSFont’s fontWithName:aName |size|:9.0
  
set aDispName to (aFont’s displayName()) as string
  
return aDispName
end getDisplayedNameOfFont

on makeNewCotEditorDoc(aCon)
  tell application “CotEditor”
    set newDoc to make new document
    
tell newDoc
      set contents to aCon
    end tell
  end tell
end makeNewCotEditorDoc

★Click Here to Open This Script 

2017/10/10 なろう小説APIで各カテゴリごとの集計を実行

「小説家になろう」サイトのAPI「なろう小説API」を呼び出して、カテゴリごとの該当件数を集計するAppleScriptです。

「なろう小説API」は事前にAPI Keyの取得も不要で、簡単に呼び出せるのでお手軽に使えます。

本AppleScriptは、「小説家になろう」掲載作品を、大カテゴリと小カテゴリでコードを指定して、ループで存在件数の集計を行います。カテゴリごとに分布が偏っているようなので、該当件数が0件のカテゴリは結果出力しないようにしています。筆者の環境では集計に22〜25秒ぐらいかかっています(インターネット接続回線速度に依存)。

http headerにgzip転送リクエスト要求を書きつつ、実際のデータ自体もgzipで圧縮されているので、二重に圧縮している状態です。実測したところ、http headerでgzip指定を行なったほうがトータルで1秒程度速かったので「そんなもんかな」と思いつつ、そのままにしています。

Web APIからのデータ受信時のNSDataからのZip展開にオープンソースのフレームワーク「GZIP」(By Nick Lockwood)を利用しています。同プロジェクトはGithub上のXcodeプロジェクトをXcodeでビルドするとFrameworkが得られるので、ビルドして~/Library/FrameworksフォルダにGZIP.frameworkを入れてください。

ジャンルは数値で指定するようになっていますが、その数値が何を示しているかという情報はAPI側からの出力にはないので、Webサイト上から文字情報をコピペで取得し、AppleScript内に記載して(ハードコーディング)カテゴリコードリストと照合して出力しています。

実際に集計してみると、ノンカテゴリが53%ということで、カテゴリ分けの機能が有効に活用されていないことが見てとれます。

そのことについては運営側も重々承知しているようで、APIの検索オプションに「キーワードに異世界転生があるものを含む」といったものがあるなど、ジャンルよりもキーワード重視するようにしているようです。

そういいつつも、使われているキーワードについては若干の表記ゆらぎがあるようで、単純にこのオプションを指定しても「異世界転生もの」をすべて抽出できていないように見えます。キーワード自体にどの程度「表記揺れ」が存在しているのかを調べてみるとよいのかもしれません。

APIの仕様上、2,000件しか詳細データを取得できないように見えるので、そのあたりがちょっと気になります(どうも全数調査を行いにくい仕様)。

分析するまでもなく、異世界転生ものが多く、ノンジャンル作品でも異世界転生ものばっかりという印象です。掘り出しもので「ソ連の宇宙技術は最強過ぎたのだが、それを西側諸国が完全に理解したのはつい最近だった」という作品に行き当たり、これが強烈に面白いです。

AppleScript名:なろう小説APIで各カテゴリごとの集計を実行
– Created 2017-10-10 by Takaaki Naganoya
– 2017 Piyomaru Software
–http://piyocast.com/as/archives/4891
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “GZIP”
–https://github.com/nicklockwood/GZIP
–http://dev.syosetu.com/man/api/
–1日の利用上限は80,000または転送量上限400MByte???

property |NSURL| : a reference to current application’s |NSURL|
property NSString : a reference to current application’s NSString
property NSArray : a reference to current application’s NSArray
property NSUTF8StringEncoding : a reference to current application’s NSUTF8StringEncoding
property NSMutableDictionary : a reference to current application’s NSMutableDictionary
property NSURLQueryItem : a reference to current application’s NSURLQueryItem
property NSURLComponents : a reference to current application’s NSURLComponents
property NSJSONSerialization : a reference to current application’s NSJSONSerialization
property NSMutableURLRequest : a reference to current application’s NSMutableURLRequest
property NSURLConnection : a reference to current application’s NSURLConnection
property NSSortDescriptor : a reference to current application’s NSSortDescriptor
property NSNumber : a reference to current application’s NSNumber
property NSNumberFormatter : a reference to current application’s NSNumberFormatter
property NSNumberFormatterRoundUp : a reference to current application’s NSNumberFormatterRoundUp
property NSNumberFormatterRoundDown : a reference to current application’s NSNumberFormatterRoundDown

set invList to {}

set bgList to {1, 2, 3, 4, 99, 98}
set bigGnereLabel to {“恋愛”, “ファンタジー”, “文芸”, “SF”, “その他”, “ノンジャンル”}

set gList to {101, 102, 201, 202, 301, 302, 303, 304, 305, 306, 307, 401, 402, 403, 404, 9901, 9902, 9903, 9904, 9999, 9801}
set smlGenreLabel to {“異世界〔恋愛〕”, “現実世界〔恋愛〕”, “ハイファンタジー〔ファンタジー〕”, “ローファンタジー〔ファンタジー〕”, “純文学〔文芸〕”, “ヒューマンドラマ〔文芸〕”, “歴史〔文芸〕”, “推理〔文芸〕”, “ホラー〔文芸〕”, “アクション〔文芸〕”, “コメディー〔文芸〕”, “VRゲーム〔SF〕”, “宇宙〔SF〕”, “空想科学〔SF〕”, “パニック〔SF〕”, “童話〔その他〕”, “詩〔その他〕”, “エッセイ〔その他〕”, “リプレイ〔その他〕”, “その他〔その他〕”, “ノンジャンル〔ノンジャンル〕”}

–全体の件数取得
set aRec to {gzip:“5″, out:“json”, lim:“1″}
set aRESTres to callNarouAPI(aRec, “1″, “1″) of me
set wholeCount to (allCount of first item of aRESTres)

–カテゴリごとの集計
repeat with i in bgList
  repeat with ii in gList
    set aRec to {gzip:“5″, biggenre:i as string, genre:ii as string, out:“json”, lim:“1″}
    
set aRESTres to callNarouAPI(aRec, “1″, “1″) of me
    
set aTotal to allCount of first item of aRESTres
    
    
if aTotal is not equal to 0 then
      set big to contents of i
      
set small to contents of ii
      
set bigLabel to getLabelFromNum(bgList, bigGnereLabel, big) of me
      
set smlLabel to getLabelFromNum(gList, smlGenreLabel, small) of me
      
set aPerCentatge to roundingDownNumStr(((aTotal / wholeCount) * 100), 1) of me
      
set the end of invList to {biggenre:bigLabel, genre:smlLabel, totalNum:aTotal, percentage:aPerCentatge}
    end if
  end repeat
end repeat

set bList to sortRecListByLabel(invList, “totalNum”, false) of me –降順ソート
–> {{totalNum:274072, biggenre:”ノンジャンル”, percentage:53.1, genre:”ノンジャンル〔ノンジャンル〕”}, {totalNum:47121, biggenre:”ファンタジー”, percentage:9.1, genre:”ハイファンタジー〔ファンタジー〕”}, {totalNum:28883, biggenre:”恋愛”, percentage:5.6, genre:”現実世界〔恋愛〕”}, {totalNum:23217, biggenre:”文芸”, percentage:4.5, genre:”ヒューマンドラマ〔文芸〕”}, {totalNum:21320, biggenre:”ファンタジー”, percentage:4.1, genre:”ローファンタジー〔ファンタジー〕”}, {totalNum:17079, biggenre:”恋愛”, percentage:3.3, genre:”異世界〔恋愛〕”}, {totalNum:16798, biggenre:”その他”, percentage:3.2, genre:”その他〔その他〕”}, {totalNum:13892, biggenre:”その他”, percentage:2.6, genre:”詩〔その他〕”}, {totalNum:13341, biggenre:”文芸”, percentage:2.5, genre:”コメディー〔文芸〕”}, {totalNum:10120, biggenre:”文芸”, percentage:1.9, genre:”ホラー〔文芸〕”}, {totalNum:9502, biggenre:”その他”, percentage:1.8, genre:”エッセイ〔その他〕”}, {totalNum:8486, biggenre:”文芸”, percentage:1.6, genre:”純文学〔文芸〕”}, {totalNum:7211, biggenre:”文芸”, percentage:1.3, genre:”アクション〔文芸〕”}, {totalNum:6199, biggenre:”SF”, percentage:1.2, genre:”空想科学〔SF〕”}, {totalNum:5780, biggenre:”その他”, percentage:1.1, genre:”童話〔その他〕”}, {totalNum:3295, biggenre:”文芸”, percentage:0.6, genre:”推理〔文芸〕”}, {totalNum:3217, biggenre:”文芸”, percentage:0.6, genre:”歴史〔文芸〕”}, {totalNum:2606, biggenre:”SF”, percentage:0.5, genre:”VRゲーム〔SF〕”}, {totalNum:1471, biggenre:”SF”, percentage:0.2, genre:”パニック〔SF〕”}, {totalNum:1454, biggenre:”SF”, percentage:0.2, genre:”宇宙〔SF〕”}, {totalNum:190, biggenre:”その他”, percentage:0.0, genre:”リプレイ〔その他〕”}}

on callNarouAPI(aRec, callFrom, callNum)
  set reqURLStr to “http://api.syosetu.com/novelapi/api/” –通常API
  
  
–set aRec to {gzip:”5″, |st|:callFrom as string, out:”json”, lim:callNum as string}
  
set aURL to retURLwithParams(reqURLStr, aRec) of me
  
set aRes to callRestGETAPIAndParseResults(aURL) of me
  
  
set aRESCode to (responseCode of aRes) as integer
  
if aRESCode is not equal to 200 then return false
  
  
set aRESHeader to responseHeader of aRes
  
set aRESTres to (json of aRes) as list
  
end callNarouAPI

–GET methodのREST APIを呼ぶ
on callRestGETAPIAndParseResults(aURL)
  set aRequest to NSMutableURLRequest’s requestWithURL:(|NSURL|’s URLWithString:aURL)
  
aRequest’s setHTTPMethod:“GET”
  
aRequest’s setValue:“gzip” forHTTPHeaderField:“Content-Encoding”
  
  
set aRes to NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value)
  
set resList to aRes as list
  
  
set bRes to contents of (first item of resList)
  
  
set rRes to bRes’s gunzippedData() –From GZIP.framework
  
  
set resStr to NSString’s alloc()’s initWithData:rRes encoding:(NSUTF8StringEncoding)
  
  
set jsonString to NSString’s stringWithString:resStr
  
set jsonData to jsonString’s dataUsingEncoding:(NSUTF8StringEncoding)
  
set aJsonDict to NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
  
–Get Response Code & Header
  
set dRes to contents of second item of resList
  
if dRes is not equal to missing value then
    set resCode to (dRes’s statusCode()) as number
    
set resHeaders to (dRes’s allHeaderFields()) as record
  else
    set resCode to 0
    
set resHeaders to {}
  end if
  
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
end callRestGETAPIAndParseResults

on retURLwithParams(aBaseURL, aRec)
  set aDic to NSMutableDictionary’s dictionaryWithDictionary:aRec
  
  
set aKeyList to (aDic’s allKeys()) as list
  
set aValList to (aDic’s allValues()) as list
  
set aLen to length of aKeyList
  
  
set qList to {}
  
repeat with i from 1 to aLen
    set aName to contents of item i of aKeyList
    
set aVal to contents of item i of aValList
    
set the end of qList to (NSURLQueryItem’s queryItemWithName:aName value:aVal)
  end repeat
  
  
set aComp to NSURLComponents’s alloc()’s initWithString:aBaseURL
  
aComp’s setQueryItems:qList
  
set aURL to (aComp’s |URL|()’s absoluteString()) as text
  
  
return aURL
end retURLwithParams

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

on getLabelFromNum(aList, labelLIst, aNum)
  set aInd to offsetOf(aList, aNum) of me
  
set anItem to contents of item aInd of labelLIst
  
return anItem
end getLabelFromNum

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

on roundingDownNumStr(aNum as string, aDigit as integer)
  set a to NSString’s stringWithString:aNum
  
set aa to a’s doubleValue()
  
set aFormatter to NSNumberFormatter’s alloc()’s init()
  
aFormatter’s setMaximumFractionDigits:aDigit
  
aFormatter’s setRoundingMode:(NSNumberFormatterRoundDown)
  
set aStr to aFormatter’s stringFromNumber:aa
  
return (aStr as text) as real
end roundingDownNumStr

on roundingUpNumStr(aNum as string, aDigit as integer)
  set a to NSString’s stringWithString:aNum
  
set aa to a’s doubleValue()
  
set aFormatter to NSNumberFormatter’s alloc()’s init()
  
aFormatter’s setMaximumFractionDigits:aDigit
  
aFormatter’s setRoundingMode:(NSNumberFormatterRoundUp)
  
set aStr to aFormatter’s stringFromNumber:aa
  
return (aStr as text) as real
end roundingUpNumStr

★Click Here to Open This Script 

2017/10/07 なろう小説APIを呼び出す v2(Zip展開つき)

「小説家になろう」サイトのAPI「なろう小説API」を呼び出してデータを取得するAppleScriptです。

「なろう小説API」は事前にAPI Keyの取得も不要で、簡単に呼び出せるのでお手軽に使えます。用意されているのはデータ取得のメソッドであり、結果の保持や集計、しぼりこみについてはローカル側で勝手に行う放任仕様です条件抽出のパラメータもあります。

ただし、本APIが用意しているZip圧縮を利用するのに少々骨が折れました。

http header中でContent-Encodingに「gzip」を指定する程度では対応できませんでした。Zip圧縮転送指定時(パラメータにgzipを指定)APIから返ってくるデータそのものがZip圧縮されており、APIから返ってきたデータそのもの(NSData)を展開する必要がありました。

そこで、NSDataをそのままZip展開できるオープンソースのフレームワーク「GZIP」(By Nick Lockwood)を利用してみました。同プロジェクトはGithub上のXcodeプロジェクトをXcodeでビルドするとFrameworkが得られるので、ビルドして~/Library/FrameworksフォルダにGZIP.frameworkを入れてください。

とりあえず呼び出して500件のデータを取得していますが、500件ずつ取得してループするように書き換えるとよいでしょう。

AppleScript名:なろう小説API v2(Zip展開つき)
– Created 2017-10-03 by Takaaki Naganoya
– 2017 Piyomaru Software
–http://piyocast.com/as/archives/4890
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “GZIP”
–https://github.com/nicklockwood/GZIP
–http://dev.syosetu.com/man/api/
–1日の利用上限は80,000または転送量上限400MByte???

property |NSURL| : a reference to current application’s |NSURL|
property NSString : a reference to current application’s NSString
property NSUTF8StringEncoding : a reference to current application’s NSUTF8StringEncoding
property NSMutableDictionary : a reference to current application’s NSMutableDictionary
property NSURLQueryItem : a reference to current application’s NSURLQueryItem
property NSURLComponents : a reference to current application’s NSURLComponents
property NSJSONSerialization : a reference to current application’s NSJSONSerialization
property NSMutableURLRequest : a reference to current application’s NSMutableURLRequest
property NSURLConnection : a reference to current application’s NSURLConnection

set reqURLStr to “http://api.syosetu.com/novelapi/api/” –通常API
–set reqURLStr to “http://api.syosetu.com/novel18api/api/”–18禁API

set aRec to {gzip:“5″, out:“json”, lim:“500″}

set aURL to retURLwithParams(reqURLStr, aRec) of me

set aRes to callRestGETAPIAndParseResults(aURL) of me

set aRESCode to (responseCode of aRes) as integer
if aRESCode is not equal to 200 then return false

set aRESHeader to responseHeader of aRes
set aRESTres to (json of aRes) as list

–GET methodのREST APIを呼ぶ
on callRestGETAPIAndParseResults(aURL)
  set aRequest to NSMutableURLRequest’s requestWithURL:(|NSURL|’s URLWithString:aURL)
  
aRequest’s setHTTPMethod:“GET”
  
aRequest’s setValue:“gzip” forHTTPHeaderField:“Content-Encoding”
  
  
set aRes to NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value)
  
set resList to aRes as list
  
  
set bRes to contents of (first item of resList)
  
  
set rRes to bRes’s gunzippedData() –From GZIP.framework
  
  
set resStr to NSString’s alloc()’s initWithData:rRes encoding:(NSUTF8StringEncoding)
  
  
set jsonString to NSString’s stringWithString:resStr
  
set jsonData to jsonString’s dataUsingEncoding:(NSUTF8StringEncoding)
  
set aJsonDict to NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
  
–Get Response Code & Header
  
set dRes to contents of second item of resList
  
if dRes is not equal to missing value then
    set resCode to (dRes’s statusCode()) as number
    
set resHeaders to (dRes’s allHeaderFields()) as record
  else
    set resCode to 0
    
set resHeaders to {}
  end if
  
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
end callRestGETAPIAndParseResults

on retURLwithParams(aBaseURL, aRec)
  set aDic to NSMutableDictionary’s dictionaryWithDictionary:aRec
  
  
set aKeyList to (aDic’s allKeys()) as list
  
set aValList to (aDic’s allValues()) as list
  
set aLen to length of aKeyList
  
  
set qList to {}
  
repeat with i from 1 to aLen
    set aName to contents of item i of aKeyList
    
set aVal to contents of item i of aValList
    
set the end of qList to (NSURLQueryItem’s queryItemWithName:aName value:aVal)
  end repeat
  
  
set aComp to NSURLComponents’s alloc()’s initWithString:aBaseURL
  
aComp’s setQueryItems:qList
  
set aURL to (aComp’s |URL|()’s absoluteString()) as text
  
  
return aURL
end retURLwithParams

★Click Here to Open This Script 

2017/08/17 japaneseTokenizeのじっけん

macOSの日本語形態素解析の機能を呼び出して日本語テキストをParseするCocoa Framework「japaneseTokenize.framework」を作成しました。それを呼び出すAppleScriptです。

「japaneseTokenize.framework」はゼロから書いたはじめてのObjective-Cのプログラムでもあります(ほぼコピペで作成してありますが)。Header Fileなんて書かないので、見よう見真似&XcodeのWarning Messageのいいなりで試行錯誤していました。

CFStringTokenizer経由で形態素解析してふりがな、元テキスト、ローマ字ふりがなを取得する「parseString:」と、NSLinguisticTagger経由で形態素解析して元テキスト、品詞を取得する「parseStringWithLinguisticTag:」の2つのメソッドを用意してみました。

これら2つの形態素解析機能のparseの結果が矛盾しているかどうかは未確認です(短い文章では同じことを確認してありますが、長い文章でも同じかどうかは未確認。多分同じだとは思いますけれども)。

(CFStringTokenizer)parseString:
(*これ, ら, 2, つ, の, 形態, 素, 解析, 機能, の, parse, の, 結果, が, 矛盾, し, て, いる, か, どう, か, は, 未, 確認, です, 。, 短い, 文章, で, は, 同じ, こと, を, 確認, し, て, あり, ます, が, 、, 長い, 文章, で, も, 同じ, か, どう, か, は, 未, 確認, 。, 多分, 同じ, だ, と, は, 思い, ます, けれど, も, 。*)

(NSLinguisticTagger) parseStringWithLinguisticTag:
(*これら, 2つ, の, 形態素, 解析, 機能, の, parse, の, 結果, が, 矛盾, し, て, いる, か, どう, か, は, 未, 確認, です, 。, 短い, 文章, で, は, 同じ, こと, を, 確認, し, て, あり, ます, が, 、, 長い, 文章, で, も, 同じ, か, どう, か, は, 未, 確認, 。, 多分, 同じ, だ, と, は, 思い, ます, けれど, も, 。*)

(ご参考) AppleScriptの「words of」の実行結果
(*これ, ら, 2, つ, の, 形態, 素, 解析, 機能, の, parse, の, 結果, が, 矛盾, し, て, いる, か, どう, か, は, 未, 確認, です, 短い, 文章, で, は, 同じ, こと, を, 確認, し, て, あり, ます, が, 長い, 文章, で, も, 同じ, か, どう, か, は, 未, 確認, 多分, 同じ, だ, と, は, 思い, ます, けれど, も*)

# うわ、まさかの「微妙に形態素解析結果が違っている」パターンが来た、、、(ーー; macOS 10.13 Betaで試してみたところ、CFStringTokenizer側の形態素解析結果に合わせてNSLinguisticTagger側の出力結果を修正している模様

NSLinguisticTaggerが返す品詞については、いまひとつしっくりこないですし、ユーザーが定義したユーザー定義辞書を考慮して形態素解析したりしないので、実用性についてはさっぱりな印象です。さまざまなWeb APIをAppleScriptから呼び出してみた印象からいえば、Apitoreの形態素解析APIが一番納得できる内容に見えます(Googleの形態素解析APIはまだ呼んでいません)。

jparsers.png

Yahoo!日本語形態素解析Web APIによる解析結果:
–> {ResultSet:{attributes:{xmlns:”urn:yahoo:jp:jlp”, |xsi:schemalocation|:”urn:yahoo:jp:jlp https://jlp.yahooapis.jp/MAService/V1/parseResponse.xsd”, |xmlns:xsi|:”http://www.w3.org/2001/XMLSchema-instance”}, ma_result:{total_count:{|contents|:”8″}, filtered_count:{|contents|:”8″}, word_list:{|word|:{{surface:{|contents|:”来週”}, reading:{|contents|:”らいしゅう”}, pos:{|contents|:”名詞”}}, {surface:{|contents|:”の”}, reading:{|contents|:”の”}, pos:{|contents|:”助詞”}}, {surface:{|contents|:”水曜日”}, reading:{|contents|:”すいようび”}, pos:{|contents|:”名詞”}}, {surface:{|contents|:”に”}, reading:{|contents|:”に”}, pos:{|contents|:”助詞”}}, {surface:{|contents|:”会議”}, reading:{|contents|:”かいぎ”}, pos:{|contents|:”名詞”}}, {surface:{|contents|:”を”}, reading:{|contents|:”を”}, pos:{|contents|:”助詞”}}, {surface:{|contents|:”予約”}, reading:{|contents|:”よやく”}, pos:{|contents|:”名詞”}}, {surface:{|contents|:”。”}, reading:{|contents|:”。”}, pos:{|contents|:”特殊”}}}}}}}

Apitore 日本語形態素解析【新語対応】 ipadic neologdによる解析結果:
–> {startTime:”1502971949537″, tokens:{{partOfSpeechLevel1:”名詞”, baseForm:”来週”, pronunciation:”ライシュー”, position:0, partOfSpeechLevel3:”*”, reading:”ライシュウ”, surface:”来週”, known:true, allFeatures:”名詞,副詞可能,*,*,*,*,来週,ライシュウ,ライシュー”, conjugationType:”*”, partOfSpeechLevel2:”副詞可能”, conjugationForm:”*”, allFeaturesArray:{”名詞”, “副詞可能”, “*”, “*”, “*”, “*”, “来週”, “ライシュウ”, “ライシュー”}, partOfSpeechLevel4:”*”}, {partOfSpeechLevel1:”助詞”, baseForm:”の”, pronunciation:”ノ”, position:2, partOfSpeechLevel3:”*”, reading:”ノ”, surface:”の”, known:true, allFeatures:”助詞,連体化,*,*,*,*,の,ノ,ノ”, conjugationType:”*”, partOfSpeechLevel2:”連体化”, conjugationForm:”*”, allFeaturesArray:{”助詞”, “連体化”, “*”, “*”, “*”, “*”, “の”, “ノ”, “ノ”}, partOfSpeechLevel4:”*”}, {partOfSpeechLevel1:”名詞”, baseForm:”水曜日”, pronunciation:”スイヨービ”, position:3, partOfSpeechLevel3:”*”, reading:”スイヨウビ”, surface:”水曜日”, known:true, allFeatures:”名詞,副詞可能,*,*,*,*,水曜日,スイヨウビ,スイヨービ”, conjugationType:”*”, partOfSpeechLevel2:”副詞可能”, conjugationForm:”*”, allFeaturesArray:{”名詞”, “副詞可能”, “*”, “*”, “*”, “*”, “水曜日”, “スイヨウビ”, “スイヨービ”}, partOfSpeechLevel4:”*”}, {partOfSpeechLevel1:”助詞”, baseForm:”に”, pronunciation:”ニ”, position:6, partOfSpeechLevel3:”一般”, reading:”ニ”, surface:”に”, known:true, allFeatures:”助詞,格助詞,一般,*,*,*,に,ニ,ニ”, conjugationType:”*”, partOfSpeechLevel2:”格助詞”, conjugationForm:”*”, allFeaturesArray:{”助詞”, “格助詞”, “一般”, “*”, “*”, “*”, “に”, “ニ”, “ニ”}, partOfSpeechLevel4:”*”}, {partOfSpeechLevel1:”名詞”, baseForm:”会議”, pronunciation:”カイギ”, position:7, partOfSpeechLevel3:”*”, reading:”カイギ”, surface:”会議”, known:true, allFeatures:”名詞,サ変接続,*,*,*,*,会議,カイギ,カイギ”, conjugationType:”*”, partOfSpeechLevel2:”サ変接続”, conjugationForm:”*”, allFeaturesArray:{”名詞”, “サ変接続”, “*”, “*”, “*”, “*”, “会議”, “カイギ”, “カイギ”}, partOfSpeechLevel4:”*”}, {partOfSpeechLevel1:”助詞”, baseForm:”を”, pronunciation:”ヲ”, position:9, partOfSpeechLevel3:”一般”, reading:”ヲ”, surface:”を”, known:true, allFeatures:”助詞,格助詞,一般,*,*,*,を,ヲ,ヲ”, conjugationType:”*”, partOfSpeechLevel2:”格助詞”, conjugationForm:”*”, allFeaturesArray:{”助詞”, “格助詞”, “一般”, “*”, “*”, “*”, “を”, “ヲ”, “ヲ”}, partOfSpeechLevel4:”*”}, {partOfSpeechLevel1:”名詞”, baseForm:”予約”, pronunciation:”ヨヤク”, position:10, partOfSpeechLevel3:”*”, reading:”ヨヤク”, surface:”予約”, known:true, allFeatures:”名詞,サ変接続,*,*,*,*,予約,ヨヤク,ヨヤク”, conjugationType:”*”, partOfSpeechLevel2:”サ変接続”, conjugationForm:”*”, allFeaturesArray:{”名詞”, “サ変接続”, “*”, “*”, “*”, “*”, “予約”, “ヨヤク”, “ヨヤク”}, partOfSpeechLevel4:”*”}, {partOfSpeechLevel1:”記号”, baseForm:”。”, pronunciation:”。”, position:12, partOfSpeechLevel3:”*”, reading:”。”, surface:”。”, known:true, allFeatures:”記号,句点,*,*,*,*,。,。,。”, conjugationType:”*”, partOfSpeechLevel2:”句点”, conjugationForm:”*”, allFeaturesArray:{”記号”, “句点”, “*”, “*”, “*”, “*”, “。”, “。”, “。”}, partOfSpeechLevel4:”*”}}, endTime:”1502971949537″, |log|:”", processTime:”0″}

japaneseTokenize.frameworkをmacOS 10.10以降用にビルドしたバイナリを用意しましたので、自己責任で~/Library/Frameworksフォルダに入れて使ってみてください。

→ Download japaneseTokenize framework Binary

AppleScript名:japaneseTokenizeのじっけん
– Created 2017-08-17 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “japaneseTokenize”
–https://github.com/murakami/workbook/tree/master/mac/Ruby
–http://d.hatena.ne.jp/shu223/20130318/1363566717

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

set targString to “来週の水曜日に会議を予約。” –”Make a meeting on next Wednesday.” in Japanese

set aRes to current application’s jTokenize’s parseString:targString
–> {{token:”来週”, romaji:”raishuu”, hurigana:”らいしゅう”}, {token:”の”, romaji:”no”, hurigana:”の”}, {token:”水曜”, romaji:”suiyou”, hurigana:”すいよう”}, {token:”日”, romaji:”hi”, hurigana:”ひ”}, {token:”に”, romaji:”ni”, hurigana:”に”}, {token:”会議”, romaji:”kaigi”, hurigana:”かいぎ”}, {token:”を”, romaji:”wo”, hurigana:”を”}, {token:”予約”, romaji:”yoyaku”, hurigana:”よやく”}, {token:”。”, romaji:”。”, hurigana:”。”}}

set aList to (aRes’s valueForKeyPath:“hurigana”) as list –ふりがな
–>  {”らいしゅう”, “の”, “すいよう”, “ひ”, “に”, “かいぎ”, “を”, “よやく”, “。”}

set bList to (aRes’s valueForKeyPath:“token”) as list –元のテキスト
–>  {”来週”, “の”, “水曜”, “日”, “に”, “会議”, “を”, “予約”, “。”}

set cList to (aRes’s valueForKeyPath:“romaji”) as list –hurigana (romaji)
–>  {”raishuu”, “no”, “suiyou”, “hi”, “ni”, “kaigi”, “wo”, “yoyaku”, “。”}

set bRes to current application’s jTokenize’s parseStringWithLinguisticTag:targString
set dList to (bRes’s valueForKeyPath:“scheme”) as list
–>  {”Noun”, “Particle”, “Noun”, “Noun”, “Particle”, “Noun”, “Particle”, “Noun”, “SentenceTerminator”}

return bRes as list
–> {{token:”来週”, |scheme|:”Noun”}, {token:”の”, |scheme|:”Particle”}, {token:”水曜”, |scheme|:”Noun”}, {token:”日”, |scheme|:”Noun”}, {token:”に”, |scheme|:”Particle”}, {token:”会議”, |scheme|:”Noun”}, {token:”を”, |scheme|:”Particle”}, {token:”予約”, |scheme|:”Noun”}, {token:”。”, |scheme|:”SentenceTerminator”}}

★Click Here to Open This Script 

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/02/16 YAMLのじっけん

オープンソースのフレームワーク「YAML.framework」(Mirek Rusin)を用いて、構造化データ記述言語であるYAMLとオブジェクト(listやrecord)との間の相互変換を行うAppleScriptです。

これまでYAMLとは縁のない生活を送ってきましたが、とくに問題はありませんでした。構造を持つデータの分量が増えた場合には、プログラム中に直接記述せず、Excelの表からデータを読み取って処理したり、データベースなど他のデータソースからデータを取得していました。

ただ、listとかrecordの内容が込み入ってくる(フィールド数が多いとか、入れ子構造の段数が深いとか)と、こういう仕組みがあったほうが便利なんだろうな、ということは理解できます。

YAML.frameworkのプロジェクトをXcodeでビルドし、出来上がったフレームワークを~/Library/Frameworksに入れてテストしてみてください。

途中、Githubに掲載されているObjective-CのサンプルプログラムがNSInputStreamを使っており、これをAppleScriptに書き換えて呼び出すとAppleScriptの処理系が100%クラッシュ。NSDataを経由して変換する方法なども試してみましたがNSInputStreamを作りにいくとクラッシュ。

結局、Stringから直接変換するメソッドがあったため、これを使用することで安定して処理できるようになりました。

当初は機械学習フレームワークの「YCML」をいじくっていたのですが、その過程で本フレームワークを発見。なかなか有用性が高そうなので試してみた次第です。

AppleScript名:YAMLのじっけん
– Created 2017-02-16 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “YAML” –https://github.com/mirek/YAML.framework
–http://piyocast.com/as/archives/4464

–YAMLの文字列からオブジェクトを生成する
set aYAMLstr to
items:
- name: Foo
- name: Bar

set aStr to current application’s NSString’s stringWithString:aYAMLstr
set aData to aStr’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)

set aData to current application’s YAMLSerialization’s objectsWithYAMLString:aStr options:(4096) |error|:(missing value)
–>  (NSArray) {{items:{{name:”Foo”}, {name:”Bar”}}}}

–オブジェクトからYAMLの文字列を取得する
set aString to (current application’s YAMLSerialization’s createYAMLStringWithObject:aData options:(1) |error|:(missing value)) as string
(* –>
“—
- items:
- name: Foo
- name: Bar
…”
*)

★Click Here to Open This Script 

AppleScript名:YAMLのじっけん4
– Created 2017-02-16 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “YAML” –https://github.com/mirek/YAML.framework
–http://piyocast.com/as/archives/4464

–YAMLの文字列からオブジェクトを生成する
set aYAMLstr to
- name: Smith
email: smith@mail.com
- name: Shelton
email: shelton@mail.com
- name: Kelly
email: kelly@mail.com

set aRes to retObjectFromYAMLString(aYAMLstr) of me
–>  {{{name:”Smith”, email:”smith@mail.com”}, {name:”Shelton”, email:”shelton@mail.com”}, {name:”Kelly”, email:”kelly@mail.com”}}}

set bYAMLstr to
names: [Smith, Shelton, Kelly]
emails: [smith@mail.com, shelton@mail.com, kelly@mail.com]

set bRes to retObjectFromYAMLString(bYAMLstr) of me
–>  {{names:{”Smith”, “Shelton”, “Kelly”}, emails:{”smith@mail.com”, “shelton@mail.com”, “kelly@mail.com”}}}

–オブジェクト(list)からYAMLの文字列を生成する
set bStr to retYAMLStringFromObject(bRes)
(*  
“—
- names:
- Smith
- Shelton
- Kelly
emails:
- smith@mail.com
- shelton@mail.com
- kelly@mail.com


*)

on retObjectFromYAMLString(aYAMLstr as string)
  set aStr to current application’s NSString’s stringWithString:aYAMLstr
  
set aData to aStr’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aData to current application’s YAMLSerialization’s objectsWithYAMLString:aStr options:(4096) |error|:(missing value)
  
return aData as list
end retObjectFromYAMLString

on retYAMLStringFromObject(anObject)
  set aString to (current application’s YAMLSerialization’s createYAMLStringWithObject:anObject options:(1) |error|:(missing value)) as string
  
return aString
end retYAMLStringFromObject

★Click Here to Open This Script 

2016/12/15 PDFしおり用データをNumbersから取得

PDFに「しおり」を作成する元のデータをNumbers上に記述しておくと、作成用のデータを取得・変換するAppleScriptです。構文確認および実行には、Shane Stanleyの「BridgePlus」AppleScript Libraries(フリー)のインストールを必要とします。

また、Numbersで(↓)のような書類を作成して、Numbersでオープンしていることが動作の前提条件です。

numbers_shiori.png

元のプログラムでは直接Script Editor上でレコードとして記述するのが、なかなか大変。また、親項目をタイトル文字列で記述するのも(作業時にミスりそうで)大変だったので、Numbers書類上で記述できるようにしてみたものです。

shiori.png

親項目は番号で記述するようにして、ID自体の連番の生成もAppleScriptから行い、極力作業ミスが発生しないように配慮してみました。

shiori2.png

AppleScript名:しおり用データをNumbersから取得
【コメント】 Book2_index_v2 を前提としています
– Created 2016-12-15 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use framework “Foundation”
use scripting additions
use BridgePlus : script “BridgePlus”
–http://piyocast.com/as/archives/4363

set aData to getIndexRecListFromNumbers() of me
–> {{|index|:3, title:”広告”, |parent|:”"}, {|index|:4, title:”本書購入特典のご案内”, |parent|:”"},…..

–NumbersのデータからPDFに付けるしおりのデータを取得する
on getIndexRecListFromNumbers()
  tell application “Numbers”
    tell window 1
      set aWinProp to properties
    end tell
    
    
set aDoc to document of aWinProp
    
tell aDoc
      tell active sheet
        tell table 1
          set colNum to column count
          
if colNum is not equal to 4 then error “Illegal Column Numbers”
          
set rowNum to row count
          
set vList to value of every cell
        end tell
      end tell
    end tell
  end tell
  
  
–Transform 1D array to 2D array
  
load framework
  
set tdList to (current application’s SMSForder’s subarraysFrom:(vList) groupedBy:colNum |error|:(missing value)) as list
  
–> {{”ID”, “index”, “title”, “parent”}, {1.0, 3.0, “広告”, missing value}, …..
  
  
–Skip First Row
  
set td2List to rest of tdList –first itemだけスキップする
  
  
set mokujiRecords to {}
  
repeat with i in td2List
    copy i to {anID, anIND, aTITLE, aParent}
    
    
–log {anID, anIND, aTITLE, aParent}
    
if aParent is not equal to missing value then
      set bParent to contents of item 3 of (item aParent of td2List)
    else
      set bParent to “”
    end if
    
    
set tmpRec to {|index|:(contents of anIND) as integer, title:aTITLE, |parent|:bParent}
    
set the end of mokujiRecords to tmpRec
  end repeat
  
  
return mokujiRecords
  
end getIndexRecListFromNumbers

★Click Here to Open This Script 

2016/12/14 レコードのうち、最大の値を持つKeyを返す

レコードのうち、最大の値を持つキーを返すAppleScriptです。

AppleScriptからMicrosoftの表情認識APIを呼び出せるようになったはいいものの、得られたデータからどの表情の可能性が高いかを判定する必要があり、そのために書いてみたものです。

Cocoaの機能を用いて、レコードのキーを取り出したり、さまざまな処理を行っています。こうした処理が手軽にできるようになったのは、実にいいことです。

さらに、画像から顔認識を行い、表情を読み取ってデータ化できるというのは、実に素晴らしいと思うものです。

ただ、いろいろ実験を行ってみたところ・・・恐れとか悲しみとか軽蔑といった微妙な感情については検出しづらいことがわかってきました。最大の値のものをピックアップするという方法ではなく、微妙な感情については別途評価を行う必要がありそうです。

mona_lisa_by_leonardo_da_vinci_from_c2rmf_retouched-1.jpg
–> “neutral”

donaldtrump61815_resized.png
–> “anger”

AppleScript名:レコードのうち、最大の値を持つKeyを返す
– Created 2016-12-14 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4359

set aDict to current application’s NSDictionary’s dictionaryWithDictionary:{sadness:0.01133416, anger:1.03975857E-4, happiness:2.90919736E-4, fear:2.28432211E-4, neutral:0.9830475, contempt:2.4698046E-4, disgust:1.02946949E-4, surprise:0.00464509334}
set aMacKey to retMaxValueKey(aDict) of me
–>  ”neutral”

on retMaxValueKey(aDict)
  set aValList to aDict’s allValues()
  
set maxVal to (sort1DNumList(aValList, false) of me)’s firstObject()
  
set keyList to (aDict’s allKeys()) as list
  
  
repeat with i in keyList
    set j to contents of i
    
set aTmp to (aDict’s valueForKey:j)
    
set aRes to compareNumerically(maxVal, aTmp) of me
    
if aRes = true then return j
  end repeat
  
  
return false
end retMaxValueKey

–Numerical Strings Compare
on compareNumerically(aText as text, bText as text)
  set aStr to current application’s NSString’s stringWithString:aText
  
return (aStr’s compare:bText options:(current application’s NSNumericSearch)) = current application’s NSOrderedSame
end compareNumerically

–1D List(数値)をsort / ascOrderがtrueだと昇順ソート、falseだと降順ソート
on sort1DNumList(theList, aBool)
  set theSet to current application’s NSSet’s setWithArray:theList
  
set theDescriptor to current application’s NSSortDescriptor’s sortDescriptorWithKey:(missing value) ascending:aBool
  
set sortedList to theSet’s sortedArrayUsingDescriptors:{theDescriptor}
  
return (sortedList)
end sort1DNumList

★Click Here to Open This Script 

2016/12/14 &と=で区切られたテキストをrecordに 改

「aParam=1234&bParam=2345」のように、URLのパラメータ部のような文字列を「&」と「=」で区切ってrecordとして出力するAppleScriptです。

「&と=で区切られたテキストをrecordに(NSScanner版)」をedama2さんがさらに改良したものです。

「先日掲載されたNSScannerのスクリプト、自分でも試してみてとても便利だったんですが、値なしの時にエラーになるので修正してみました。

あとリファレンス本に「repeat until」はあんまり使わないと書いてあったので使ってみました。(edama2さん)」

なるほど。たしかにラベルだけで値が存在しないケースに対応できるとよさそうです。

AppleScript名:&と=で区切られたテキストをrecordに 改
– Created 2016-12-12 by Shane Stanley
– Modified 2016-12-14 by edama2
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
–http://piyocast.com/as/archives/4358

set aParamStr to "access_token=XXxxx(XXxXXXXXxxXxxXXx))&expires=86399&name="
set aDict to (parseStrByAmpAndEqual(aParamStr) of me)
–>  {expires:"86399", |name|:"", access_token:"XXxxx(XXxXXXXXxxXxxXXx))"}

on parseStrByAmpAndEqual(aParamStr)
  set theScanner to current application’s NSScanner’s scannerWithString:aParamStr
  
set aDict to current application’s NSMutableDictionary’s |dictionary|()
  
  
repeat until (theScanner’s isAtEnd as boolean)
    – terminate check, return the result (aDict) to caller
    
set {theResult, theKey} to theScanner’s scanUpToString:"=" intoString:(reference)
    
    
– skip over separator
    
theScanner’s scanString:"=" intoString:(missing value)
    
set {theResult, theValue} to theScanner’s scanUpToString:"&" intoString:(reference)
    
if theValue is missing value then set theValue to "" –>追加
    
    
– skip over separator
    
theScanner’s scanString:"&" intoString:(missing value)
    
    
aDict’s setObject:theValue forKey:theKey
  end repeat
  
  
return aDict as record
end parseStrByAmpAndEqual

★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/12/12 &と=で区切られたテキストをrecordに(NSScanner版)

「aParam=1234&bParam=2345」のように、URLのパラメータ部のような文字列を「&」と「=」で区切ってrecordとして出力するAppleScriptです。

Shane Stanleyから「NSScannerを使った実装例」を教えてもらいました。「高速ではないけど、NSSCannerは便利なクラスなので慣れておいたほうがいいよ」という実例です。

Cocoaには、AppleScriptの処理系にはまったく存在していない強力な機能があり、使うととても便利で、プログラムを短く書くことができ、圧倒的に高速処理が可能です。

 リスト項目のユニーク化:NSSet
 リスト項目のユニーク化と出現回数カウント:NSCountedSet
 リスト項目のユニーク化とソート:NSMutableIndexSet
 リスト項目のしぼりこみ:NSPredicate

など、もはやプログラムを書く上で「ないと困る!」というレベルの道具になっています。ファイルパスの加工が強力なNSStringもないと困るレベルのものです。

NSScannerについて、Shaneのプログラムをそのまま掲載するとこんな感じですが、これは「巨大なプログラムを書き慣れた人が書く」タイプのプログラムであり、個別に理解を深めようとすると・・・つまり、「教材」として提示する際には若干の清書が必要になります。

AppleScript名:&と=で区切られたテキストをrecordに(NSScanner Orig)
– Created 2016-12-12 by Shane Stanley
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4356

set aParamStr to “access_token=XXxxx(XXxXXXXXxxXxxXXx))&expires=86399″
set aDict to (parseStrByAmpAndEqual(aParamStr) of me) as record

on parseStrByAmpAndEqual(aParamStr)
  set theScanner to current application’s NSScanner’s scannerWithString:aParamStr
  
set aDict to current application’s NSMutableDictionary’s |dictionary|()
  
repeat
    set {theResult, theKey} to theScanner’s scanUpToString:“=” intoString:(reference)
    
if theResult as boolean is false then return aDict
    
– skip over separator
    
theScanner’s scanString:“=” intoString:(missing value)
    
set {theResult, theValue} to theScanner’s scanUpToString:“&” intoString:(reference)
    
– skip over separator
    
theScanner’s scanString:“&” intoString:(missing value)
    
aDict’s setObject:theValue forKey:theKey
  end repeat
end parseStrByAmpAndEqual

★Click Here to Open This Script 

自分が動作内容を確認するためには、こんな感じに

(1)意味を取りやすいように空行を追加
(2)プログラムの動きを理解しやすいように、さまざまな変数のログ表示を行う
(3)プログラムのInputとOutputがわかりやすいように、サンプルデータの処理結果を掲載

のように書き換えます。

AppleScript名:&と=で区切られたテキストをrecordに(NSScanner 2)
– Created 2016-12-12 by Shane Stanley
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4356

set aParamStr to “access_token=XXxxx(XXxXXXXXxxXxxXXx))&expires=86399″
set aDict to (parseStrByAmpAndEqual(aParamStr) of me) as record
–>  {expires:”86399″, access_token:”XXxxx(XXxXXXXXxxXxxXXx))”}

on parseStrByAmpAndEqual(aParamStr)
  set theScanner to current application’s NSScanner’s scannerWithString:aParamStr
  
log (theScanner’s scanLocation())
  
  
set aDict to current application’s NSMutableDictionary’s |dictionary|()
  
repeat
    – terminate check, return the result (aDict) to caller
    
set {theResult, theKey} to theScanner’s scanUpToString:“=” intoString:(reference)
    
if theResult as boolean is false then return aDict
    
log (theScanner’s scanLocation())
    
    
– skip over separator
    
theScanner’s scanString:“=” intoString:(missing value)
    
set {theResult, theValue} to theScanner’s scanUpToString:“&” intoString:(reference)
    
log (theScanner’s scanLocation())
    
    
– skip over separator
    
theScanner’s scanString:“&” intoString:(missing value)
    
    
aDict’s setObject:theValue forKey:theKey
    
log (theScanner’s scanLocation())
  end repeat
end parseStrByAmpAndEqual

★Click Here to Open This Script 

そして、最終的に確認用のコードを削除して(クリーニングして)、こういうリストを掲載しています。

AppleScript名:&と=で区切られたテキストをrecordに(NSScanner 1)
– Created 2016-12-12 by Shane Stanley
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4356

set aParamStr to “access_token=XXxxx(XXxXXXXXxxXxxXXx))&expires=86399″
set aDict to (parseStrByAmpAndEqual(aParamStr) of me) as record
–>  {expires:”86399″, access_token:”XXxxx(XXxXXXXXxxXxxXXx))”}

on parseStrByAmpAndEqual(aParamStr)
  set theScanner to current application’s NSScanner’s scannerWithString:aParamStr
  
set aDict to current application’s NSMutableDictionary’s |dictionary|()
  
  
repeat
    – terminate check, return the result (aDict) to caller
    
set {theResult, theKey} to theScanner’s scanUpToString:“=” intoString:(reference)
    
if theResult as boolean is false then return aDict
    
    
– skip over separator
    
theScanner’s scanString:“=” intoString:(missing value)
    
set {theResult, theValue} to theScanner’s scanUpToString:“&” intoString:(reference)
    
    
– skip over separator
    
theScanner’s scanString:“&” intoString:(missing value)
    
    
aDict’s setObject:theValue forKey:theKey
  end repeat
end parseStrByAmpAndEqual

★Click Here to Open This Script 

2016/12/09 &と=で区切られたテキストをrecordに

「aParam=1234&bParam=2345」のように、URLのパラメータ部のような文字列を「&」と「=」で区切ってrecordとして出力するAppleScriptです。

とあるWeb APIでOAuth認証を呼び出してアクセストークンを取得したら、こんな形式の文字列で返してきた偏屈なもの(Stack ExchangeのAPI)があったので、対処のために作成したものです。

よく作る(作り捨てする)タイプの処理なので、たぶん何度も書いていると思います、、、、

AppleScript名:&と=で区切られたテキストをrecordに
– Created 2016-12-09 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4355

set aParamStr to “access_token=XXxxx(XXxXXXXXxxXxxXXx))&expires=86399″
set aDict to (parseStrByAmpAndEqual(aParamStr) of me) as record
–> {expires:”86399″, access_token:”XXxxx(XXxXXXXXxxXxxXXxx))”}

on parseStrByAmpAndEqual(aParamStr)
  set aStr to current application’s NSString’s stringWithString:aParamStr
  
set aList to (aStr’s componentsSeparatedByString:“&”) as list
  
set aDict to current application’s NSMutableDictionary’s alloc()’s init()
  
  
repeat with i in aList
    set j to (current application’s NSString’s stringWithString:(contents of i))
    
set {aLabel, aValue} to (j’s componentsSeparatedByString:“=”)
    (
aDict’s setValue:aValue forKey:aLabel)
  end repeat
  
  
return aDict
end parseStrByAmpAndEqual

★Click Here to Open This Script 

2016/11/06 XMLをrecordにv2

XMLをrecordに変換するAppleScriptです。

以前にAppleScript-Users ML上で流れていたXML→record変換のAppleScriptでしたが、動作確認を行ってもうまく動かず、そのまま放置状態になっていました。

見直してみたところ、「NSMutableDictionary’s dictionary()」というカラのmutable dictionaryを作成する部分が、うまくAppleScriptの処理系に認識されていなかったようでした。少し書き直してみました。

AppleScript名:XMLをrecordにv2
–2015 Shane Stanley & Alex Zavatone
– Modified 2016-11-06 by Takaaki Naganoya
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4306

property dictStack : missing value – stack to hold array of dictionaries
property textInProgress : “” – string to collect text as it is found
property anError : missing value – if we get an error, store it here

set xmlString to “< ?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>

Saga
Nor
én
Malm
ö
Martin
Rohde
K
øbenhavn

set xmlRes to my makeRecordWithXML:xmlString
–> {|character|:{firstName:{|contents|:”Saga”}, lastName:{|contents|:”Norén”}, city:{|contents|:”Malmö“}, partner:{firstName:{|contents|:”Martin”}, lastName:{|contents|:”Rohde”}, city:{|contents|:”København”}, attributes:{approach:”dogged”}}}}

on makeRecordWithXML:xmlString
  – set up properties
  
set my dictStack to current application’s NSMutableArray’s array() – empty mutable array
  
set anEmpty to current application’s NSMutableDictionary’s |dictionary|()
  (
my dictStack)’s addObject:anEmpty – add empty mutable dictionary
  
set my textInProgress to current application’s NSMutableString’s |string|() – empty mutable string
  
  
– convert XML from string to data
  
set anNSString to current application’s NSString’s stringWithString:xmlString
  
set theData to anNSString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
  
– initialize an XML parser with the data
  
set theNSXMLParser to current application’s NSXMLParser’s alloc()’s initWithData:theData
  
  
– set this script to be the parser’s delegate
  
theNSXMLParser’s setDelegate:me
  
  
– tell it to parse the XML
  
set theResult to theNSXMLParser’s parse()
  
if theResult then – went OK, get first item on stack
    return ((my dictStack)’s firstObject()) as record
  else – error, so return error
    error (my anError’s localizedDescription() as text)
  end if
end makeRecordWithXML:

– this is an XML parser delegate method. Called when new element found
on parser:anNSXMLParser didStartElement:elementName namespaceURI:aString qualifiedName:qName attributes:aRecord
  – store reference to last item on the stack
  
set parentDict to my dictStack’s lastObject()
  
  
– make new child
  
set childDict to current application’s NSMutableDictionary’s |dictionary|()
  
  
– if there are attributes, add them as a record with key “attributes”
  
if aRecord’s |count|() > 0 then
    childDict’s setValue:aRecord forKey:“attributes”
  end if
  
  
– see if there’s already an item for this key
  
set existingValue to parentDict’s objectForKey:elementName
  
  
if existingValue is not missing value then
    – there is, so if it’s an array, store it…
    
if (existingValue’s isKindOfClass:(current application’s NSMutableArray)) as boolean then
      set theArray to existingValue
    else
      – otherwise create an array and add it
      
set theArray to current application’s NSMutableArray’s arrayWithObject:existingValue
      
parentDict’s setObject:theArray forKey:elementName
    end if
    
    
– then add the new dictionary to the array
    
theArray’s addObject:childDict
  else
    – add new dictionary directly to the parent
    
parentDict’s setObject:childDict forKey:elementName
  end if
  
  
– also add the new dictionary to the end of the stack
  (
my dictStack)’s addObject:childDict
end parser:didStartElement:namespaceURI:qualifiedName:attributes:

– this is an XML parser delegate method. Called at the end of an element
on parser:anNSXMLParser didEndElement:elementName namespaceURI:aString qualifiedName:qName
  – if any text has been stored, add it as a record with key “contents”
  
if my textInProgress’s |length|() > 0 then
    set dictInProgress to my dictStack’s lastObject()
    
dictInProgress’s setObject:textInProgress forKey:“contents”
    
    
– reset textInProgress property for next element
    
set my textInProgress to current application’s NSMutableString’s |string|()
  end if
  
  
– remove last item from the stack
  
my dictStack’s removeLastObject()
end parser:didEndElement:namespaceURI:qualifiedName:

– this is an XML parser delegate method. Called when string is found. May be called repeatedly
on parser:anNSXMLParser foundCharacters:aString
  – only append string if it’s not solely made of space characters (which should be, but aren’t, caught by another delegate method)
  
if (aString’s stringByTrimmingCharactersInSet:(current application’s NSCharacterSet’s whitespaceAndNewlineCharacterSet()))’s |length|() > 0 then
    (my textInProgress)’s appendString:aString
  end if
end parser:foundCharacters:

– this is an XML parser delegate method. Called when there’s an error
on parser:anNSXMLParser parseErrorOccurred:anNSError
  set my anError to anNSError
end parser:parseErrorOccurred:

★Click Here to Open This Script 

2016/11/05 XMLをNSDictionaryに

オープンソースのXMLDictionary(By Nick Lockwood)をFramework化した「XmlToDictKit」を呼び出して、XMLをNSDictionaryにするAppleScriptです。

XMLをNSDictionaryにするのには、なかなか手こずらされており、依然としてSatimageのXMLLib OSAXが手放せない状況ですが、OSAXを使わずになんとかする方法についてはつねに模索しておりました。

「XML Parser」などをキーワードにGithub上でいろいろ物色していたところ、そんなに気合の入ったキーワードでなくても、「XML NSDictionary」ぐらいでいろいろ見つかりました。

それらを人気順でソートし上から順番に物色。条件が合って手軽にフレームワーク化に成功したのがこの「XMLDictionary」です。

XMLをNSDictionaryに変換し、さらにNSDictionaryをrecordまで変換すればAppleScriptで簡単に取り扱えます(NSDictionaryの属性値ラベルに空白などが入っていなければ)。

フレームワークについては、例によってOS X 10.10をターゲットにしてビルドしてみました。~/Library/Frameworksに入れてご利用ください(あくまで自己責任で)。

XMLそのものよりも、RSSのParseが割と手間だったので、RSSを手軽に扱えるようになったことのメリットが大きいと感じています。

→ FrameworkのZipアーカイブのダウンロード(30KB)

AppleScript名:XMLをDictionaryに(remote file)
– Created 2016-11-05 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “XmlToDictKit” –https://github.com/nicklockwood/XMLDictionary
–http://piyocast.com/as/archives/4304

set aURL to current application’s |NSURL|’s alloc()’s initWithString:“http://www.ibiblio.org/xml/examples/shakespeare/all_well.xml”
set xmlString to current application’s NSString’s alloc()’s initWithContentsOfURL:aURL encoding:(current application’s NSUTF8StringEncoding) |error|:(missing value)
if xmlString = missing value then return false
set xmlDoc to (current application’s NSDictionary’s dictionaryWithXMLString:xmlString) as record

★Click Here to Open This Script 

AppleScript名:XMLをDictionaryに(local file)
– Created 2016-11-05 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “XmlToDictKit” –https://github.com/nicklockwood/XMLDictionary
–http://piyocast.com/as/archives/4304

set aFile to POSIX path of (choose file)
set aURL to current application’s |NSURL|’s fileURLWithPath:aFile
set xmlString to current application’s NSString’s alloc()’s initWithContentsOfURL:aURL encoding:(current application’s NSUTF8StringEncoding) |error|:(missing value)
if xmlString = missing value then return false
set xmlDoc to (current application’s NSDictionary’s dictionaryWithXMLString:xmlString) as record

★Click Here to Open This Script 

AppleScript名:RSSをDictionaryに(remote file)
– Created 2016-11-05 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “XmlToDictKit” –https://github.com/nicklockwood/XMLDictionary
–http://piyocast.com/as/archives/4304

set aURL to current application’s |NSURL|’s alloc()’s initWithString:“http://piyocast.com/as/feed”
set xmlString to current application’s NSString’s alloc()’s initWithContentsOfURL:aURL encoding:(current application’s NSUTF8StringEncoding) |error|:(missing value)
if xmlString = missing value then return false
set xmlDoc to (current application’s NSDictionary’s dictionaryWithXMLString:xmlString) as record
set aChannel to |item| of channel of xmlDoc
set aDoc1 to first item of aChannel
–>  {category:{”Application Control”, “10.10 savvy”, “10.11 savvy”, “10.12 savvy”}, dc:creator:”maro”, comments:”http://piyocast.com/as/archives/4304#comments”, title:”XMLをNSDictionaryに”, link:”http://piyocast.com/as/archives/4304″, pubDate:”Sat, 05 Nov 2016 21:21:17 +0900″, description:”オープンソースのXMLDictionary(By Nick Lockwood)をFramework化した「XmlToDictKit」を呼び出して、XMLをNSDictionaryにするAppleScr…”, guid:{__text:”http://piyocast.com/as/archives/4304″, _isPermaLink:”false”}, wfw:commentRss:”http://piyocast.com/as/archives/4304/feed/”}

★Click Here to Open This Script 

2016/10/31 listのrecordをplistにserializeして、plistをde-serializeする

Listでまとめたrecordをエンコーディングしてplist文字列にして(serialize)、さらにそれを元に戻す(deserialize)AppleScriptです。

本来、「エンコーダーとデコーダーは一緒に作る」はずのものですが、plistへのエンコードScriptしかできておらず、世の中に転がっているObjective-Cのサンプルとにらめっこしてもさっぱり分からなかったので、AppleScript Users MLに質問を投げてみたら、Shane Stanleyからあっさりと、

「これ、もうdeprecatedなmethodなんで使っちゃダメだよ。あと、deserializeするときには文字列をそのまま渡さないでね」

というアドバイスとコードそのものを教えてもらいました。というわけで、整理して掲載しておきます。

AppleScript名:listのrecordをplistにserializeして、plistをde-serializeする
– Created 2016-10-30 by Takaaki Naganoya
– Modified 2016-10-31 by Shane Stanley
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4296

set aList to {{theName:“Sound Track”, numberOfTimes:1721}, {theName:“Rock”, numberOfTimes:942}}
set aRes to serializeToPlistString(aList) of me
set bRes to (deserializeToPlistString(aRes) of me) as list
–>  {{numberOfTimes:1721, theName:”Sound Track”}, {numberOfTimes:942, theName:”Rock”}}

–list or record –> XML-format plist string
on serializeToPlistString(aList as {list, record})
  set pListData to current application’s NSPropertyListSerialization’s dataWithPropertyList:aList |format|:(current application’s NSPropertyListXMLFormat_v1_0) options:0 |error|:(missing value)
  
set bStr to (current application’s NSString’s alloc()’s initWithData:pListData encoding:(current application’s NSUTF8StringEncoding)) as string
  
return bStr
end serializeToPlistString

–XML-format plist string–> list or record
on deserializeToPlistString(aStr as string)
  set deStr to current application’s NSString’s stringWithString:aStr
  
set theData to deStr’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aList to current application’s NSPropertyListSerialization’s propertyListWithData:theData options:(current application’s NSPropertyListMutableContainersAndLeaves) |format|:(missing value) |error|:(missing value)
  
return aList
end deserializeToPlistString

★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/28 ベクトルのコサイン類似度を計算する v2

ベクトル同士のコサイン類似度を計算するAppleScriptの改良版です。

「ガンダム」「アムロ・レイ」「ガンキャノン」

という3つの言葉の類似度を計算したところ、

 (1)「ガンダムとガンキャノン」
 (2)「アムロ・レイとガンキャノン」
 (3)「アムロ・レイとガンダム」

の順に類似度が計算されてしまい、「ガンキャノン問題」として問題視されました。

Apitoreさんより「ベクトルの長さで正規化するとよい」という助言をいただき、いろいろ調べて改良してみたものです。

その結果、

 (1)「ガンダムとガンキャノン」
 (2)「ガンダムとアムロ・レイ」
 (3)「アムロ・レイとガンキャノン」

の順番に計算結果が出るようになり、一安心しています。

AppleScript名:ベクトルのコサイン類似度を計算する v2
– Created 2016-10-28 by Takaaki Naganoya
– 2016 Piyomaru Software
–以下のページを参照しました
–http://www.cse.kyoto-su.ac.jp/~g0846020/keywords/cosinSimilarity.html
–http://piyocast.com/as/archives/4293

set aRec to {startTime:“1477635439753″, |word|:“ガンダム”, endTime:“1477635439754″, |log|:“Success.”, |vector|:{-0.173511996865, 0.186350002885, 0.288884997368, 0.107758000493, 0.287292987108, 0.119222998619, 0.246404007077, -0.332718014717, -0.309754997492, 0.326180011034, 0.499563008547, -0.131274998188, 0.238587006927, -0.317025989294, -0.008682000451, 0.619518995285, 0.316774994135, -0.053045999259, 0.028408000246, -0.428086012602, -0.607270002365, 0.657006978989, 0.676119029522, 0.203107997775, -0.316679000854, 0.082666002214, -0.310041993856, 0.603790998459, 0.468154996634, 0.206021994352, 0.028333999217, -0.087867997587, -0.358617991209, 0.128415003419, 0.037046000361, -0.260360985994, -0.210915997624, -0.015564999543, -0.180935993791, 0.083250999451, -0.193316996098, 0.250761002302, 0.202393993735, -0.155086994171, 0.036905001849, 0.418110996485, 0.067231997848, -0.116421997547, -0.341655999422, 0.05919200182, 0.389017999172, -0.544457018375, -0.244222998619, 0.337971001863, 0.163227006793, 0.117914997041, -0.042672999203, -0.215838998556, 0.191798001528, 0.174465000629, -0.055527999997, 0.121985003352, -0.046782001853, -0.347003012896, -0.425747990608, 0.576622009277, 0.322551995516, -0.426732987165, -0.580038011074, -0.579939007759, 0.29191800952, 0.076993003488, 0.220890000463, -0.374727010727, 0.11546599865, 0.239750996232, -0.599394023418, 0.581852018833, -0.124090999365, -0.180638998747, 0.5446190238, -0.65235298872, 0.456075012684, 0.494884997606, 0.484183996916, 0.020594999194, 0.362075001001, -0.089720003307, 0.230300992727, 0.281304001808, 0.286731004715, 0.374053001404, -0.177623003721, 0.090945996344, -0.399394005537, 0.573561012745, 0.130366995931, 0.15235799551, -0.323183000088, 0.143162995577, 0.274109989405, 0.520829021931, 0.171061992645, -0.079223997891, -0.529187977314, 0.109682999551, -0.433328002691, -0.26163700223, -0.624193012714, 1.016695022583, -0.359939008951, 0.183194994926, 0.018155999482, -0.14149799943, -0.240256994963, 0.413583993912, 0.378069013357, -0.35772100091, 0.056784998626, 0.842347025871, -0.041281998158, 0.121949002147, -0.55557101965, -0.012947999872, -0.451754003763, 0.095089003444, 0.468187004328, -0.376350998878, 0.78152102232, 0.307211011648, -0.709493994713, -0.498080998659, -0.324746012688, -0.199910998344, 0.066499002278, -0.047377999872, -0.116584002972, -0.392536014318, 0.534222006798, -0.004480999894, 0.106450997293, 0.492370009422, -0.205549001694, -0.138926997781, -0.25551199913, 0.122440002859, -0.063225999475, 0.400667995214, -0.294638991356, -0.618892014027, -0.163193002343, -0.203306004405, -0.500850975513, 0.183540001512, -0.46670499444, -0.213167995214, -0.276264995337, 0.204497992992, -0.156630992889, 0.383563011885, 0.496547013521, 0.113885998726, 0.020508000627, -0.389571994543, 0.218575999141, -0.064272001386, -0.026503000408, -0.173591002822, 0.254842013121, 0.464908987284, -0.103918999434, -0.269497007132, 0.070835001767, 0.203738003969, 0.181133002043, -0.246653005481, -0.136351004243, -0.24780100584, 0.326480001211, -0.193390995264, -0.041266001761, 0.245121002197, -0.118851996958, -0.083679996431, -0.171301007271, 0.031709998846, -0.332567989826, -0.059787001461, 0.001627000049, -0.048179998994, -0.396964997053, 0.31639200449, -0.352144986391, 0.008798000403, -0.232255995274, -0.394028007984, 0.116296000779, 0.171626001596, 0.013128999621, -0.295181006193}, processTime:“1″}

set bRec to {startTime:“1477635460487″, |word|:“アムロ・レイ”, endTime:“1477635460487″, |log|:“Success.”, |vector|:{0.03407099843, 0.10659699887, 0.127606004477, -0.13093200326, 0.17143599689, -0.386317998171, 0.223474994302, -0.63797801733, -0.21521499753, 0.215176001191, 0.607864022255, 0.176204994321, -0.184221997857, 0.361393988132, 0.16435199976, 0.652567028999, 0.242174997926, -0.325437992811, 0.394699007273, -0.393142998219, -0.312689989805, 0.173059001565, 0.660327970982, -0.07935500145, -0.34789198637, -0.223361998796, -0.153245002031, 0.175822004676, 0.104438997805, 0.50031298399, 0.16487300396, -0.138288006186, -0.054469998926, 0.100807003677, 0.05673699826, -0.067408002913, 0.017626000568, -0.07518299669, -0.201003998518, -0.005166999996, -0.088767997921, 0.615378975868, 0.126588001847, -0.127129003406, 0.159886002541, 0.374316006899, -0.03769300133, -0.050262000412, 0.063642002642, 0.169852003455, 0.409768998623, -0.453413009644, -0.122203998268, 0.294649988413, 0.11619400233, 0.576021015644, -0.117141999304, -0.486111998558, 0.283957004547, -0.101135000587, -0.078950002789, 0.173895001411, -0.083311997354, 0.028666999191, -0.21795900166, 0.184988006949, -0.185552999377, -0.399181008339, -0.098324000835, -0.352382987738, 0.075119003654, -0.17525100708, 0.373353004456, -0.152073994279, 0.051049001515, 0.110192000866, -0.726333022118, 0.334517002106, -0.075718998909, -0.354999989271, 0.655991971493, -0.298009991646, 0.508288025856, 0.243245005608, 0.239094004035, -0.086781002581, -0.161684006453, -0.507776021957, 0.261065006256, 0.284135997295, 0.307931989431, 0.213102996349, 0.136901006103, 0.397157996893, -0.464179992676, 0.262387990952, 0.380863010883, -0.071410998702, -0.165156006813, 0.135739997029, 0.283789008856, 0.820464015007, -0.359337002039, 0.155907005072, -0.330406993628, 0.400182008743, 0.119069002569, -0.182172998786, -0.147823005915, 0.674717009068, -0.637548983097, -0.110163003206, 0.025941999629, -0.274792999029, -0.184186995029, 0.543650984764, 0.189351007342, -0.354799985886, -0.005100999959, 0.318190008402, 0.088809996843, 0.300523996353, -0.336024999619, -0.279132008553, -0.489708006382, 0.119197003543, 0.357654988766, -0.009196000174, 0.908367991447, 0.127497002482, -0.877427995205, -0.362311005592, 0.045292001218, 0.228176996112, 0.003637999995, -0.021867999807, 0.264483004808, -0.531185984612, 0.075176000595, -0.258029013872, 0.180007994175, 0.549230992794, 0.085698001087, 0.164148002863, 0.198268994689, 0.206103995442, 0.182986006141, 0.262667000294, -0.509878993034, -0.44718798995, 0.136090993881, -0.18127900362, -0.148757994175, 0.263893991709, -0.386029988527, -0.198642000556, 0.220932006836, 0.202956005931, 0.048569001257, 0.194492995739, 0.180389001966, 0.697315990925, 0.139156997204, -0.228732004762, 0.201507002115, -0.043519001454, -0.419701009989, -0.424322992563, 0.085637003183, 0.513258993626, -0.90073800087, -0.365974009037, -0.043921001256, -0.012201000005, 0.343672990799, -0.352656006813, 0.335415005684, -0.584758996964, -0.045765001327, -0.614067018032, 0.095513001084, 0.461355000734, -0.371479004622, -0.612700998783, 0.043907001615, 0.56964302063, -0.364234000444, -0.080641999841, 0.140410006046, -0.180212005973, -0.543507993221, 0.225119993091, -0.285872995853, 0.08812700212, -0.299535006285, 0.026107000187, -0.221154004335, 0.39902600646, 0.160721004009, -0.433041006327}, processTime:“0″}

set cRec to {startTime:“1477635477524″, |word|:“ガンキャノン”, endTime:“1477635477524″, |log|:“Success.”, |vector|:{0.072226002812, -0.123470000923, 0.229124993086, 0.024253999814, 0.090787000954, -0.554116010666, 0.127351999283, -0.277144014835, -0.273193985224, 0.049963001162, 0.459771990776, -0.271524995565, 0.12961499393, -0.174656003714, 0.56355202198, 0.922071993351, 0.629299998283, 0.046698000282, 0.017914000899, -0.093442000449, -0.435452997684, 0.599241018295, 0.600166976452, -0.085536003113, -0.361353993416, 0.147931993008, -0.145070001483, 0.277393013239, 0.016388999298, 0.262728989124, 0.212905004621, 0.189909994602, -0.292201012373, 0.332486003637, 0.308021992445, -0.036079999059, -0.271553009748, -0.20025600493, -0.090479001403, 0.268270999193, -0.272096991539, 0.199298992753, 0.15323600173, -0.348919987679, 0.290313988924, 0.180563002825, -0.179280996323, -0.239015996456, -0.279372006655, 0.237440004945, 0.588576972485, -0.73378098011, -0.176648005843, 0.382687985897, 0.023191999644, 0.218005001545, 0.104557998478, -0.209011003375, -0.139624997973, 0.429735004902, 0.027498999611, -0.114556998014, 0.439689993858, -0.343279987574, -0.206854000688, 0.510254979134, 0.38624098897, -0.413646996021, -0.354449987411, -0.344117999077, -0.059645000845, 0.084478996694, 0.013961999677, -0.162633001804, 0.16069200635, -0.239609003067, -0.710308015347, 0.490334004164, 0.209011003375, -0.068875998259, 0.620296001434, -0.67517799139, 0.308690011501, 0.349496990442, 0.543331027031, 0.014736999758, 0.265686988831, -0.242991000414, 0.08111499995, 0.433555006981, 0.534663021564, 0.440093994141, 0.046149998903, 0.456764996052, -0.426779985428, 1.205618023872, 0.433625012636, 0.19698600471, -0.024245999753, -0.195654004812, 0.778258979321, 1.02594602108, -0.0279610008, -0.08894199878, -0.757061004639, 0.001425000024, -0.355924993753, 0.055286001414, 0.080266997218, 0.389950990677, -0.39531698823, 0.532419979572, 0.106784999371, -0.050149999559, -0.230510994792, 0.41841301322, 0.785488009453, 0.17853499949, 0.012822999619, 0.533285975456, 0.046064998955, 0.457116991282, -0.336412996054, 0.086345002055, -0.490134000778, -0.060506001115, 0.505389988422, -0.440815001726, 0.701705992222, 0.233691006899, -0.556469976902, -0.175993993878, 0.047694001347, -0.439999997616, 0.048073999584, 0.32014799118, -0.157723993063, -0.406812012196, -0.235781997442, 0.149423003197, 0.221878007054, 0.274697005749, -0.336528003216, -0.027574999258, -0.05121299997, 0.049157001078, -0.061744999141, 0.571139991283, -0.426824986935, -0.511521995068, -0.180418998003, -0.173777997494, -0.501815021038, 0.165115997195, -0.52134001255, -0.110209003091, -0.296977996826, 0.113278999925, -0.254267007113, 0.274937003851, 0.171541005373, 0.226473003626, -0.05610800162, -0.063437998295, 0.179731994867, 0.016723999754, -0.181597992778, -0.0923660025, 0.297807008028, 0.402370989323, -0.933079004288, -0.062503002584, 0.156483992934, 0.066535003483, -0.129124999046, -0.464343994856, 0.046394001693, -0.510720014572, 0.117109999061, -0.378863990307, 0.104653000832, 0.297672003508, 0.057688999921, -0.290758013725, -0.042853001505, 0.69950902462, -0.666705012321, -0.062345001847, 0.131890997291, 0.12205799669, 0.190595000982, 0.141661003232, -0.141069993377, 0.29548099637, -0.252436995506, -0.12503400445, 0.245934993029, 0.292874008417, -0.207329005003, -0.592303991318}, processTime:“0″}

set aList to |vector| of aRec
set bList to |vector| of bRec
set cList to |vector| of cRec

set aRes to calcCosinSimilarity(aList, bList) of me –ガンダムとアムロ・レイ
set bRes to calcCosinSimilarity(aList, cList) of me –ガンダムとガンキャノン
set cRes to calcCosinSimilarity(bList, cList) of me –アムロ・レイとガンキャノン

return {aRes, bRes, cRes}
–> {44.057800255168, 50.555325012947, 32.83911534787}
–(1)「ガンダムとガンキャノン」、(2)「ガンダムとアムロ・レイ」、(3)「アムロ・レイとガンキャノン」の順番

–コサイン類似度をベクターのリストから計算する
on calcCosinSimilarity(aList, bList)
  set aLen to length of aList
  
set bLen to length of bList
  
if aLenbLen then error “Not Same Item Nums”
  
  
set aRes to 0
  
repeat with i from 1 to aLen
    set a1 to (contents of item i of aList)
    
set b1 to (contents of item i of bList)
    
    
set vL to getVectorLength(a1, b1) of me
    
set normF to (1 / vL)
    
set aRes to aRes + ((a1 * normF) * (b1 * normF))
  end repeat
  
  
return aRes
end calcCosinSimilarity

–ベクトルの長さを求める
on getVectorLength(a, b)
  return getSQR(a ^ 2 + b ^ 2) of me
end getVectorLength

–平方根を求める
on getSQR(aNum)
  return (aNum ^ 0.5)
end getSQR

★Click Here to Open This Script 

2016/06/06 レコードの値をクリア

与えられたレコード(record)型の変数について、ラベルはそのままで値についてクリアを行うAppleScriptです。

たしかに、ありそうで書いていなかったような気がする内容です。単純にヌル(ヌル文字列)でクリアするバージョンと、与えたrecordの各値の型を見てクリア時の初期値を変更するバージョンを作ってみました。

実際のAppleScriptのワークフローの中で使う場合には、AppleScriptアプレット起動後にplistファイルから設定値を読み取り、初期化動作が必要なものについてだけ初期化を行うというような内容になるでしょうか。

そのため、初期化してほしい属性ラベルのリストを渡すか、初期化してはいけない属性ラベルのリストを与えるかをしつつ、このような動作を行うものが「実戦レベルで使える」ルーチンになるはずです。

AppleScript名:レコードの値をクリア
– Created 2016-06-06 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.5″
use scripting additions
use framework “Foundation”

set initVal to “”
set aRec to {aaa:“111″, bbb:“222″, ccc:“333″}
set aRec to clearRecordValues(aRec, initVal) of me
–>  {aaa:”", bbb:”", ccc:”"}

on clearRecordValues(aRec, initVal)
  set aDic to current application’s NSMutableDictionary’s dictionaryWithDictionary:aRec
  
repeat with i in (aDic’s allKeys() as list)
    set j to contents of i
    (
aDic’s setValue:initVal forKey:j)
  end repeat
  
return aDic as record
end clearRecordValues

★Click Here to Open This Script 

AppleScript名:レコードの値をクリア v2
– Created 2016-06-06 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.5″
use scripting additions
use framework “Foundation”

set initVal to “”
set aaaaaaaRec to {aaa:“111″, bbb:2.1234, ccc:-1}
set bRec to clearRecordValues(aaaaaaaRec) of me
–>  {aaa:”", bbb:0, ccc:0}

on clearRecordValues(paramRec)
  set aDic to current application’s NSMutableDictionary’s dictionaryWithDictionary:paramRec
  
set keyList to (aDic’s allKeys() as list)
  
set valList to (aDic’s allValues() as list)
  
  
repeat with i from 1 to (length of keyList)
    set aKey to contents of item i of keyList
    
set tmpVal to contents of item i of valList
    
set aClass to class of tmpVal
    
    
if aClass is in {number, integer, real} then
      set initVal to 0
    else if aClass is in {string, text, Unicode text} then
      set initVal to “”
    else
      set initVal to missing value
    end if
    
    (
aDic’s setValue:initVal forKey:aKey)
  end repeat
  
return aDic as record
end clearRecordValues

★Click Here to Open This Script 

2016/05/11 ShangriLa Anime API V1のデータを取得する

技術書典の一般向けサイトで、参加サークルの詳細な説明文が見られるようになったので、ひととおり見てみました。すると、ノーマークだったサークルが面白いことをやっていることが分かったので、ちょっと見てみました。

ブースB-11の「秋葉原IT戦略研究所」さんが「SNSデータ解析で見る2016年アニメ界の展望」という本を出されるそうで、それだけだと何のことかわからなかったのですが、Twiter上でハッシュタグをつけてつぶやいている膨大なデータを分析してみた、という話だと理解しました。

その活動の一環として、「秋葉原IT戦略研究所」さんではRESTful APIで呼べるアニメ情報データベースを公開されており、とくに認証も何もかかっていないので気楽に呼べそうです。

そこで、実際にAppleScriptから呼んでみることにしました。実行にはShane StanleyのAppleScript Libraries「Bridge Plus」のインストールを必要とします。

まずは、このデータベースが対象にしているクール(1クール13話、1年を52週と仮定したときに1年は4クール)の情報を取得してみました。各クールが何年何月何日から何年何月何日までなのか、という情報は提供してくれないため、各自でカレンダー計算を行う必要がありそうです(4/1なのに前クールの最終回を放映していたというパターンもあるので、そのあたりどうなるのかルールが少々不明)。

AppleScript名:GET method REST API_Anime API_get cours
– Created 2016-05-11 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use BridgePlus : script “BridgePlus”

set reqURLStr to “http://api.moemoe.tokyo/anime/v1/master/cours”

set aRes to callRestGETAPIAndParseResults(reqURLStr) of me

set aRESTres to json of aRes
set aRESCode to responseCode of aRes
–>  200
set aRESHeader to responseHeader of aRes
–>  {Connection:”keep-alive”, Access-Control-Allow-Origin:”*”, Content-Type:”application/json; charset=utf-8″, Content-Length:”353″, Server:”nginx/1.8.0″, Date:”Wed, 11 May 2016 01:14:49 GMT”}

return aRESTres
–>  (NSDictionary) {7:{id:7, year:2015, cours:3}, 3:{id:3, year:2014, cours:3}, 8:{id:8, year:2015, cours:4}, 4:{id:4, year:2014, cours:4}, 9:{id:9, year:2016, cours:1}, 5:{id:5, year:2015, cours:1}, 1:{id:1, year:2014, cours:1}, 6:{id:6, year:2015, cours:2}, 10:{id:10, year:2016, cours:2}, 2:{id:2, year:2014, cours:2}}

–GET methodのREST APIを呼ぶ
on callRestGETAPIAndParseResults(aURL)
  load framework
  
–Request  
  
set aRequest to current application’s NSMutableURLRequest’s requestWithURL:(current application’s |NSURL|’s URLWithString:aURL)
  
aRequest’s setHTTPMethod:“GET”
  
aRequest’s setCachePolicy:(current application’s NSURLRequestReloadIgnoringLocalCacheData)
  
aRequest’s setHTTPShouldHandleCookies:false
  
aRequest’s setTimeoutInterval:60
  
–CALL REST API
  
set aRes to current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value)
  
–Parse Results
  
set resList to aRes as list
  
set bRes to contents of (first item of resList)
  
set resStr to current application’s NSString’s alloc()’s initWithData:bRes encoding:(current application’s NSUTF8StringEncoding)
  
set jsonString to current application’s NSString’s stringWithString:resStr
  
set jsonData to jsonString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aJsonDict to current application’s NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
–Get Response Code
  
set dRes to contents of second item of resList
  
set resCode to ASify from (dRes’s statusCode())
  
–Get Response Header
  
set resHeaders to ASify from (dRes’s allHeaderFields())
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
end callRestGETAPIAndParseResults

★Click Here to Open This Script 

次に、指定した年のアニメ作品に関する情報を取得。

AppleScript名:GET method REST API_Anime API_getInfo_in_a_year
– Created 2016-05-11 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use BridgePlus : script “BridgePlus”

set reqURLStr to “http://api.moemoe.tokyo/anime/v1/master/” & “2016″

set aRes to callRestGETAPIAndParseResults(reqURLStr) of me

set aRESTres to json of aRes
set aRESCode to responseCode of aRes
–>  200
set aRESHeader to responseHeader of aRes
–>  {Connection:”keep-alive”, Access-Control-Allow-Origin:”*”, Content-Type:”application/json; charset=utf-8″, Content-Length:”353″, Server:”nginx/1.8.0″, Date:”Wed, 11 May 2016 01:14:49 GMT”}

return aRESTres
–>  (NSArray) {{id:281, title:”機動戦士ガンダム サンダーボルト”}, {id:282, title:”プリンス・オブ・ストライド オルタナティブ”}, {id:283, title:”無彩限のファントム・ワールド”}, {id:284, title:”ハルチカ〜ハルタとチカは青春する〜”}, {id:285, title:”ノルン+ノネット”}, {id:286, title:”アクティヴレイド −機動強襲室第八係−”}, {id:287, title:”少女たちは荒野を目指す”}, {id:288, title:”僕だけがいない街”}, {id:289, title:”おじさんとマシュマロ”}, {id:290, title:”ファンタシースターオンライン2 ジ アニメーション”}, {id:291, title:”だがしかし”}, {id:292, title:”暗殺教室(第2期)”}, {id:293, title:”ディバインゲート”}, {id:294, title:”おしえて!ギャル子ちゃん”}, {id:295, title:”石膏ボーイズ”}, {id:296, title:”霊剣山 星屑たちの宴”}, {id:297, title:”GATE 自衛隊 彼の地にて、斯く戦えり(2期)”}, {id:298, title:”昭和元禄落語心中”}, {id:299, title:”紅殻のパンドラ”}, {id:300, title:”ブブキ・ブランキ”}, {id:301, title:”ラクエンロジック”}, {id:302, title:”デュラララ!!×2 結”}, {id:303, title:”ナースウィッチ小麦ちゃんR”}, {id:304, title:”虹色デイズ”}, {id:305, title:”大家さんは思春期!”}, {id:306, title:”Dimension W”}, {id:307, title:”灰と幻想のグリムガル”}, {id:308, title:”シュヴァルツェスマーケン”}, {id:309, title:”最弱無敗の神装機竜(バハムート)”}, {id:310, title:”赤髪の白雪姫(第2期)”}, {id:311, title:”てーきゅう(第7期)”}, {id:312, title:”魔法少女なんてもういいですから。”}, {id:313, title:”蒼の彼方のフォーリズム”}, {id:314, title:”この素晴らしい世界に祝福を!”}, {id:315, title:”亜人”}, {id:316, title:”FAIRY TAIL ZERO”}, {id:317, title:”ももくり”}, {id:318, title:”この男子、魔法がお仕事です。”}, {id:319, title:”SUSHI POLICE”}, {id:320, title:”血液型くん!4″}, {id:321, title:”迷家‐マヨイガ‐”}, {id:322, title:”宇宙パトロールルル子”}, {id:323, title:”機動戦士ガンダムユニコーン RE:0096″}, {id:324, title:”影鰐-KAGEWANI-承”}, {id:325, title:”ぼのぼの”}, {id:326, title:”フューチャーカード バディファイト トリプルディー”}, {id:327, title:”逆転裁判”}, {id:328, title:”学戦都市アスタリスク 2nd SEASON”}, {id:329, title:”僕のヒーローアカデミア”}, {id:330, title:”マクロス”}, {id:331, title:”コンクリート・レボルティオ〜超人幻想〜THE LAST SONG”}, {id:332, title:”くまみこ”}, {id:333, title:”怪盗ジョーカー(シーズン3)”}, {id:334, title:”ばくおん!!”}, {id:335, title:”聖戦ケルベロス 竜刻のファタリテ”}, {id:336, title:”ハンドレッド”}, {id:337, title:”薄桜鬼〜御伽草子〜”}, {id:338, title:”ジョーカー・ゲーム”}, {id:339, title:”双星の陰陽師”}, {id:340, title:”SUPER LOVERS”}, {id:341, title:”鬼斬”}, {id:342, title:”文豪ストレイドッグス”}, {id:343, title:”あんハピ♪”}, {id:344, title:”クロムクロ”}, {id:345, title:”ネトゲの嫁は女の子じゃないと思った?”}, {id:346, title:”甲鉄城のカバネリ”}, {id:347, title:”少年メイド”}, {id:348, title:”坂本ですが?”}, {id:349, title:”田中くんはいつもけだるげ”}, {id:350, title:”キズナイーバー”}, {id:351, title:”はいふり”}, {id:352, title:”ふらいんぐうぃっち”}, {id:353, title:”とんかつDJアゲ太郎”}, {id:354, title:”三者三葉”}, {id:355, title:”うさかめ”}, {id:356, title:”マギ シンドバッドの冒険”}, {id:357, title:”Re:ゼロから始める異世界生活”}, {id:358, title:”うしおととら(第3クール)”}, {id:359, title:”ワガママハイスペック”}, {id:360, title:”ジョジョの奇妙な冒険 ダイヤモンドは砕けない”}, {id:361, title:”テラフォーマーズ リベンジ”}, {id:362, title:”プリパラ(3rdシーズン)”}, {id:363, title:”エンドライド”}, {id:364, title:”ビッグオーダー”}}

–GET methodのREST APIを呼ぶ
on callRestGETAPIAndParseResults(aURL)
  load framework
  
–Request  
  
set aRequest to current application’s NSMutableURLRequest’s requestWithURL:(current application’s |NSURL|’s URLWithString:aURL)
  
aRequest’s setHTTPMethod:“GET”
  
aRequest’s setCachePolicy:(current application’s NSURLRequestReloadIgnoringLocalCacheData)
  
aRequest’s setHTTPShouldHandleCookies:false
  
aRequest’s setTimeoutInterval:60
  
–CALL REST API
  
set aRes to current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value)
  
–Parse Results
  
set resList to aRes as list
  
set bRes to contents of (first item of resList)
  
set resStr to current application’s NSString’s alloc()’s initWithData:bRes encoding:(current application’s NSUTF8StringEncoding)
  
set jsonString to current application’s NSString’s stringWithString:resStr
  
set jsonData to jsonString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aJsonDict to current application’s NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
–Get Response Code
  
set dRes to contents of second item of resList
  
set resCode to ASify from (dRes’s statusCode())
  
–Get Response Header
  
set resHeaders to ASify from (dRes’s allHeaderFields())
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
end callRestGETAPIAndParseResults

★Click Here to Open This Script 

最後に、年およびクール(1〜4)を指定して作品情報を取得するものです。

AppleScript名:GET method REST API_Anime API_getInfo_in_a_year_and_cour
– Created 2016-05-11 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use BridgePlus : script “BridgePlus”

set aYear to 2016
set aCour to 1

set reqURLStr to “http://api.moemoe.tokyo/anime/v1/master/” & (aYear as string) & “/” & (aCour as string)

set aRes to callRestGETAPIAndParseResults(reqURLStr) of me

set aRESTres to json of aRes
set aRESCode to responseCode of aRes
–>  200
set aRESHeader to responseHeader of aRes
–>  {Connection:”keep-alive”, Access-Control-Allow-Origin:”*”, Content-Type:”application/json; charset=utf-8″, Content-Length:”353″, Server:”nginx/1.8.0″, Date:”Wed, 11 May 2016 01:14:49 GMT”}

return aRESTres
–>  (NSArray) {{id:281, title_short3:”", sex:0, sequel:0, created_at:”2016-01-01 23:40:06.0″, public_url:”http://gundam-tb.net/”, twitter_hash_tag:”gundam_tb”, title:”機動戦士ガンダム サンダーボルト”, updated_at:”2016-01-01 23:40:06.0″, twitter_account:”gundam_tb”, title_short1:”サンダーボルト”, title_short2:”", cours_id:9}, …….

–GET methodのREST APIを呼ぶ
on callRestGETAPIAndParseResults(aURL)
  load framework
  
–Request  
  
set aRequest to current application’s NSMutableURLRequest’s requestWithURL:(current application’s |NSURL|’s URLWithString:aURL)
  
aRequest’s setHTTPMethod:“GET”
  
aRequest’s setCachePolicy:(current application’s NSURLRequestReloadIgnoringLocalCacheData)
  
aRequest’s setHTTPShouldHandleCookies:false
  
aRequest’s setTimeoutInterval:60
  
–CALL REST API
  
set aRes to current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value)
  
–Parse Results
  
set resList to aRes as list
  
set bRes to contents of (first item of resList)
  
set resStr to current application’s NSString’s alloc()’s initWithData:bRes encoding:(current application’s NSUTF8StringEncoding)
  
set jsonString to current application’s NSString’s stringWithString:resStr
  
set jsonData to jsonString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aJsonDict to current application’s NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
–Get Response Code
  
set dRes to contents of second item of resList
  
set resCode to ASify from (dRes’s statusCode())
  
–Get Response Header
  
set resHeaders to ASify from (dRes’s allHeaderFields())
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
end callRestGETAPIAndParseResults

★Click Here to Open This Script 

放送されている番組の情報を提供する情報発信元としては、新聞などにテレビ番組のラテ欄情報を提供している東京ニュース通信社あるいは日刊編集センターか、EPGのメタデータを提供している(株)エムデータあたりがBtoB向け専門でやっています。BtoC向けにデータを出すかどうかは不明ですが、話をしてみるといいんじゃないでしょうか?

2016/03/06 REST APIに対してGET、POST、PUT、DELETEのmethodを呼び出す

AppleScriptでWeb上のREST APIを呼び出すじっけんです。Web上にあるREST APIの実験用サービスJSONPlaceholderをAppleScriptから呼び出して各種メソッド呼び出しの実証を行ってみました。

できてしまえばたいしたことはありませんが、地道に調査しておかないと今日明日でいきなりやれと言われても困ってしまいます。

実際に、NTTdocomoの形態素解析のREST APIをmethod=POSTでAppleScriptから呼び出してみたところ、

–> (NSDictionary) {request_id:”record001″, word_list:{{{”私”}, {”の”}, {”名前”}, {”は”}, {” ”}, {”長野”}, {”谷”}, {”です”}, {”。”}}}, info_filter:”form”}

となりました(words ofの実行結果と変わり映えしない、、、、)。固有名詞としてあらかじめ人名を登録しておきたいケースがほとんどなので、ちょっとこれだと使えない感じではあります。ただ、実際に稼働しているAPIを呼べることは実証できました。

AppleScript名:GET method REST API
– Created 2016-03-03 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use BridgePlus : script “BridgePlus”

set reqURLStr to “http://jsonplaceholder.typicode.com/posts”

set aRes to callRestGETAPIAndParseResults(reqURLStr) of me

set aRESTres to json of aRes
–>  (NSArray) {{body:”quia et suscipit suscipit recusandae consequuntur expedita et cum reprehenderit molestiae ut ut quas totam nostrum rerum est autem sunt rem eveniet architecto”, id:1, title:”sunt aut facere repellat provident occaecati excepturi optio reprehenderit”, userId:1},…

set aRESCode to responseCode of aRes
–>  200

set aRESHeader to responseHeader of aRes
–>  {Content-Type:”application/json; charset=utf-8″, Pragma:”no-cache”, X-Powered-By:”Express”, Via:”1.1 vegur”, Server:”Cowboy”, Expires:”-1″, Cache-Control:”no-cache”, Date:”Sun, 06 Mar 2016 07:27:26 GMT”, Access-Control-Allow-Credentials:”true”, Content-Length:”27520″, Connection:”keep-alive”, X-Content-Type-Options:”nosniff”, Etag:”W/\”6b80-uPwhAkDat3Fl5TugzmyYpQ\”", Vary:”Origin”}

–GET methodのREST APIを呼ぶ
on callRestGETAPIAndParseResults(aURL)
  load framework
  
set aRequest to current application’s NSMutableURLRequest’s requestWithURL:(current application’s |NSURL|’s URLWithString:aURL)
  
aRequest’s setHTTPMethod:“GET”
  
  
set aRes to current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value)
  
set resList to aRes as list
  
  
set bRes to contents of (first item of resList)
  
set resStr to current application’s NSString’s alloc()’s initWithData:bRes encoding:(current application’s NSUTF8StringEncoding)
  
  
set jsonString to current application’s NSString’s stringWithString:resStr
  
set jsonData to jsonString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aJsonDict to current application’s NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
  
–Get Response Code
  
set dRes to contents of second item of resList
  
set resCode to ASify from (dRes’s statusCode())
  
  
–Get Response Header
  
set resHeaders to ASify from (dRes’s allHeaderFields())
  
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
  
end callRestGETAPIAndParseResults

★Click Here to Open This Script 

AppleScript名:POST method REST API
– Created 2016-03-06 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use BridgePlus : script “BridgePlus”

set reqURLStr to “http://jsonplaceholder.typicode.com/posts”
set aPostData to {title:“foo”, body:“bar”, userId:1}

set aRes to callRestPOSTAPIAndParseResults(reqURLStr, aPostData) of me

set aRESTres to json of aRes
–>  (NSDictionary) {body:”bar”, id:101, title:”foo”, userId:1}

set aRESCode to responseCode of aRes
–>  200

set aRESHeader to responseHeader of aRes
–>  {Content-Type:”application/json; charset=utf-8″, Pragma:”no-cache”, X-Powered-By:”Express”, Via:”1.1 vegur”, Server:”Cowboy”, Expires:”-1″, Cache-Control:”no-cache”, Date:”Sun, 06 Mar 2016 07:47:56 GMT”, Access-Control-Allow-Credentials:”true”, Content-Length:”65″, Connection:”keep-alive”, X-Content-Type-Options:”nosniff”, Etag:”W/\”41-DL1IzEbjanDFwm6hF2BfJw\”", Vary:”Origin, X-HTTP-Method-Override”}

–POST methodのREST APIを呼ぶ
on callRestPOSTAPIAndParseResults(aURL, aPostData)
  load framework
  
set dataJson to current application’s NSJSONSerialization’s dataWithJSONObject:aPostData options:0 |error|:(missing value)
  
set aRequest to current application’s NSMutableURLRequest’s requestWithURL:(current application’s |NSURL|’s URLWithString:aURL)
  
  
aRequest’s setHTTPMethod:“POST”
  
aRequest’s setHTTPBody:dataJson
  
aRequest’s setValue:“application/json” forHTTPHeaderField:“Accept”
  
aRequest’s setValue:“application/json” forHTTPHeaderField:“Content-Type”
  
  
set aRes to current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value)
  
set resList to aRes as list
  
  
set bRes to contents of (first item of resList)
  
set resStr to current application’s NSString’s alloc()’s initWithData:bRes encoding:(current application’s NSUTF8StringEncoding)
  
  
set jsonString to current application’s NSString’s stringWithString:resStr
  
set jsonData to jsonString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aJsonDict to current application’s NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
  
–Get Response Code
  
set dRes to contents of second item of resList
  
set resCode to ASify from (dRes’s statusCode())
  
  
–Get Response Header
  
set resHeaders to ASify from (dRes’s allHeaderFields())
  
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
  
end callRestPOSTAPIAndParseResults

★Click Here to Open This Script 

AppleScript名:PUT method REST API
– Created 2016-03-06 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use BridgePlus : script “BridgePlus”

set reqURLStr to “http://jsonplaceholder.typicode.com/posts/1″
set aPostData to {title:“ぴよまるソフトウェア”, body:“PUTのじっけん”, userId:1}
–set aPostData to {title:”foo”, body:”bar”, userId:1}

set aRes to callRestPUTAPIAndParseResults(reqURLStr, aPostData) of me

set aRESTres to json of aRes
–>  (NSDictionary) {body:”PUTのじっけん”, id:1, title:”ぴよまるソフトウェア”, userId:1}
return aRESTres
set aRESCode to responseCode of aRes
–>  200

set aRESHeader to responseHeader of aRes
–>  {Content-Type:”application/json; charset=utf-8″, Pragma:”no-cache”, X-Powered-By:”Express”, Via:”1.1 vegur”, Server:”Cowboy”, Expires:”-1″, Cache-Control:”no-cache”, Date:”Sun, 06 Mar 2016 07:47:56 GMT”, Access-Control-Allow-Credentials:”true”, Content-Length:”65″, Connection:”keep-alive”, X-Content-Type-Options:”nosniff”, Etag:”W/\”41-DL1IzEbjanDFwm6hF2BfJw\”", Vary:”Origin, X-HTTP-Method-Override”}

–PUT methodのREST APIを呼ぶ
on callRestPUTAPIAndParseResults(aURL, aPostData)
  load framework
  
set dataJson to current application’s NSJSONSerialization’s dataWithJSONObject:aPostData options:0 |error|:(missing value)
  
set aRequest to current application’s NSMutableURLRequest’s requestWithURL:(current application’s |NSURL|’s URLWithString:aURL)
  
  
aRequest’s setHTTPMethod:“PUT”
  
aRequest’s setHTTPBody:dataJson
  
aRequest’s setValue:“application/json” forHTTPHeaderField:“Accept”
  
aRequest’s setValue:“application/json” forHTTPHeaderField:“Content-Type”
  
  
set aRes to current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value)
  
set resList to aRes as list
  
  
set bRes to contents of (first item of resList)
  
set resStr to current application’s NSString’s alloc()’s initWithData:bRes encoding:(current application’s NSUTF8StringEncoding)
  
  
set jsonString to current application’s NSString’s stringWithString:resStr
  
set jsonData to jsonString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aJsonDict to current application’s NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
  
–Get Response Code
  
set dRes to contents of second item of resList
  
set resCode to ASify from (dRes’s statusCode())
  
  
–Get Response Header
  
set resHeaders to ASify from (dRes’s allHeaderFields())
  
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
  
end callRestPUTAPIAndParseResults

★Click Here to Open This Script 

AppleScript名:DELETE method REST API
– Created 2016-03-06 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use BridgePlus : script “BridgePlus”

set reqURLStr to “http://jsonplaceholder.typicode.com/posts/1″

set aRes to callRestDELETEAPIAndParseResults(reqURLStr) of me

set aRESTres to json of aRes
–>  (NSDictionary) {}

set aRESCode to responseCode of aRes
–>  200

set aRESHeader to responseHeader of aRes
–>  {Content-Type:”application/json; charset=utf-8″, Pragma:”no-cache”, X-Powered-By:”Express”, Via:”1.1 vegur”, Server:”Cowboy”, Expires:”-1″, Cache-Control:”no-cache”, Date:”Sun, 06 Mar 2016 07:53:51 GMT”, Access-Control-Allow-Credentials:”true”, Content-Length:”2″, Connection:”keep-alive”, X-Content-Type-Options:”nosniff”, Etag:”W/\”2-mZFLkyvTelC5g8XnyQrpOw\”", Vary:”Origin”}

–DELETE methodのREST APIを呼ぶ
on callRestDELETEAPIAndParseResults(aURL)
  load framework
  
set aRequest to current application’s NSMutableURLRequest’s requestWithURL:(current application’s |NSURL|’s URLWithString:aURL)
  
set respF to false
  
  
aRequest’s setHTTPMethod:“DELETE”
  
  
set aRes to current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value)
  
set resList to aRes as list
  
  
set bRes to contents of (first item of resList)
  
set resStr to current application’s NSString’s alloc()’s initWithData:bRes encoding:(current application’s NSUTF8StringEncoding)
  
  
set jsonString to current application’s NSString’s stringWithString:resStr
  
set jsonData to jsonString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aJsonDict to current application’s NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
  
–Get Response Code
  
set dRes to contents of second item of resList
  
set resCode to ASify from (dRes’s statusCode())
  
  
–Get Response Header
  
set resHeaders to ASify from (dRes’s allHeaderFields())
  
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
  
end callRestDELETEAPIAndParseResults

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

2015/07/27 ASOCでAppleScript設定ファイルの読み込み

Cocoaの機能を用いてScript Editor自身の書式設定ファイル(plist)を読み込むAppleScriptです。

とくに特殊な処理は必要なく、一般的なplistファイルに記録されたDictionaryをrecordに読み込んで、最終的にNSDataから各種データに復元しています。もうちょっとこなれた書き方もありそうですが、とりあえずデータを取り出すレベルまで書いてみた次第です。

Scriptが返している各種Cocoaのオブジェクトについては、Apple純正Script Editorでは表示されず、ASObjC Explorer 4で表示されるものを記載しています。

本当に色とフォント種別(+サイズほか関連情報)しか入っていないのにはびっくりです。

AppleScript名:ASOCでAppleScript設定ファイルの読み込み
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

set AppleScriptFontFile to “~/Library/Preferences/com.apple.applescript.plist”
set pathString to current application’s NSString’s stringWithString:AppleScriptFontFile
set newPath to pathString’s stringByExpandingTildeInPath()
set aRec to retDictFromPlist(newPath) of me

set asAttr to AppleScriptSourceAttributes of aRec
set asAttrArray to {}

repeat with i in asAttr
  
  set aCol to NSColor of i
  
set aFont to NSFont of i
  
  set aColO to (current application’s NSUnarchiver’s unarchiveObjectWithData:aCol)
  
set aFonO to (current application’s NSUnarchiver’s unarchiveObjectWithData:aFont)
  
  set the end of asAttrArray to {aColO, aFonO}
  
end repeat

asAttrArray
–>  {​​​​​{​​​​​​​(NSDeviceRGBColor) NSDeviceRGBColorSpace 0.568627 0.156863 0.564706 1, ​​​​​​​(NSFont) “Osaka 12.00 pt. P [] (0×600000249cc0) fobj=0×6000005e9800, spc=3.33″​​​​​}, ​​​​​{​​​​​​​(NSDeviceRGBColor) NSDeviceRGBColorSpace 0.372549 0.368627 0.368627 1, ​​​​​​​(NSFont) “Osaka 12.00 pt. P [] (0×600000249cc0) fobj=0×6000005e9800, spc=3.33″​​​​​}, ​​​​​{​​​​​​​(NSDeviceRGBColor) NSDeviceRGBColorSpace 0.054902 0.243137 0.984314 1, ​​​​​​​(NSFont) “Osaka 12.00 pt. P [] (0×600000249cc0) fobj=0×6000005e9800, spc=3.33″​​​​​}, ​​​​​{​​​​​​​(NSDeviceRGBColor) NSDeviceRGBColorSpace 0.470588 0.203922 0.796079 1, ​​​​​​​(NSFont) “Osaka 12.00 pt. P [] (0×600000249cc0) fobj=0×6000005e9800, spc=3.33″​​​​​}, ​​​​​{​​​​​​​(NSDeviceRGBColor) NSDeviceRGBColorSpace 0.988235 0.164706 0.109804 1, ​​​​​​​(NSFont) “Osaka 12.00 pt. P [] (0×600000249cc0) fobj=0×6000005e9800, spc=3.33″​​​​​}, ​​​​​{​​​​​​​(NSDeviceRGBColor) NSDeviceRGBColorSpace 0 0 0 1, ​​​​​​​(NSFont) “Osaka 12.00 pt. P [] (0×600000249cc0) fobj=0×6000005e9800, spc=3.33″​​​​​}, ​​​​​{​​​​​​​(NSDeviceRGBColor) NSDeviceRGBColorSpace 0.568627 0.321569 0.0666667 1, ​​​​​​​(NSFont) “Osaka 12.00 pt. P [] (0×600000249cc0) fobj=0×6000005e9800, spc=3.33″​​​​​}, ​​​​​{​​​​​​​(NSDeviceRGBColor) NSDeviceRGBColorSpace 0 0 0 1, ​​​​​​​(NSFont) “Osaka 12.00 pt. P [] (0×600000249cc0) fobj=0×6000005e9800, spc=3.33″​​​​​}, ​​​​​{​​​​​​​(NSDeviceRGBColor) NSDeviceRGBColorSpace 0.152941 0.788235 0.788235 1, ​​​​​​​(NSFont) “Osaka 12.00 pt. P [] (0×600000249cc0) fobj=0×6000005e9800, spc=3.33″​​​​​}, ​​​​​{​​​​​​​(NSDeviceRGBColor) NSDeviceRGBColorSpace 0.0588235 0.243137 0.984314 1, ​​​​​​​(NSFont) “Osaka 12.00 pt. P [] (0×600000249cc0) fobj=0×6000005e9800, spc=3.33″​​​​​}, ​​​​​{​​​​​​​(NSDeviceRGBColor) NSDeviceRGBColorSpace 0.121569 0.713726 0.988235 1, ​​​​​​​(NSFont) “Osaka 12.00 pt. P [] (0×600000249cc0) fobj=0×6000005e9800, spc=3.33″​​​​​}, ​​​​​{​​​​​​​(NSDeviceRGBColor) NSDeviceRGBColorSpace 0.505882 0.227451 0.85098 1, ​​​​​​​(NSFont) “Osaka 12.00 pt. P [] (0×600000249cc0) fobj=0×6000005e9800, spc=3.33″​​​​​}, ​​​​​{​​​​​​​(NSDeviceRGBColor) NSDeviceRGBColorSpace 0.364706 0.211765 0.572549 1, ​​​​​​​(NSFont) “Osaka 12.00 pt. P [] (0×600000249cc0) fobj=0×6000005e9800, spc=3.33″​​​​​}, ​​​​​{​​​​​​​(NSDeviceRGBColor) NSDeviceRGBColorSpace 0.0352941 0.215686 0.733333 1, ​​​​​​​(NSFont) “Osaka 12.00 pt. P [] (0×600000249cc0) fobj=0×6000005e9800, spc=3.33″​​​​​}, ​​​​​{​​​​​​​(NSDeviceRGBColor) NSDeviceRGBColorSpace 0.0313726 0.215686 0.733333 1, ​​​​​​​(NSFont) “Osaka 12.00 pt. P [] (0×600000249cc0) fobj=0×6000005e9800, spc=3.33″​​​​​}, ​​​​​{​​​​​​​(NSDeviceRGBColor) NSDeviceRGBColorSpace 0.0352941 0.215686 0.729412 1, ​​​​​​​(NSFont) “Osaka 12.00 pt. P [] (0×600000249cc0) fobj=0×6000005e9800, spc=3.33″​​​​​}, ​​​​​{​​​​​​​(NSDeviceRGBColor) NSDeviceRGBColorSpace 0.337255 0.215686 0.741176 1, ​​​​​​​(NSFont) “Osaka 12.00 pt. P [] (0×600000249cc0) fobj=0×6000005e9800, spc=3.33″​​​​​}, ​​​​​{​​​​​​​(NSDeviceRGBColor) NSDeviceRGBColorSpace 0.203922 0.12549 0.388235 1, ​​​​​​​(NSFont) “Osaka 12.00 pt. P [] (0×600000249cc0) fobj=0×6000005e9800, spc=3.33″​​​​​}​​​}

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

★Click Here to Open This Script 

2015/07/23 ASOCでレコードのリストをユニーク化

レコードのリスト(例:{{namae:”Piyomaru”,age:16}, {namae:”Piyoko”, age:13}})で、重複データ項目があった場合にそなえ、重複項目を削除する「ユニーク化」を行うAppleScriptObjCのScriptです。

BridgePlusを用いたバージョンと、ASObjCExtras.frameworkを用いたバージョンを掲載します。現在ではBridgePlusの使用が強く推奨されています。

AppleScript名:ASOCでレコードのリストをユニーク化(BridgePlus)
use AppleScript version “2.4″
use framework “Foundation”
use scripting additions
use script “BridgePlus”

load framework

set msRecList to {{msName:“格 陸戦型ジム 獲得済 COST: 120″, sortieTimes:12}, {msName:“狙 ジム・スナイパーカスタム 獲得済 COST: 200″, sortieTimes:6}, {msName:“狙 ジム・スナイパーカスタム 獲得済 COST: 200″, sortieTimes:6}}

set newMsList to uniquefyList(msRecList)
–>  {{sortieTimes:6, msName:”狙 ジム・スナイパーカスタム 獲得済 COST: 200″}, {sortieTimes:12, msName:”格 陸戦型ジム 獲得済 COST: 120″}}

–レコードのリストをユニーク化
on uniquefyList(aList)
  set msArray to Cocoaify aList
  
set aRes to current application’s NSSet’s setWithArray:(msArray’s allObjects())
  
set bRes to aRes’s allObjects()
  
set cRes to ASify from bRes as list
  
return cRes
end uniquefyList

★Click Here to Open This Script 

AppleScript名:ASOCでレコードのリストをユニーク化(ASObjCExtras.framework)
use AppleScript version “2.4″
use framework “Foundation”
use framework “ASObjCExtras”
use scripting additions

set msRecList to {{msName:“格 陸戦型ジム 獲得済 COST: 120″, sortieTimes:12}, {msName:“狙 ジム・スナイパーカスタム 獲得済 COST: 200″, sortieTimes:6}, {msName:“狙 ジム・スナイパーカスタム 獲得済 COST: 200″, sortieTimes:6}}

set newMsList to uniquefyList(msRecList)
–>  {{sortieTimes:6, msName:”狙 ジム・スナイパーカスタム 獲得済 COST: 200″}, {sortieTimes:12, msName:”格 陸戦型ジム 獲得済 COST: 120″}}

–レコードのリストをユニーク化
on uniquefyList(aList)
  set msArray to current application’s SMSFord’s Cocoaify:aList
  
set aRes to current application’s NSSet’s setWithArray:(msArray’s allObjects())
  
set bRes to aRes’s allObjects()
  
set cRes to bRes’s ASify() as list
  
return cRes
end uniquefyList

★Click Here to Open This Script 

2015/07/20 JSON文字列とrecordの相互変換

Cocoaの機能を用いて、JSON文字列をAppleScriptのrecordに変換、あるいはその逆変換を行うAppleScriptです。

recordまで変換しなくても、NSDictionaryとかNSMutableDictionaryで値を保持しておくほうが(入り組んだデータの場合に)アクセスが楽なので、recordにするかどうかは処理する人の自由です。

ただ、こうしてテストScriptを書いて思うのは、ShaneのASObjC Explorer 4の結果表示機能は(ASOCでScriptingを行うのに)必須のものだということです。

AppleScript名:ASOCでrecordをJSON文字列に
– Created 2015-07-20 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

set aRec to {abc:“test”, bcd:“test2″, cde:“test3″}

set aDict to current application’s NSDictionary’s dictionaryWithDictionary:aRec
–>  (NSDictionary) {abc:”test”, bcd:”test2″, cde:”test3″}

set jsonData to current application’s NSJSONSerialization’s dataWithJSONObject:aDict options:(0 as integer) |error|:(missing value) –0 is NSJSONWritingPrettyPrinted
–> (* (NSData) <7b226162 63223a22 74657374 222c2262 6364223a 22746573 7432222c 22636465 223a2274 65737433 227d> *)

set resString to current application’s NSString’s alloc()’s initWithData:jsonData encoding:(current application’s NSUTF8StringEncoding)
–>  (NSString) “{”abc”:”test”,”bcd”:”test2″,”cde”:”test3″}”

★Click Here to Open This Script 

AppleScript名:ASOCでjson文字列をrecordに
– Created 2015-07-20 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

set jsonText to “{\”abc\”:\”test\”,\”bcd\”:\”test2\”,\”cde\”:\”test3\”}”

set jsonString to current application’s NSString’s stringWithString:jsonText
–>  (NSString) “{”abc”:”test”,”bcd”:”test2″,”cde”:”test3″}”

set jsonData to jsonString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
–>  (NSData) <7b226162 63223a22 74657374 222c2262 6364223a 22746573 7432222c 22636465 223a2274 65737433 227d>

set aJsonDict to current application’s NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
–>  (NSDictionary) {abc:”test”, bcd:”test2″, cde:”test3″}

set aRec to aJsonDict as record
–>  {abc:”test”, bcd:”test2″, cde:”test3″}

★Click Here to Open This Script 

2015/07/13 ASOCでDict書き込み_3(Bridge Plus)

完全に趣味の内容のAppleScriptを、Shane Stanleyがアップデートして送ってくれました(汗)。趣味のScriptというのは、完全に作り捨てに近いというか、あまり後先考えずに作ってしまうことが多いですが・・・

書き換えポイントは、

 (1)ASObjcExtras.frameworkではなくBridgePlusライブラリを使用
 (2)NSFileManagerでディレクトリを作成
 (3)パスの組み立て時に「URLByAppendingPathComponent:」を使うことにより、フォルダを示すパスの末尾に「:」がついていなかったとか、「::」といった誤った表記を行ってしまった場合に備えている

とのこと。do shell scriptなんてダサいぜ、的なShaneのご意見もありましたが、使って簡潔になる箇所(dateコマンドとか)は使ってもいいんじゃないかと。いえ、意見はとってもわかるんですけど、「使えるものはなんでも使う派」なんで(^ー^;;

OS X 10.11ではaliasやfileとNSURLが正確にBridgeされるようになるので、パスの取り扱いについては機能向上(簡潔な記述)が期待ができそうです。”NSURL”を書くと予約語とぶつかる、とかいうダサダサな仕様が直ることは10.11に期待したいです。

しかし、英語圏のShaneから正確な日本語データの入ったプログラムが送られてくると(リスト内のリンクをクリックすればScript Editorに転送されるのはわかっているとはいえ)、かなりビビります(^ー^;;

AppleScript名:ASOCでDict書き込み_3(Bridge Plus)
use AppleScript version “2.4″
use framework “Foundation”
use scripting additions
use script “BridgePlus” – instead of ASOBjCExtras framework

load framework – BridgePlus command to load

set a1List to {“msName”, “sortieTimes”}
set b1List to {{“近 装甲強化型ジム 獲得済 COST: 200″, 66}, {“遠 ジム・キャノン 獲得済 COST: 160″, 43}, {“近 ザクII(F2) 獲得済 COST: 160″, 42}, {“近 ジム・コマンド 獲得済 COST: 200″, 32}, {“近 ジム(WD隊) 獲得済 COST: 160″, 28}, {“近 陸戦型ガンダム 獲得済 COST: 220″, 24}, {“近 ジム改 獲得済 COST: 240″, 22}, {“遠 ガンタンク 獲得済 COST: 200″, 22}, {“格 ジム(指揮官機) 獲得済 COST: 160″, 20}, {“近 ジム 獲得済 COST: 120″, 19}, {“遠 量産型ガンタンク 獲得済 COST: 160″, 14}, {“格 陸戦型ジム 獲得済 COST: 120″, 12}, {“格 ガンダム 獲得済 COST: 280″, 11}, {“近 ジム・トレーナー 獲得済 COST: 120″, 9}, {“射 ジム・スナイパーII(WD隊) 獲得済 COST: 220″, 9}, {“射 陸戦型ガンダム(ジム頭) 獲得済 COST: 200″, 7}, {“格 ガンダムEz8 獲得済 COST: 240″, 6}, {“近 ジム・寒冷地仕様 獲得済 COST: 200″, 6}, {“狙 ジム・スナイパーカスタム 獲得済 COST: 200″, 6}, {“格 ジム・ストライカー 獲得済 COST: 180″, 4}, {“格 ガンキャノン重装型 獲得済 COST: 160″, 3}, {“近 アクア・ジム 獲得済 COST: 160″, 2}, {“射 ガンキャノン 獲得済 COST: 200″, 2}, {“近 ジム・コマンドライトアーマー 獲得済 COST: 160″, 1}, {“格 ボールK型 獲得済 COST: 120″, 0}, {“格 B.D.2号機 獲得済 COST: 260″, 0}, {“格 プロトタイプガンダム 獲得済 COST: 280″, 0}, {“近 パワード・ジム 獲得済 COST: 240″, 0}, {“射 デザート・ジム 獲得済 COST: 160″, 0}, {“遠 量産型ガンキャノン 獲得済 COST: 200″, 0}}

– BridgePlus uses SMSForder instead of SMSFord in ASOBjCExtras, but method is the same
set aArray to current application’s SMSForder’s subarraysIn:b1List asDictionariesUsingLabels:a1List |error|:(missing value)

set cRec to {msList:aArray, sortieDate:date string of (current date)}

set aName to “efsf.plist”
saveRecordToFolAsPlist(cRec, “戦場の絆”, aName) of me

on saveRecordToFolAsPlist(theRecord, folName, aName)
  
  
set myAppSupDir to POSIX path of (path to application support from user domain)
  
set folderURL to (current application’s class “NSURL”’s fileURLWithPath:myAppSupDir)’s URLByAppendingPathComponent:folName
  
  
–do shell script(mkdir -p)のかわりに、指定ディレクトリまで作成
  
current application’s NSFileManager’s defaultManager()’s createDirectoryAtURL:folderURL withIntermediateDirectories:true attributes:(missing value) |error|:(missing value)
  
  
set theDict to current application’s NSDictionary’s dictionaryWithDictionary:theRecord
  
set aRes to theDict’s writeToURL:(folderURL’s URLByAppendingPathComponent:aName) atomically:true
  
  
return aRes as boolean
  
end saveRecordToFolAsPlist

★Click Here to Open This Script 

2015/03/18 レコードとレコードの連結

This AppleScript is a sample of concatenation of multiple records. If two records have same property-labeled element & different value….

複数のレコードの連結を行うAppleScriptサンプルです。ラベル値が同じものは1つにまとめられる、というのはわかりますが、異なる値を持つ場合の挙動には注意が必要です。

AppleScript名:レコードとレコードの連結
–Case: same label (fullName) and same value ("abc")
set aRec to {fullName:"abc", anAge:46}
set bRec to {fullName:"abc", aInfo:10}

set cRec to aRec & bRec
–> {fullName:"abc", anAge:46, aInfo:10}

–Case: same label (fullName) and different value ("abc", "def")
set dRec to {fullName:"abc", anAge:46}
set eRec to {fullName:"def", aInfo:10}

set fRec to dRec & eRec
–> {fullName:"abc", anAge:46, aInfo:10}

set gRec to eRec & dRec
–> {fullName:"def", aInfo:10, anAge:46}

★Click Here to Open This Script