Scala: Как использовать fold * с Map?

У меня есть карта [String, String] и хочу объединить значения в одну строку.

Я вижу, как это сделать, используя List...

scala> val l = List("te", "st", "ing", "123")
l: List[java.lang.String] = List(te, st, ing, 123)

scala> l.reduceLeft[String](_+_)
res8: String = testing123

fold * или уменьшить *, кажется, правильный подход. Я просто не могу получить синтаксис для карты.

Ответ 1

Складки на карте работают так же, как и в списке пар. Вы не можете использовать сокращение, потому что тогда тип результата должен быть таким же, как тип элемента (т.е. Пара), но вам нужна строка. Таким образом, вы используете foldLeft с пустой строкой в ​​качестве нейтрального элемента. Вы также не можете просто использовать _+_, потому что тогда вы попытаетесь добавить пару к строке. Вместо этого вы должны использовать функцию, которая добавляет накопленную строку, первое значение пары и второе значение пары. Итак, вы получите следующее:

scala> val m = Map("la" -> "la", "foo" -> "bar")                 
m: scala.collection.immutable.Map[java.lang.String,java.lang.String] = Map(la -> la, foo -> bar)

scala> m.foldLeft("")( (acc, kv) => acc + kv._1 + kv._2)
res14: java.lang.String = lalafoobar

Объяснение первого аргумента для сброса:

Как вы знаете, функция (acc, kv) => acc + kv._1 + kv._2 получает два аргумента: вторая - пара ключей-значений, обрабатываемая в данный момент. Первый - это результат, накопленный до сих пор. Однако каково значение acc, когда обрабатывается первая пара (и пока результат не накапливается)? Когда вы используете reduce, первое значение acc будет первой парой в списке (а первое значение kv будет второй парой в списке). Однако это не работает, если вы хотите, чтобы тип результата отличался от типов элементов. Поэтому вместо сокращения мы используем fold, где мы передаем первое значение acc в качестве первого аргумента foldLeft.

Вкратце: первый аргумент foldLeft указывает, что должно быть начальным значением acc.

Как заметил Том, вы должны иметь в виду, что карты не обязательно поддерживают порядок вставки (Map2 и co. do, но hashmaps нет), поэтому строка может отображать элементы в другом порядке, чем в которые вы вставили.

Ответ 2

Вопрос уже был дан ответ, но я хотел бы указать, что есть более простые способы создания этих строк, если это все, что вы хотите. Вот так:

scala> val l = List("te", "st", "ing", "123")
l: List[java.lang.String] = List(te, st, ing, 123)

scala> l.mkString
res0: String = testing123

scala> val m = Map(1 -> "abc", 2 -> "def", 3 -> "ghi")
m: scala.collection.immutable.Map[Int,java.lang.String] = Map((1,abc), (2,def), (3,ghi))

scala> m.values.mkString
res1: String = abcdefghi