我有一组键控记录,其中包含book id和reader id字段。
case class Book(book: Int, reader: Int)
如何使用aggregateByKey
将具有相同密钥的所有记录合并为以下格式的一条记录:
(key:Int, (books: List:[Int], readers: List:[Int]))
书籍是所有书籍的列表,读者是具有给定键的记录中所有读者的列表?
我的代码(如下)导致编译错误:
import org.apache.log4j.{Level, Logger}
import org.apache.spark.{SparkContext, SparkConf}
object Aggr {
case class Book(book: Int, reader: Int)
val bookArray = Array(
(2,Book(book = 1, reader = 700)),
(3,Book(book = 2, reader = 710)),
(4,Book(book = 3, reader = 710)),
(2,Book(book = 8, reader = 710)),
(3,Book(book = 1, reader = 720)),
(4,Book(book = 2, reader = 720)),
(4,Book(book = 8, reader = 720)),
(3,Book(book = 3, reader = 730)),
(4,Book(book = 8, reader = 740))
)
def main(args: Array[String]) {
Logger.getLogger("org.apache.spark").setLevel(Level.WARN)
Logger.getLogger("org.eclipse.jetty.server").setLevel(Level.OFF)
// set up environment
val conf = new SparkConf()
.setMaster("local[5]")
.setAppName("Aggr")
.set("spark.executor.memory", "2g")
val sc = new SparkContext(conf)
val books = sc.parallelize(bookArray)
val aggr = books.aggregateByKey((List()[Int], List()[Int]))
({case
((bookList:List[Int],readerList:List[Int]), Book(book, reader)) =>
(bookList ++ List(book), readerList ++ List(reader))
},
{case ((bookLst1:List[Int], readerLst1:List[Int]),
(bookLst2:List[Int], readerLst2:List[Int])
) => (bookLst1 ++ bookLst2, readerLst1 ++ readerLst2) })
}
}
错误:
Error:(36, 44) object Nil does not take type parameters.
val aggr = books.aggregateByKey((List()[Int], List()[Int]))
Error:(37, 6) missing parameter type for expanded function The argument types of an anonymous function must be fully known. (SLS 8.5) Expected type was: ?
({case
^
^
更新
使用(List(0), List(0)
初始化accumalator时,所有内容都会编译,但会在结果中插入额外的零。非常有趣:
val aggr : RDD[(Int, (List[Int], List[Int]))] = books.aggregateByKey((List(0), List(0))) (
{case
((bookList:List[Int],readerList:List[Int]), Book(book, reader)) =>
(bookList ++ List(book), readerList ++ List(reader))
},
{case ((bookLst1:List[Int], readerLst1:List[Int]),
(bookLst2:List[Int], readerLst2:List[Int])
) => (bookLst1 ++ bookLst2, readerLst1 ++ readerLst2) }
)
这导致以下输出:
[Stage 0:> (0 + 0) / 5](2,(List(0, 1, 0, 8),List(0, 700, 0, 710)))
(3,(List(0, 2, 0, 1, 0, 3),List(0, 710, 0, 720, 0, 730)))
(4,(List(0, 3, 0, 2, 8, 0, 8),List(0, 710, 0, 720, 720, 0, 740)))
假设我可以将空列表作为初始化列表而不是带有零的列表,我当然不会有额外的零,列表会很好地连接。
有人可以解释一下为什么空列表初始值设定项(List(), List()
导致错误并且(List(0), List(0)
编译。它是Scala错误还是一个功能?
答案 0 :(得分:2)
实际上你做的一切都很好,只是你的缩进/语法风格有点草率,你只需要从中移动一个括号:
val aggr = books.aggregateByKey((List()[Int], List()[Int]))
({case
进入这个:
val aggr = books.aggregateByKey((List[Int](), List[Int]())) (
{case
这些链接可能会说明为什么这对您没有用处:
What are the precise rules for when you can omit parenthesis, dots, braces, = (functions), etc.?(第一个答案)
http://docs.scala-lang.org/style/method-invocation.html#suffix-notation
答案 1 :(得分:0)
回答您的更新 - 您错放了列表的类型声明。如果您将它们声明为List[Int]()
而不是List()[Int]
,那么一切都会奏效。编译器错误消息正确地告诉您问题,但它并不容易理解。通过将[Int]
放在最后,您将类型参数传递给List()
函数的结果。 List()
的结果是Nil
- 表示空列表的单个对象 - 它不接受类型参数。
至于为什么List(0)
也有效 - 如果可以的话,scala会执行类型推断。您已经声明了列表中的一个元素 - 即0,一个整数,因此它推断出这是一个List[Int]
。但请注意,这不会声明一个空列表,而是一个单个零的列表。您可能希望改为使用List[Int]()
。
仅使用List()
无法正常工作,因为scala无法推断出空列表的类型。