Вы выполняете операцию фильтрации (игнорируете, если не (B < 0)
) и отображаете (из каждой строки, получаете A и B/делаем что-то с A и B).
Вы могли бы написать это следующим образом:
val valueDF = fileDataFrame.select("colA", "colB")
val valueArr = valueDF.collect()
val result = valueArr.filter(_(1).toString().toDouble < 0).map{row => (row(0).toString(), row(1).toString().toDouble)}
// do something with result
Вы также можете сделать первое отображение, а затем фильтрация:
val result = valueArr.map{row => (row(0).toString(), row(1).toString().toDouble)}.filter(_._2 < 0)
Scala также предлагает более удобные варианты для такого рода операций (спасибо Саша Кольберг), названный withFilter
и collect
. withFilter
имеет преимущество перед filter
в том, что он не создает новую коллекцию, экономя вас за один проход, см. this answer для получения более подробной информации. С collect
вы также сопоставляете и фильтруете за один проход, передавая частичную функцию, которая позволяет выполнять сопоставление образцов, см., Например, это answer.
В вашем случае collect
будет выглядеть следующим образом:
val valueDF = fileDataFrame.select("colA", "colB")
val valueArr = valueDF.collect()
val result = valueArr.collect{
case row if row(1).toString().toDouble < 0) => (row(0).toString(), row(1).toString().toDouble)
}
// do something with result
(я думаю, что есть более элегантный способ выразить это, но что осталось в качестве упражнения;))
Кроме того, есть легкая нотация называемых «последовательными пониманиями». С этим можно было бы написать:
val result = for (row <- valueArr if row(1).toString().toDouble < 0) yield (row(0).toString(), row(1).toString().toDouble)
Или более гибкий вариант:
val result = for (row <- valueArr) yield {
val double = row(1).toString().toDouble
if (double < 0) {
(row(0).toString(), double)
}
}
В качестве альтернативы, вы можете использовать foldLeft
:
val valueDF = fileDataFrame.select("colA", "colB")
val valueArr = valueDF.collect()
val result = valueArr.foldLeft(Seq[(String, Double)]()) {(s, row) =>
val a = row(0).toString()
val b = row(1).toString().toDouble
if (b < 0){
s :+ (a, b) // append tuple with A and B to results sequence
} else {
s // let results sequence unmodified
}
}
// do something with result
Все они считаются функционально ... которые вы предпочитаете, по большей части, вопрос вкуса. Первые 2 примера (фильтр/карта, карта/фильтр) имеют недостаток производительности по сравнению с остальными, поскольку они повторяют последовательность в два раза.
Обратите внимание, что в FP очень важно минимизировать побочные эффекты/изолировать их от основной логики. I/O («написать A и B где-то») является побочным эффектом. Таким образом, вы обычно будете писать свои функции таким образом, чтобы у них не было побочных эффектов - только логика ввода -> вывода, не затрагивая или не извлекая данные из окружения. Как только у вас есть конечный результат, вы можете делать побочные эффекты. В этом конкретном случае, когда у вас есть result
(это последовательность из A
и B
кортежей), вы можете пропустить ее и распечатать. Таким образом, вы можете, например, легко изменить способ печати (вы можете распечатать на консоль, отправить в удаленное место и т. Д.), Не касаясь основной логики.
Также вы можете предпочесть неизменные значения (val
), где это возможно, что безопаснее. Даже в вашей петле, row
, A
и B
не изменяются, поэтому нет причин использовать var
.
(Btw, я скорректировал имена значений, начиная с нижнего регистра, см. conventions).
Если ValueArr является набором, он должен иметь функцию 'filter()', которая принимает функцию '(a, b) => Boolean' в качестве параметра и возвращает коллекцию только тех элементов, где возвращаемая функция правда. – thwiegan