假设我们有一个包含在文件Array.json中的一维javascript对象数组,其中的密钥模式是未知的,即在读取文件之前不知道密钥。
然后我们希望输出一个带有标题或第一个条目的CSV文件,该条目是来自所有对象的逗号分隔的一组键。
文件的每一行都应包含逗号分隔值,这些值对应于文件中的每个键。
Array.json
[
abc:123,
xy:"yz",
s12:13,
],
...
[
abc:1
s:133,
]
有效输出:
abc,xy,s12,s
123,yz,13,
1,,,133
我正在教自己'功能风格'编程,但我认为这个问题不适合功能性解决方案。 我相信这个问题需要为输出头保留一些状态,然后每行依赖于该头。
我想在一次通过中解决问题。我的目标是大数据集的效率,最小的遍历,以及可能的并行化。如果这是不可能的,那么你可以提供证据或推理来解释原因吗?
编辑:有没有办法在功能上解决这个问题?:假设您按特定顺序传递一次数组。然后 从头开始,标头集对于第一个看起来像
abc,xy,s12
宾语。使用CSV条目123,yz,13
。然后在下一个对象上添加一个 标头集的附加键,因此abc,xy,s12,s
将成为标头 并且CSV条目为1,,,133
。最后我们不需要 第二次传递数据集。我们可以追加额外的 逗号到结果集。这是我们接近单一的一种方式 通过....
是否有用于解决此类问题的功能工具(功能),我应该考虑什么? [通过功能工具我的意思是Monads,FlatMap,Filters等]。 另外,我应该考虑期货这样的事情吗?
目前我一直在尝试使用Java8来解决这个问题,但我对Scala等解决方案持开放态度。理想情况下,我能够确定Java8s的功能方法是否可以解决问题,因为这是我目前正在使用的语言英寸
答案 0 :(得分:0)
完全可以在函数式编程中使用状态。但是在函数式编程中不允许具有可变状态或变异状态。
功能编程主张创建新的更改状态,而不是改变状态。
因此,在程序中创建的读取和访问状态为Ok,直到并且除非您发生变异或副作用。
即将到来。
val list = List(List("abc" -> "123", "xy" -> "yz"), List("abc" -> "1"))
list.map { inner => inner.map { case (k, v) => k}}.flatten
list.map { inner => inner.map { case (k, v) => v}}.flatten
<强> REPL 强>
scala> val list = List(List("abc" -> "123", "xy" -> "yz"), List("abc" -> "1"))
list: List[List[(String, String)]] = List(List((abc,123), (xy,yz)), List((abc,1)))
scala> list.map { inner => inner.map { case (k, v) => k}}.flatten
res1: List[String] = List(abc, xy, abc)
scala> list.map { inner => inner.map { case (k, v) => v}}.flatten
res2: List[String] = List(123, yz, 1)
或使用flatMap而不是map并展平
val list = List(List("abc" -> "123", "xy" -> "yz"), List("abc" -> "1"))
list.flatMap { inner => inner.map { case (k, v) => k}}
list.flatMap { inner => inner.map { case (k, v) => v}}
答案 1 :(得分:0)
这可能是一种方法:
val arr = Array(Map("abc" -> 123, "xy" -> "yz", "s12" -> 13), Map("abc" -> 1, "s" -> 133))
val keys = arr.flatMap(_.keys).distinct // get the distinct keys for header
arr.map(x => keys.map(y => x.getOrElse(y,""))) // get an array of rows
答案 2 :(得分:0)
在函数式编程中,不允许使用可变状态。但不可改变的状态/价值观都很好。
假设您已将json文件读入值输入:List [Map [String,String]],以下代码将解决您的问题:
val input = List(Map("abc"->"123", "xy"->"yz" , "s12"->"13"), Map("abc"->"1", "s"->"33"))
val keys = input.map(_.keys).flatten.toSet
val keyvalues = input.map(kvs => keys.map(k => (k->kvs.getOrElse(k,""))).toMap)
val values = keyvalues.map(_.values)
val result = keys.mkString(",") + "\n" + values.map(_.mkString(",")).mkString("\n")
答案 3 :(得分:0)
由于csv输出会随着每一个新的输入行而改变,所以在写出之前必须将其保存在内存中。 如果您考虑从csv文件的内部表示创建输出文本格式 数据上的另一个“传递”(csv的内部表示实际上是{{1您必须遍历以将其转换为文本)然后单次传递无法执行此操作。
但是,如果这是可以接受的,那么您可以使用Map[String,List[String]]
从json文件中读取单个项目,将其合并到csv文件中,并执行此操作直到流为空。
假设csv文件的内部表示是
Stream
您可以将单个项目表示为
trait CsvFile {
def merge(line: Map[String, String]): CsvFile
}
您可以使用trait Item {
def asMap: Map[String, String]
}
:
foldLeft
或使用递归来获得相同的结果
def toCsv(items: Stream[Item]): CsvFile =
items.foldLeft(CsvFile(Map()))((csv, item) => csv.merge(item.asMap))
注意:当然你不必为CsvFile或Item创建类型,你可以分别使用Map [String,List [String]]和Map [String,String]
更新:
随着@tailrec def toCsv(items: Stream[Item], prevCsv: CsvFile): CsvFile =
items match {
case Stream.Empty => prevCsv
case item #:: rest =>
val newCsv = prevCsv.merge(item.asMap)
toCsv(rest, newCsv)
}
特征/类的更多细节请求,这是一个示例实现:
CsvFile