Scala: How do I use fold* with Map?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3199414/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-22 02:17:06  来源:igfitidea点击:

Scala: How do I use fold* with Map?

scala

提问by Vonn

I have a Map[String, String] and want to concatenate the values to a single string.

I have a Map[String, String] and want to concatenate the values to a single string.

I can see how to do this using a List...

I can see how to do this using a 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* or reduce* seem to be the right approach I just can't get the syntax right for a Map.

fold* or reduce* seem to be the right approach I just can't get the syntax right for a Map.

回答by sepp2k

Folds on a map work the same way they would on a list of pairs. You can't use reduce because then the result type would have to be the same as the element type (i.e. a pair), but you want a string. So you use foldLeftwith the empty string as the neutral element. You also can't just use _+_because then you'd try to add a pair to a string. You have to instead use a function that adds the accumulated string, the first value of the pair and the second value of the pair. So you get this:

Folds on a map work the same way they would on a list of pairs. You can't use reduce because then the result type would have to be the same as the element type (i.e. a pair), but you want a string. So you use foldLeftwith the empty string as the neutral element. You also can't just use _+_because then you'd try to add a pair to a string. You have to instead use a function that adds the accumulated string, the first value of the pair and the second value of the pair. So you get this:

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

Explanation of the first argument to fold:

Explanation of the first argument to fold:

As you know the function (acc, kv) => acc + kv._1 + kv._2gets two arguments: the second is the key-value pair currently being processed. The first is the result accumulated so far. However what is the value of accwhen the first pair is processed (and no result has been accumulated yet)? When you use reducethe first value of accwill be the first pair in the list (and the first value of kv will be the second pair in the list). However this does not work if you want the type of the result to be different than the element types. So instead of reduce we use fold where we pass the first value of accas the first argument to foldLeft.

As you know the function (acc, kv) => acc + kv._1 + kv._2gets two arguments: the second is the key-value pair currently being processed. The first is the result accumulated so far. However what is the value of accwhen the first pair is processed (and no result has been accumulated yet)? When you use reducethe first value of accwill be the first pair in the list (and the first value of kv will be the second pair in the list). However this does not work if you want the type of the result to be different than the element types. So instead of reduce we use fold where we pass the first value of accas the first argument to foldLeft.

In short: the first argument to foldLeftsays what the starting value of accshould be.

In short: the first argument to foldLeftsays what the starting value of accshould be.

As Tom pointed out, you should keep in mind that maps don't necessarily maintain insertion order (Map2 and co. do, but hashmaps do not), so the string may list the elements in a different order than the one in which you inserted them.

As Tom pointed out, you should keep in mind that maps don't necessarily maintain insertion order (Map2 and co. do, but hashmaps do not), so the string may list the elements in a different order than the one in which you inserted them.

回答by Daniel C. Sobral

The question has been answered already, but I'd like to point out that there are easier ways to produce those strings, if that's all you want. Like this:

The question has been answered already, but I'd like to point out that there are easier ways to produce those strings, if that's all you want. Like this:

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