Сопоставьте ключи и значения карты Scala

89

У Scala MapLikeесть метод

mapValues [C] (f: (B) ⇒ C): Map[A, C] 

Но иногда хочется другого типа:

mapKeysAndValues [C] (f: (A, B) ⇒ C): Map[A, C] 

Есть ли простой способ сделать это, которого мне не хватает? Конечно, это можно сделать со складкой.

Алексей Романов
источник

Ответы:

166

mapметод выполняет итерацию по всем (key, value)парам. Вы можете использовать это так:

val m = Map("a" -> 1, "b" -> 2)

val incM = m map {case (key, value) => (key, value + 1)}
Tenhi
источник
8
Если у вас уже есть функция f : (A,B) => (A,C), то можете просто m.map(f.tupled). Работает с, val f = (x: String, y: Int) => (x, y+1)но, как ни странно, ответ жалуется, если я определяю fэквивалентно с def.
Дэн Бертон
2
что здесь дает ключевое слово case?
Вездесущий
@Omnipresent {case (key, value) => ...}в этом случае - это просто сопоставление с образцом. Вместо того, чтобы предоставлять функцию a map, я передаю ему частичную функцию.
tenshi
Примечание: caseпозволит вам помещать типы в его шаблон, но это небезопасно, так как может вызвать ошибку времени выполнения (потому что это просто совпадение с шаблоном). Между тем, если вы когда-нибудь измените структуру коллекции под mapфункцией так, что будет слишком мало или слишком много объектов для интерпретации (key, value), то опять же, я ожидаю, что вы получите ошибку времени выполнения. :(
комбинатор
8

А как насчет этого кода:

val m = Map(1 -> "one", 2 -> "two")
def f(k: Int, v: String) = k + "-" + v
m map {case (k, v) => (k, f(k, v))}

Что производит:

 Map(1 -> 1-one, 2 -> 2-two)

Это можно упаковать в служебный метод:

def mapKeysAndValues[A,B,C](input: Map[A,B], fun: (A, B) => C) = 
  input map {case(k,v) => (k, fun(k, v))}

Применение:

mapKeysAndValues(
  Map(1 -> "one", 2 -> "two"), 
  (k: Int, v: String) => k + "-" + v
)
Томаш Нуркевич
источник
Разве это не то же самое MapLike#transform?
Хосам Али
5
m map (t => (t._1, t._2 + 1))

m map (t => t._1 -> t._2 + 1)
Григорий Кислин
источник
2

С некоторым Скалазом:

scala> def fst[A, B] = (x: (A, B)) => x._1
fst: [A, B]=> (A, B) => A

scala> Map(1 -> "Lorem", 2 -> "Ipsum").map(fst &&& Function.tupled(_.toString + _))
res1: scala.collection.immutable.Map[Int,java.lang.String] = Map(1 -> 1Lorem, 2 -> 2Ipsum)

Мне больше нравится решение @tenshi.

пропавший
источник
0

Вы можете создать служебный класс:

class MyMapLike[K,V](m:MapLike[K,V,_]){
 def mapKeysAndValues[R](f: (K, V) => R)={
   m.map{case (k,v)=> f(k,v)}
 } 
}
object MyMapLike{
 implicit def maplike2mymaplike[K,V](ml:MapLike[K,V,_]):MyMapLike[K,V]=new MyMapLike(m)

}

import MyMapLike._
Map(1 -> "one", 2 -> "two").mapKeysAndValues(k,v=>v*k)

Код не тестировался, но он должен работать примерно так же.

Alex
источник