Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/list/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Scala 通过维护订单聚合重复记录,还包括重复记录_Scala_List_Time Complexity_Aggregate Functions - Fatal编程技术网

Scala 通过维护订单聚合重复记录,还包括重复记录

Scala 通过维护订单聚合重复记录,还包括重复记录,scala,list,time-complexity,aggregate-functions,Scala,List,Time Complexity,Aggregate Functions,我正在尝试解决一个有趣的问题,只需对聚合(如总和、计数等)执行groupBy很容易,但这个问题略有不同。让我解释一下: 这是我的元组列表: val repeatSmokers: List[(String, String, String, String, String, String)] = List( ("ID76182", "sachin", "kita MR.", "56308", "1990", "300"), ("ID76182", "KOUN", "Jana MR.

我正在尝试解决一个有趣的问题,只需对聚合(如总和、计数等)执行groupBy很容易,但这个问题略有不同。让我解释一下:

这是我的元组列表:

val repeatSmokers: List[(String, String, String, String, String, String)] =
  List(
    ("ID76182", "sachin", "kita MR.", "56308", "1990", "300"),
    ("ID76182", "KOUN", "Jana MR.", "56714", "1990", "100"),
    ("ID76182", "GANGS", "SKILL", "27539", "1990", "255"),
    ("ID76182", "GANGS", "SKILL", "27539", "1990", "110"),
    ("ID76182", "SEMI", "GAUTAM A MR.", "45873", "1990", "20"),
    ("ID76182", "SEMI", "GAUTAM A MR.", "45873", "1990", "6750"),
    ("ID76182", "DOWNES", "RYAN", "47542", "1990", "2090"),
    ("ID76182", "DRAGON", "WARS", "49337", "1990", "200"),
    ("ID76182", "HULK", "PAIN MR.", "47542", "1990", "280"),
    ("ID76182", "JAMES", "JIM", "30548", "1990", "300"),
    ("ID76182", "KIMMELSHUE", "RUTH", "55345", "1990", "2600"),
    ("ID76182", "DRAGON", "WARS", "49337", "1990", "370"),
    ("ID76182", "COOPER", "ANADA", "45873", "1990", "2600"),
    ("ID76182", "SEMI", "GAUTAM A MR.", "45873", "1990", "2600"),
    ("ID76182", "HULK", "PAIN MR.", "47542", "1990", "256")
  )
.map(recordTuple => Record.tupled(recordTuple))
这些记录的模式是
(Idnumber、name、test\u code、year、amount)
。从这些元素中,我只需要重复记录,我们在上面的列表中定义唯一组合的方式是采用
(sachin,kita先生,56308)
名称和测试代码组合。这意味着如果相同的名字和测试代码重复,这是一个重复吸烟记录。为了简单起见,您可以假设只有test_代码是唯一值,如果它重复,您可以说它是一个重复的吸烟者记录

以下是确切的输出:

最后,这里最具挑战性的部分是维持每秒钟重复吸烟者记录的顺序和总和,并添加发生次数

例如:此记录架构为:ID76182475421990536,2

ID号、测试代码、年份、金额、发生次数

因为它发生了两次,我们看到上面的2

注:


输出可以是任何集合的列表,但它的格式应与我上面提到的格式相同,因此这里有一些Scala中的代码,但实际上是用Scala编写的Java代码:

import java.util.ArrayList
导入java.util.LinkedHashMap
导入scala.collection.convert_
类型RawRecord=(String,String,String,String,String,String)
类型记录=(字符串,字符串,字符串,字符串,Int,Int)
类型RecordKey=(字符串,字符串,字符串,字符串)
类型输出=(字符串,字符串,字符串,字符串,Int,Int,Int)
val keyF:Record=>RecordKey=r=>(r._1,r._2,r._3,r._4)
val repeatSmokersRaw:列表[RawRecord]=
名单(
(“ID76182”、“sachin”、“kita先生”、“56308”、“1990”、“300”),
(“ID76182”、“KOUN”、“Jana先生”、“56714”、“1990”、“100”),
(“ID76182”、“帮派”、“技能”、“27539”、“1990”、“255”),
(“ID76182”、“帮派”、“技能”、“27539”、“1990”、“110”),
(“ID76182”、“SEMI”、“GAUTAM A先生”、“45873”、“1990”、“20”),
(“ID76182”、“SEMI”、“GAUTAM A先生”、“45873”、“1990”、“6750”),
(“ID76182”、“唐斯”、“瑞安”、“47542”、“1990”、“2090”),
(“ID76182”、“龙”、“战争”、“49337”、“1990”、“200”),
(“ID76182”、“绿巨人”、“痛苦先生”、“47542”、“1990”、“280”),
(“ID76182”、“詹姆斯”、“吉姆”、“30548”、“1990”、“300”),
(“ID76182”、“KIMMELSHUE”、“RUTH”、“55345”、“1990”、“2600”),
(“ID76182”、“龙”、“战争”、“49337”、“1990”、“370”),
(“ID76182”、“库珀”、“阿纳达”、“45873”、“1990”、“2600”),
(“ID76182”、“SEMI”、“GAUTAM A先生”、“45873”、“1990”、“2600”),
(“ID76182”,“绿巨人”,“痛苦先生”,“47542”,“1990”,“256”)
)
val repeatSmokers=repeatSmokersRaw.map(r=>(r._1,r._2,r._3,r._4,r._5.toInt,r._6.toInt))
val acc=new LinkedHashMap[RecordKey,(util.ArrayList[Output],Int,Int]
repeat.foreach(r=>{
val键=键F(r)
var cur=附件get(键)
如果(cur==null){
cur=(新的ArrayList[Output](),0,0)
}
val nextCnt=cur._2+1
val sum=当前值3+r.\U 6
val输出=(r._1,r._2,r._3,r._4,r._5,和,下一个)
电流_1.添加(输出)
acc.put(键,(当前1,下一个,总和))
})
val result=acc.values().asScala.filter(p=>p.\u 2>1).flatMap(p=>p.\u 1.asScala)
//或者,如果您很聪明,您可以将过滤器和平面图合并为
//val result=acc.values().asScala.flatMap(p=>if(p.\U 1.size>1)p.\U 1.asScala else Nil)
println(result.mkString(“\n”))
它打印

(ID76182,帮派,技能,275391990255,1)
(ID76182,帮派,技能,275391990365,2)
(ID76182,SEMI,GAUTAM A先生,458731990,20,1)
(ID76182,SEMI,GAUTAM A先生,4587319906770,2)
(ID76182,SEMI,GAUTAM A先生,4587319909370,3)
(ID76182,龙,战争,493371990200,1)
(ID76182,龙,战争,493371990570,2)
(ID76182,HULK,PAIN先生,475421990280,1)
(ID76182,HULK,PAIN先生,475421990536,2)


这段代码中的主要技巧是使用Java作为累加器集合,因为它保留了插入顺序。另外一个技巧是在内部存储一些列表(因为我使用Java集合,所以我决定对内部累加器使用
ArrayList
,但您可以使用任何您喜欢的东西)。因此,我们的想法是构建一个key=>吸烟者列表的映射,另外为每个key存储当前计数器和当前总和,以便可以将“聚合”吸烟者添加到列表中。构建映射时,通过它过滤掉那些没有累积至少2条记录的键,然后将列表映射转换为单个列表(这是使用
LinkedHashMap
非常重要的一点,因为插入顺序在迭代过程中保持不变)以下是解决此问题的一种实用方法:

对于此输入:

val repeatSmokers: List[(String, String, String, String, String, String)] =
  List(
    ("ID76182", "sachin", "kita MR.", "56308", "1990", "300"),
    ("ID76182", "KOUN", "Jana MR.", "56714", "1990", "100"),
    ("ID76182", "GANGS", "SKILL", "27539", "1990", "255"),
    ("ID76182", "GANGS", "SKILL", "27539", "1990", "110"),
    ("ID76182", "SEMI", "GAUTAM A MR.", "45873", "1990", "20"),
    ("ID76182", "SEMI", "GAUTAM A MR.", "45873", "1990", "6750"),
    ("ID76182", "DOWNES", "RYAN", "47542", "1990", "2090"),
    ("ID76182", "DRAGON", "WARS", "49337", "1990", "200"),
    ("ID76182", "HULK", "PAIN MR.", "47542", "1990", "280"),
    ("ID76182", "JAMES", "JIM", "30548", "1990", "300"),
    ("ID76182", "KIMMELSHUE", "RUTH", "55345", "1990", "2600"),
    ("ID76182", "DRAGON", "WARS", "49337", "1990", "370"),
    ("ID76182", "COOPER", "ANADA", "45873", "1990", "2600"),
    ("ID76182", "SEMI", "GAUTAM A MR.", "45873", "1990", "2600"),
    ("ID76182", "HULK", "PAIN MR.", "47542", "1990", "256")
  )
val repeatSmokers: List[(String, String, String, String, String, Int)] =
  List(
    ("ID76182", "sachin", "kita MR.", "56308", "1990", 300),
    ("ID76182", "KOUN", "Jana MR.", "56714", "1990", 100),
    ("ID76182", "GANGS", "SKILL", "27539", "1990", 255),
    ("ID76182", "GANGS", "SKILL", "27539", "1990", 110),
    ("ID76182", "SEMI", "GAUTAM A MR.", "45873", "1990", 20),
    ("ID76182", "SEMI", "GAUTAM A MR.", "45873", "1990", 6750),
    ("ID76182", "DOWNES", "RYAN", "47542", "1990", 2090),
    ("ID76182", "DRAGON", "WARS", "49337", "1990", 200),
    ("ID76182", "HULK", "PAIN MR.", "47542", "1990", 280),
    ("ID76182", "JAMES", "JIM", "30548", "1990", 300),
    ("ID76182", "KIMMELSHUE", "RUTH", "55345", "1990", 2600),
    ("ID76182", "DRAGON", "WARS", "49337", "1990", 370),
    ("ID76182", "COOPER", "ANADA", "45873", "1990", 2600),
    ("ID76182", "SEMI", "GAUTAM A MR.", "45873", "1990", 2600),
    ("ID76182", "HULK", "PAIN MR.", "47542", "1990", 256)
  )
使用表示记录的case类:

case class Record(
    id: String,
    fname: String,
    lname: String,
    code: String,
    year: String,
    amount: String)
我们可以运行以下程序:

val result = repeatSmokers
  .map(recordTuple => Record.tupled(recordTuple))
  .zipWithIndex
  .groupBy { case (record, order) => (record.fname, record.lname, record.code) }
  .flatMap {

    case (_, List(singleRecord)) => Nil // get rid of non-repeat records

    case (key, records) => {

      val firstKeyIdx = records.head._2

      val amounts = records.map {
        case (record, order) => record.amount.toInt
      }.foldLeft(List[Int]()) {
        case (Nil, addAmount) => List(addAmount)
        case (previousAmounts :+ lastAmount, addAmount) =>
          previousAmounts :+ lastAmount :+ (lastAmount + addAmount)
      }

      records
        .zip(amounts)
        .zipWithIndex
        .map {
          case (((rec, order), amount), idx) =>
            val serializedRecord =
              List(rec.id, rec.code, rec.year, amount, idx + 1)
            (serializedRecord.mkString(","), (firstKeyIdx, idx))
        }
    }
  }
  .toList
  .sortBy { case (serializedRecord, finalOrder) => finalOrder }
  .map { case (serializedRecord, finalOrder) => serializedRecord }
这将产生:

ID76182,27539,1990,255,1
ID76182,27539,1990,365,2
ID76182,45873,1990,20,1
ID76182,45873,1990,6770,2
ID76182,45873,1990,9370,3
ID76182,49337,1990,200,1
ID76182,49337,1990,570,2
ID76182,47542,1990,280,1
ID76182,47542,1990,536,2
一些解释:

从元组实例化case类的一种非常好的方法(从元组列表创建记录列表):

每个记录都使用其全局索引(记录,索引)进行元组化,以便后者能够处理订单:

.zipWithIndex
然后,我们使用您需要的密钥分组:

.groupBy { case (record, order) => (record.fname, record.lname, record.code) }
然后,对于分组阶段产生的每个键/值,我们将输出一个记录列表(如果值是单个记录,则输出一个空列表)。因此,平面图可以将要生成的列表展平

以下是摆脱单一记录的部分:

case (_, List(singleRecord)) => Nil
另一种情况涉及累积金额的创建(这是一个Int列表)(Spark开发人员注意:groupBy保留给定键中值元素的顺序):

这些金额压缩回记录,以便使用给定的累计金额修改每个记录金额。同时,也可以将记录序列化为最终所需的格式:

records
    .zip(amounts)
    .zipWithIndex
    .map {
      case (((rec, order), amount), idx) =>
        val serializedRecord =
          List(rec.id, rec.code, rec.year, amount, idx + 1).mkString(
            ",")
        (serializedRecord, (firstKeyIdx, idx))
    }
上一部分还压缩了记录及其索引。事实上,每个序列化记录都提供了一个元组(firstKeyIdx,
records
    .zip(amounts)
    .zipWithIndex
    .map {
      case (((rec, order), amount), idx) =>
        val serializedRecord =
          List(rec.id, rec.code, rec.year, amount, idx + 1).mkString(
            ",")
        (serializedRecord, (firstKeyIdx, idx))
    }
.sortBy { case (serializedRecord, finalOrder) => finalOrder }
val repeatSmokers: List[(String, String, String, String, String, Int)] =
  List(
    ("ID76182", "sachin", "kita MR.", "56308", "1990", 300),
    ("ID76182", "KOUN", "Jana MR.", "56714", "1990", 100),
    ("ID76182", "GANGS", "SKILL", "27539", "1990", 255),
    ("ID76182", "GANGS", "SKILL", "27539", "1990", 110),
    ("ID76182", "SEMI", "GAUTAM A MR.", "45873", "1990", 20),
    ("ID76182", "SEMI", "GAUTAM A MR.", "45873", "1990", 6750),
    ("ID76182", "DOWNES", "RYAN", "47542", "1990", 2090),
    ("ID76182", "DRAGON", "WARS", "49337", "1990", 200),
    ("ID76182", "HULK", "PAIN MR.", "47542", "1990", 280),
    ("ID76182", "JAMES", "JIM", "30548", "1990", 300),
    ("ID76182", "KIMMELSHUE", "RUTH", "55345", "1990", 2600),
    ("ID76182", "DRAGON", "WARS", "49337", "1990", 370),
    ("ID76182", "COOPER", "ANADA", "45873", "1990", 2600),
    ("ID76182", "SEMI", "GAUTAM A MR.", "45873", "1990", 2600),
    ("ID76182", "HULK", "PAIN MR.", "47542", "1990", 256)
  )
case class Record(
  id: String, fname: String, lname: String,
  code: String, year: String, var amount: Int
)

case class Key(fname: String, lname: String, code: String)

val preparedRecords: List[(Key, Record)] = repeatSmokers.map {
  case recordTuple @ (_, fname, lname, code, _, _) =>
    (Key(fname, lname, code), Record.tupled(recordTuple))
}
import scala.collection.mutable.LinkedHashMap

def aggregateDuplicatesWithOrder(
    remainingRecords: List[(Key, Record)],
    processedRecords: LinkedHashMap[Key, List[Record]]
): LinkedHashMap[Key, List[Record]] =
  remainingRecords match {

    case (key, record) :: newRemainingRecords => {

      processedRecords.get(key) match {
        case Some(recordList :+ lastRecord) => {
          record.amount = record.amount + lastRecord.amount
          processedRecords.update(key, recordList :+ lastRecord :+ record)
        }
        case None => processedRecords(key) = List(record)
      }

      aggregateDuplicatesWithOrder(newRemainingRecords, processedRecords)
    }

    case Nil => processedRecords
  }

val result = aggregateDuplicatesWithOrder(
  preparedRecords, LinkedHashMap[Key, List[Record]]()
).values.flatMap {
  case _ :: Nil => Nil
  case records =>
    records.zipWithIndex.map { case (rec, idx) =>
      List(rec.id, rec.code, rec.year, rec.amount, idx + 1).mkString(",")
    }
}