2014-02-19 2 views
0

Читаю файл TSV шаблон и использовать используя что-то вроде этого:фильтрация внутри `for` с соответствующими

case class Entry(entryType: Int, value: Int) 

def filterEntries(): Iterator[Entry] = { 
    for { 
    line <- scala.io.Source.fromFile("filename").getLines() 
    } yield new Entry(line.split("\t").map(x => x.toInt)) 
} 

Теперь я оба заинтересованы в фильтрации записей, чьи entryType установлены на 0 и пренебрегая линии с количество столбцов больше или меньше 2 (это не соответствует конструктору). Мне было интересно, есть ли идиоматический способ достичь этого, может быть использование сопоставления шаблонов и метода unapply в сопутствующем объекте. Единственное, о чем я могу думать, это использовать .filter на результирующем итераторе.

Я также принимаю решение, не связанное с циклом for, но которое возвращает Iterator[Entry]. Эти решения должны быть толерантными к искаженным входным данным.

+0

Вы должны опубликовать свое решение в качестве ответа на свой вопрос. – gzm0

+0

Выполнено. В любом случае спасибо за вашу поддержку, которую очень ценят :) – nopper

ответ

2

Это больше состоянии артистических:

package object liner { 
    implicit class R(val sc: StringContext) { 
    object r { 
     def unapplySeq(s: String): Option[Seq[String]] = sc.parts.mkString.r unapplySeq s 
    } 
    } 
} 

package liner { 

    case class Entry(entryType: Int, value: Int) 

    object I { 
    def unapply(s: String): Option[Int] = util.Try(s.toInt).toOption 
    } 

    object Test extends App { 
    def lines = List("1 2", "3", "", " 4 5 ", "junk", "0, 100000", "6 7 8") 

    def entries = lines flatMap { 
     case r"""\s*${I(i)}(\d+)\s+${I(j)}(\d+)\s*""" if i != 0 => Some(Entry(i, j)) 
     case __________________________________________________ => None 
    } 
    Console println entries 
    } 
} 

Будем надеяться, что регулярное выражение Интерполятор сделает его в стандартный дистрибутив в ближайшее время, но это показывает, как легко подстроить. Также, надеюсь, интерполятор типа scanf позволит легко извлекать case f"$i%d".

Я только начал использовать «удлиненный шаблон» в шаблонах, чтобы выровнять стрелки.

Существует куколочный или, может быть, личиночный регулярное выражение макрос:

https://github.com/som-snytt/regextractor

+0

Это только 2.11, правильно? Нет совпадения в строковых интерполяторах в 2.10 IIRC. – gzm0

+0

Прошу прощения, но я действительно не согласен с тем, что это хорошая идея: лайнер не выполняет проверку времени компиляции, что аргументная строка имеет смысл (нет подкатегории и т. Д.). Также обратите внимание, что '. *' Должно быть, вероятно, '. *?', И даже тогда у вас возникнет проблема с тем, что вы поймаете строки вроде «10 10 10». Вероятно, это не то, что хотел OP. – gzm0

+0

@ gzm0 Это 2.10 кошер. –

0

Вы можете создавать переменные в голове для-понимания, а затем использовать караул:

редактирования: обеспечить длину массива

for { 
    line <- scala.io.Source.fromFile("filename").getLines() 
    arr = line.split("\t").map(x => x.toInt) 
    if arr.size == 2 && arr(0) != 0 
} yield new Entry(arr(0), arr(1)) 
+0

Да, вы правы. Вероятно, я неправильно написал демо-код. К сожалению, дело в том, что входной файл может содержать различное количество столбцов, которое не соответствует конструктору Entry. – nopper

+0

Я обновил свой ответ. – drexin

0

Левая из <- или = в for-loop может быть полноценным шаблоном. Таким образом, вы можете написать следующее:

def filterEntries(): Iterator[Int] = for { 
    line <- scala.io.Source.fromFile("filename").getLines() 
    arr = line.split("\t").map(x => x.toInt) 
    if arr.size == 2 
    // now you may use pattern matching to extract the array 
    Array(entryType, value) = arr 
    if entryType == 0 
} yield Entry(entryType, value) 

Обратите внимание, что это решение будет бросать NumberFormatException, если поле не раскладывается в Int. Если вы этого не хотите, вам нужно будет инкапсулировать x.toInt с Try и совпадением шаблонов.

+0

Это не фильтрует, а бросает 'MatchError', когда' entryType' не равен 0 или строка не имеет ровно 2 записи. – drexin

+0

Ouch ... Будет обновляться. Спасибо – gzm0

+0

, если entryType! = 0, правильно? У вас нет модульного теста, не так ли. Риторический вопрос. (Извините, просто дразня. Не намеревайтесь препятствовать помощи в SO.) –

0

Я решил его, используя следующий код:

import scala.util.{Try, Success} 

val lines = List(
    "1\t2", 
    "1\t", 
    "2", 
    "hello", 
    "1\t3" 
) 

case class Entry(val entryType: Int, val value: Int) 
object Entry { 
    def unapply(line: String) = { 
    line.split("\t").map(x => Try(x.toInt)) match { 
     case Array(Success(entryType: Int), Success(value: Int)) => Some(Entry(entryType, value)) 
     case _ => 
     println("Malformed line: " + line) 
     None 
    } 
    } 
} 

for { 
    line <- lines 
    entryOption = Entry.unapply(line) 
    if entryOption.isDefined 
} yield entryOption.get 
Смежные вопросы