我在C
中有一系列元组,这是用户1的活动日志
scala> C.collect.foreach(println)
((1,A,1),1)
((1,B,2),1)
((1,C,4),2)
((1,D,7),3)
((1,E,15),8)
((1,F,16),1)
第一个元组中的第三个条目(1,2,4,7,15,16)是时间戳,第二个条目(1,1,2,3,8,1)是连续时间戳之间的差异。
我试图在第一次启动某个操作时或在一段时间TIMEOUT
之后启动操作时创建一个会话。
我的计划是首先将ID
分配给每个元组,然后将它们映射成对。 ID
s将是它所属的会话中的第一个时间戳。
例如,如果TIMEOUT = 2
,示例将映射到
(1, (1,"A",1))
(1, (1,"B",2))
(4, (1,"C",4)) //creation of a new session with ID 4
(7, (1,"D",7)) //creation of a new session with ID 7
(15, (1,"E",15)) //creation of a new session with ID 15
(15, (1,"F",16))
然后我将按会话处理数据。
但是,我在这种映射方面遇到了困难。
我需要保留某种全局变量来跟踪TIMEOUT
中的最后一个时间戳,并在创建新会话时更新此变量,并将其设为ID
随后的条目。
因为这是Spark
,我使用Accumulator
accum
就像一个全局变量。
如果时间戳差异> = 2,我不确定如何设置accum
的值,然后将新值用作新会话的ID
。如果时间戳差异< 2,会话的ID
保持不变。
到目前为止我的尝试是
val accum = sc.accumulator(0, "My Accumulator")
C.map(x => (x._2 match {
case _ if (x._2 > -2) => accum.setValue(x._1._3); accum.value
case _ => accum.value
}, x._1)).collect
然后失败了。
我想这是因为accum.setValue()
是一个带有副作用的语句,而不是一个值,scala
中不允许这样做。此外,对象的变异在scala
中不受欢迎。我也知道语法错了。但是,我想不出有任何其他方法可以做到这一点。
如何实现此映射?谢谢。
答案 0 :(得分:0)
问题不在于副作用。 Scala中允许使用副作用。在功能代码中不鼓励他们。问题只是你需要将函数体放入{},如果你希望它有多个语句。同样使用匹配只有if是没有意义的。我还假设你想要条件&gt; = 2 not&gt; -2,至少这适合你的例子。
所以这应该有效:
val accum = sc.accumulator(0, "My Accumulator")
C.map(x =>
(if (x._2 >= 2) {
accum.setValue(x._1._3)
accum.value
} else accum.value,
x._1)
).collect
唯一的问题是第一个ID,因为在您检测到第一个超时之前,您的ID将为0。但是你的例子并没有真正解释你如何处理这种边缘情况。
然而,我不会使用副作用来解决这个问题。序列上有一个scanLeft方法,允许您在访问前一个值时进行转换:
val list = List(
((1,"A",1),1),
((1,"B",2),1),
((1,"C",4),2),
((1,"D",7),3),
((1,"E",15),8),
((1,"F",16),1))
list.tail.scanLeft((list.head._1._1, list.head._1)){
case ((id, _), ((a, b, id2), delta)) =>
if(delta < 2) (id, (a,b,id2))
else (id2, (a,b,id2))
}
这也解决了第一个id的问题,因为明确指定了第一个元素。这显然假设您的序列中至少有一个元素。