Работает ли итерация с вложенным foreach в scala? - PullRequest
0 голосов
/ 28 апреля 2018

у меня есть

finals = Set[(String, String)]

и

init = Array[(String, String), String]

Что я пытаюсь сделать, если (String, String) массива init , равен кортежу из набора finalals , добавить его в Map[(String, String), String].

Например, финал :

Set[(m1, c1), (m2, c1)]

и init :

Array[[(m1, c1), n], [(m2, c2), l], (m2, c1), k]]

Результат будет:

Map[((m1, c1), n), ((m2, c1), k)]

Мой вопрос: правильны ли вложенные foreach в scala? Поскольку я написал код для итерации обеих структур одновременно, ошибки не было, и она работала, но я не знаю, является ли это функциональным способом.

Мой код:

init.foreach(i => {
 finals.foreach(j => {
  if (i._1.equals(j))
   { counts += (i._1 -> i._2) }
  })
 })

Заранее спасибо!

Ответы [ 3 ]

0 голосов
/ 28 апреля 2018

foreach может быть функциональным, но counts += ... - нет - как побочный эффект, он изменяет структуру данных counts.

Вот более длинный ответ, который приводит к одной строке, но я пытаюсь объяснить мыслительный процесс:

Когда я пытаюсь найти функциональный способ написания кода, мне часто бывает полезно подумать о том, как, например, я могу использовать ...

  • рекурсия
  • более продвинутые операции (карта, плоская карта, уменьшение, фильтрация, сгиб, ...)

В этом случае, если вы, например, подумайте, как вы могли бы сделать map, вы могли бы перефразировать это:

Что я пытаюсь сделать, это если (String, String) финала массива равен кортежу из набора инициализации, добавить его в Map [(String, String), String].

(я предполагаю, что вы случайно поменяли местами финалы / init в своем вопросе, но правильно указали их в бите кода)

как:

Я пытаюсь сопоставить элементы (String, String) в массиве finals, и, если один из них находится в init, вернуть найденный элемент. Возвращенное значение сохраняется в counts.

Однако в данном случае это не совсем то, что вы хотите - map требует, чтобы вы возвращали ровно 1 элемент для каждого элемента, который вы отображаете, поэтому вам нужно изменить значение на flatMap, чтобы избежать этого ограничения. .

val finals: Set[(String, String)] = Set(("m1", "c1"), ("m2", "c1"))

val init: Array[((String, String), String)] = Array((("m1", "c1"), "n"), (("m2", "c2"), "l"), (("m2", "c1"), "k"))

val counts = finals.flatMap( finals_item => init.filter(_._1 == finals_item))
/*
Set[((String, String), String)] = Set(
  (("m1", "c1"), "n"),
  (("m2", "c1"), "k")
)
*/

Тогда вы просто добавляете .toMap:

finals.flatMap( finals_item => init.filter(_._1 == finals_item)).toMap
0 голосов
/ 29 апреля 2018

Вот еще один способ написания этого кода

counts = init.filter(e => finals.contains(e._1)).toMap

Объяснение

Лучший способ подумать о такой проблеме - взглянуть на основные операции, которые вы выполняете, и выработать эквивалентную операцию в Scala.

Основная операция, которую вам требуется, это выбрать определенные элементы коллекции , называемые init, и операция Scala для этого будет filter.

Тест, который вы применяете, состоит в том, чтобы увидеть, находится ли первый кортеж в другой коллекции , и операция Scala для этого - contains.

Наконец, вы хотите преобразовать коллекцию кортежей в Map, и операция Scala для этого будет toMap.

0 голосов
/ 28 апреля 2018

Вот функциональный способ использования его с помощью collect

scala> val x=Set(("m1", "c1"), ("m2", "c1"))
//x: scala.collection.immutable.Set[(String, String)] = Set((m1,c1), (m2,c1))

scala> val y = Array((("m1", "c1"), "n"), (("m2", "c2"), "l"), (("m2", "c1"), "k"))

//y: Array[((String, String), String)] = Array(((m1,c1),n), ((m2,c2),l), ((m2,c1),k))

scala> y.collect{
        case e if x.filter(p=> p._1 == e._1._1 && p._2 == e._1._2).size>0 => e 
     }
//res3: Array[((String, String), String)] = Array(((m1,c1),n), ((m2,c1),k))
...