在Scala中以功能性方式遍历填充2个HasSet的表

时间:2019-03-03 16:00:24

标签: scala functional-programming

我正在编写一个Scala应用,并尝试以一种功能性的方式遍历一个表(以二维数组的形式)。对于表中的每一行,我想用第一列的所有不同值填充Set,并使用第二列的所有不同值填充第二Set。

我尝试了许多方法,但是找不到如何以功能样式进行操作的解决方案。因为我从迭代中获得了2个新变量,所以如果没有非功能性的帮助,这似乎是不可能的。

这是一个非功能性的示例,其中包含一个包含产品和客户的表的可变HashSets:

val myInputTable =
  Array(Array("Product A","Customer 1"), Array("Product B","Customer 1"),
    Array("Product C","Customer 2"), Array("Product A","Customer 2"))

val productSet = new collection.mutable.HashSet[String]
val customerSet = new collection.mutable.HashSet[String]

for(
  inputLine <- myInputTable;
  inputElement <- inputLine
) {
  if (inputLine.indexOf(inputElement) == 0) {
    productSet.add(inputElement)
  } else {
    customerSet.add(inputElement)
  }
}

println("Product Set:")
productSet.foreach(println)
println("\nCustomer Set:")
customerSet.foreach(println) 

有没有办法使用不可变的Sets,其他对象或for-yield语法来做到这一点?

感谢您的任何回答或提示,

费利克斯

2 个答案:

答案 0 :(得分:4)

每当您发现自己尝试在更新某些可变状态时尝试对序列中迭代的代码进行FP验证时,一个好的第一种方法就是使用foldLeft

val myInputTable =
  Array(Array("Product A","Customer 1"), Array("Product B","Customer 1"),
    Array("Product C","Customer 2"), Array("Product A","Customer 2"))

val (products, customers) =
  myInputTable.foldLeft((Set.empty[String], Set.empty[String])) {
    case ((ps, cs), Array(p, c)) => (ps + p, cs + c)
    case ((ps, cs), _) => (ps, cs) // Alternatively fail here.
  }

foldLeft的第一个参数是初始状态。我们想使用两个不可变的集合,因此我们使用Set.empty[String]的元组。 foldLeft的下一个参数是一个函数:

  {
    case ((ps, cs), Array(p, c)) => (ps + p, cs + c)
    case ((ps, cs), _) => (ps, cs) // Alternatively fail here.
  }

这应该是从当前累积状态(ps, cs)和每个元素Array(p, c)到下一个状态的函数。它将从左到右(因此foldLeft)应用于集合中的每个函数,从而累积状态更改,并将返回状态的最终值。它是这样的:

scala> val (products, customers) =
     |   myInputTable.foldLeft((Set.empty[String], Set.empty[String])) {
     |     case ((ps, cs), Array(p, c)) => (ps + p, cs + c)
     |     case ((ps, cs), _) => (ps, cs) // Alternatively fail here.
     |   }
products: scala.collection.immutable.Set[String] = Set(Product A, Product B, Product C)
customers: scala.collection.immutable.Set[String] = Set(Customer 1, Customer 2)

在某些情况下,可能会有更特定的组合器使您可以更简洁地表达操作,但是foldLeft是一个很好的通用起点,它允许从可变代码到纯函数代码的相当直接的转换。

答案 1 :(得分:2)

也许transpose会为您做一份工作?

val table = Array(
      Array("Product A","Customer 1"),
      Array("Product B","Customer 1"), 
      Array("Product C","Customer 2"),
      Array("Product A","Customer 2")
)

val Array(productSet, customerSet) = table.transpose.map(_.toSet)

productSet //Set(Product A, Product B, Product C)
customerSet //Set(Customer 1, Customer 2)