{1,2,3,5,7}みたいなリストを"1-3,5,7"に変換する
電車で移動中にタイムラインに流れて来てちょっと面白そうだということで実装してみた。
{1,2,3,5,7}
— いくた♥️なお/2日目東I45b (@ikutana) 2018年2月16日
みたいな配列(またはリスト)を
“1-3,5,7”に変換するってのを書かなきゃいけない。逆の変換はそう難しくないのだけど。どう書こうかな。
スマートに書けるかな、と思ったけどそんな事はなかった。
object ListConverter { /** * {1,2,3,5,7}みたいな配列(またはリスト)を“1-3,5,7”に変換する */ def toSequenceString(list: List[Int]): String = { val sorted = list.sorted sorted.lastOption match { case None => "" case Some(last) => val (grouped, _) = sorted.init.foldRight((List(List(last)), last)) { case (cr, (h :: t, prv)) if prv - 1 == cr => ((cr :: h) :: t, cr) case (cr, (acc, _)) => (List(cr) :: acc, cr) } grouped.map { case Nil => "" case h :: Nil => h.toString case l => s"${l.head}-${l.last}" }.mkString(",") } } /** * “1-3,5,7”をList(1,2,3,5,7)に変換する(toSequenceStringの逆変換) */ def sequenceStringToList(str: String): Either[Throwable, List[Int]] = { def map2[E, A, B, C](e1: Either[E, A], e2: Either[E, B])(f: (A, B) => C): Either[E, C] = for { a <- e1 b <- e2 } yield f(a, b) str.split(",").map { s => s.split("-") match { case Array(from, to) => Try((from.toInt to to.toInt).toList).toEither case Array(numStr) => Try(List(numStr.toInt)).toEither case _ => Left(new IllegalArgumentException(s"パースできない文字列: $str")) } }.foldRight[Either[Throwable, List[Int]]](Right(List()))(map2(_, _)(_ ++ _)) } }
良いやり方を思い付いたら、また公開する。