Итерация по заполнению таблицы 2 HasSets функционально в Scala - PullRequest
0 голосов
/ 03 марта 2019

Я пишу приложение Scala и пытаюсь перебрать таблицу (в виде 2-мерного массива) функциональным способом.Для каждой строки в таблице я хочу заполнить набор всеми отличающимися значениями из первого столбца, а второй набор - всеми отличающимися значениями из второго столбца.

Я пробовал много подходов, но не смог найти какое-либо решение, каким образомсделать это в функциональном стиле.Поскольку я получаю 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) 

Есть ли способ, как я мог бы сделать это с неизменяемыми наборами, другими объектами или, может быть, синтаксис for-yield?

Спасибо за любые ответы или подсказки,

Феликс

Ответы [ 2 ]

0 голосов
/ 03 марта 2019

Может быть, просто транспонировать сработает для вас?

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)
0 голосов
/ 03 марта 2019

Каждый раз, когда вы пытаетесь выполнить 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 является хорошей общей отправной точкой, которая позволяет довольно простые переводы с изменяемогов чисто функциональный код.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...