本エントリは Scala で ATND API を叩くScala で partition 関数を使用してリストの要素を振り分けるの続きです。

さて、ここまでくるともうひとつの ListBuffer 使用箇所も不変リストにしたい。したほうがいいはずだ、多分。
残った ListBuffer 使用箇所は、「"keyword=aaa,bbb", "ym=201207", "ymd=20120723"」といった要素を持つリストを用意して
mkString("&") で「"keyword=aaa,bbb&ym=201207&ymd=20120723"」といった文字列を作っている。
(話はそれるけど、要素の区切り文字や接頭辞・接尾辞をつけた文字列を作れる mkString って便利)
「::」で要素を連結したリストを作れば上手くいくのでは、と思い付き、以下のようにやってみた。
import scala.io.Source
import scala.xml.XML

import java.text.SimpleDateFormat

val source = Source.fromURL("http://api.atnd.org/events/?" + getParams(args))
val response = XML.loadString(source.mkString)

val orgFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss")
val showFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm")

response \\ "event" foreach { e =>
    println("『%s』\n  %s\n  %s\n" format(
    e \\ "title" text,
	showFormat.format(orgFormat.parse(e \\ "started_at" text)),
	e \\ "place" text))
}

def getParams(args:Array[String]):String = {
	val ymPattern = """^\d{6}$""".r
	val ymdPattern = """^\d{8}$""".r
	
	val (ymList, otherList) = args partition (ymPattern.findFirstIn(_) != None)
	val (ymdList, keywordList) = otherList partition (ymdPattern.findFirstIn(_) != None)
	
	val paramsList =
 		createSearchQuery("keyword", keywordList) ::
 		createSearchQuery("ym", ymList) ::
 		createSearchQuery("ymd", ymdList) ::
 		List()
	
	paramsList.mkString("&")
}

def createSearchQuery(key:String, params:Array[String]):String = {
	if (!params.isEmpty) {
		key + "=" + params.mkString(",")
	} else {
		""
	}
}
「::」は要素となる型を先頭に追加したリストを作るので、createSearchQuery で要素が空でなければキーと値を文字列にして、
空であれば空文字を返すようにした。
一見うまくいったっぽいけど、空文字はリストの要素なので、例えば何も引数を渡さないと getParams が「&&」という文字列を返してしまう。
(空文字3つのリストなので、空文字・&・空文字・&・空文字が返る)
期待する動作は「&&」ではなくて空文字を作ってほしいので、別の方法を考える。

「:::」で連結してみよう。
import scala.io.Source
import scala.xml.XML

import java.text.ParseException;
import java.text.SimpleDateFormat

val source = Source.fromURL("http://api.atnd.org/events/?" + getParams(args))
val response = XML.loadString(source.mkString)

val orgFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss")
val showFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm")

response \\ "event" foreach { e =>
    println("『%s』\n  %s\n  %s\n" format(
	e \\ "title" text,
	showFormat.format(orgFormat.parse(e \\ "started_at" text)),
	e \\ "place" text))
}

def getParams(args:Array[String]):String = {
	val ymFormat = new SimpleDateFormat("yyyyMM")
	ymFormat.setLenient(false)
	val ymdFormat = new SimpleDateFormat("yyyyMMdd")
	ymdFormat.setLenient(false)
	
	val (ymList, otherList) = args partition (validDateFormat(_, ymFormat))
	val (ymdList, keywordList) = otherList partition (validDateFormat(_, ymdFormat))
	
	val paramsList =
 		createSearchQuery("keyword", keywordList) :::
 		createSearchQuery("ym", ymList) :::
 		createSearchQuery("ymd", ymdList)
	
	paramsList.mkString("&")
}

def validDateFormat(arg:String, fmt:SimpleDateFormat):Boolean = {
	try {
		fmt.parse(arg)
		true
	} catch {
		case _:ParseException => false
	}
}

def createSearchQuery(key:String, params:Array[String]):List[String] = {
	if (!params.isEmpty) {
		List(key + "=" + params.mkString(","))
	} else {
		List()
	}
}
「:::」はリスト同士を連結して新しいリストを作る。
createSearchQuery で元の要素が何もなければ空リストを返すようにした。
これで何も引数を渡さないと getParams が空文字を返してくれるようになった。
(本題と関係ないけど、日付かどうかの判定を正規表現から
SimpleDateFormat で厳密にパースできるかどうかで判定する validDateFormat を作成した)

可変リスト使用箇所がなくなり、前より更に良くなった気がする!

Copyright© 2011-2021 Shunsuke Otani All Right Reserved .