我有一个原始的C#LINQ查询:
var rez = planes
.Where(b => (b.FlDate >= dateFrom) && (b.FlDate <= dateTo))
.GroupBy(i => i.Destination)
.Select(g => new { Destination = g.Key, Count = g.Count() })
.ToList();
并重新编写带有问题的Scala代码:
var rez = planes
.filter( _.FlDate.getMillis >= dateFrom.getMillis)
.filter(_.FlDate.getMillis <= dateTo.getMillis)
.groupBy(_.Destination)
.flatMap( new { Destination:String = _.Key, Count = _.Count() })//issue is here
所以主要任务是创建具有Destination
和Count
属性的匿名对象数组
Scala PlanesLogRow类来源:
import com.github.nscala_time.time.Imports._
class PlanesLogRow {
var FlDate:DateTime = new DateTime
var Origin = ""
var Destination = ""
}
答案 0 :(得分:4)
Scala中没有C#comaprable anonmyous类(虽然它们确实存在,但它们对IMO的作用却远没那么大)。或者,您可以使用具有良好语法糖的Tuple2[String, Int]
((String, Int)
)。
为了方便起见,我们可以创建case class
的{{1}},它为我们提供了一种创建类的简洁方法(注意所有值都是不可变的):
PlanesLogRow
现在让我们创建一个序列:
case class PlanesLogRow(flightDate: ZonedDateTime, origin: String, destination: String)
从和到日期(为方便起见,我使用了val flights = Seq(
PlanesLogRow(ZonedDateTime.now(), "US", "Kiev"),
PlanesLogRow(ZonedDateTime.now().plusHours(2L), "US", "Prague"),
PlanesLogRow(ZonedDateTime.now().plusHours(4L), "Canada", "Prague")
)
):
java.time
现在,我们可以过滤一次(不需要对集合进行两次传递),分组,然后将结果输出到val dateFrom = ZonedDateTime.now
val dateTo = ZonedDateTime.now().plusHours(5L)
:
Map[String, Int]
收率:
val result: Map[String, Int] =
flights
.filter(logRow => logRow.flightDate.isAfter(dateFrom) && logRow.flightDate.isBefore(dateTo))
.groupBy(_.destination)
.map { case (location, logRows) => (location, logRows.length) }
在Scala中,与C#中的LINQ不同,集合上的组合是严格的,而不是懒惰的。这意味着对于您调用的每个组合子(Map(Prague -> 2)
,map
等),将会分配一个新的集合。要解决这个问题,您可以使用生成惰性集合的views:
filter
除此之外,scala字段的命名约定是camelCase,而不是像C#那样的PascalCase。
答案 1 :(得分:1)
作为@Yuval Itzchakov的优秀答案的补充,如果你真的想要AnyRef{val destination: String; val count: Int}
而不是Tuple2[String, Int]
,你可以替换它的最后map
与...:
.map {
case (location, logRows) => new {
val destination = location;
val count = logRows.length
}
}
注意:正如@pedrofurla正确指出的那样,此解决方案强制运行时使用反射。