Получить из списка подсписки, которые соответствуют предикату - PullRequest
3 голосов
/ 23 мая 2011

Я очень новичок в Scala, но из того, что я прочитал, кажется, что это идеальный язык для работы над проектом, над которым я работаю.

У меня очень большой CSV-файл, который выглядит следующим образом:

INDEX, CITY, COST
  7  ,  London, 500
  7  ,  Paris, 200
  11  ,  Rome, 300
  11  ,  New York, 100
  11  ,  Madrid, 7

Я хочу прочитать в CSV-файле и создать список всех элементов с одинаковым индексом, по одному индексу за раз.

Из приведенного выше примера я хотел бы получить список, содержащий строки:

7, London, 500
7, Paris, 200

И второй список, содержащий строки:

11, Rome, 300
11, New York, 100
11, Madrid, 7

Это довольно легко прочитать в файле CSV:

val iter = src.getLines().drop(1).map(_.split(",")) //from SO :) 

Однако я изо всех сил пытаюсь найти чистый способ создания моих подсписков. Мне кажется, что должен быть хороший, лаконичный способ добиться этого с помощью Scala. Мне бы особенно хотелось, чтобы данные загружались лениво, поскольку их много. Не могли бы вы подсказать, как мне этого добиться?

Все данные упорядочены по индексу (хотя индексы не последовательные), а файл CSV, с которым я работаю, не содержит вложенных запятых или экранированных символов.

Ответы [ 3 ]

3 голосов
/ 23 мая 2011

Source.getLines уже ленивый. Он возвращает итератор, который будет извлекать каждую строку из базового файла по требованию. Большинство операций с итераторами также возвращают итераторы, поэтому в следующем коде:

val iter = src.getLines.tail map {_ split ","}

Вы правильно назвали значение. Это будет Iterator[Array[String]], с каждым массивом String, создаваемым по требованию.

Сталкивались ли вы с какими-либо конкретными проблемами, которые могли бы предполагать, что данные не загружаются лениво?

ОБНОВЛЕНИЕ

Чтобы затем создать один из ваших подсписков из этого итератора, вы можете:

val id7 = iter filter {_(0) == 7)

Опять же, это все равно будет ленивым.

Или ... вы можете сгруппировать лот:

val grouped = iter.toStream groupBy {_(0)}

К сожалению, это не совсем лениво. Самая последняя строка может иметь уникальное значение в первом столбце, поэтому вам нужно прочитать каждый элемент из входных данных, чтобы узнать, сколько подразделений требуется. В REPL также проще форсировать подпотоки, чтобы вы могли видеть, что они содержат:

val grouped = iter.toStream groupBy {_(0)} mapValues {_.toList}
2 голосов
/ 23 мая 2011
scala> List(Array(1,"a"),Array(2,"b"),Array(1,"c")).groupBy(_(0))
res1: scala.collection.immutable.Map[Any,List[Array[Any]]] = Map(1 -> List(Array(1, a), Array(1, c)), 2 -> List(Array(2, b)))

Итак, вам нужно добавить .groupBy(_(0)) для группировки по первому элементу внутри массивов.

1 голос
/ 23 мая 2011

Когда у вас много данных, вы должны быть более осторожны в отношении того, какие операции вы хотите выполнить.

Предположим, что ваш файл такой большой , что вы не можете загрузить все это в память, и взамен вы готовы (вынуждены) прочитать его в ~ N раз, чтобы чтобы получить N различных подмножеств.

Во-первых, вы должны выяснить, сколько подмножеств вам понадобится. Давайте создадим что-то с притворным методом getLines:

val src = new { def getLines() = Iterator("#", "1,a", "2,b", "2,c") }

Теперь нам нужно найти все начальные индексы. Вы могли бы использовать split, но поскольку вы обрабатываете много данных и вам не нужно разбивать их на части, давайте просто найдем первую запятую (здесь при условии, что всегда есть запятая найдено):

val idx = Set() ++ src.getLines().drop(1).map(s => s.substring(0, s.indexOf(',')))

Хорошо, теперь мы знаем, что ищем. Затем мы проходим и получаем его с помощью класса, который поможет нам лениво загрузить данные:

class OneIndex(index: String) {
  lazy val data = src.getLines().drop(1).filter(
    s => index == s.substring(0,s.indexOf(','))
  ).toArray
}
val everything = idx.map(i => (i,new OneIndex(i))).toMap

scala> everything("2").data.foreach(println)
2,b
2,c

Есть еще много вещей, которые можно добавить - возможно, .toInt или .trim.toInt помогут в какой-то момент превратить значения индекса из строк в целые. Можно также задаться вопросом, действительно ли вы хотите ленивой загрузки, поскольку это заставляет вас читать весь файл несколько раз. Но это как минимум базовая структура.

...