Есть ли такая вещь, как двунаправленные карты в Scala?
Я хотел бы связать 2 столбца с уникальными идентификаторами и получить первое значение столбца со значением второго столбца, а также вторым значением столбца значением первого столбца. Что-то вроде
Map(1 <-> "one", 2 <-> "two", 3 <-> "three")
Есть ли такое средство в Scala?
На самом деле мне нужно еще больше: 3 столбца для выбора любого в триплете другим в триплете (индивидуальные значения никогда не будут встречаться более одного раза на всей карте). Но двунаправленная двунаправленная карта тоже может помочь.
Ответы
Ответ 1
Мой подход BiMap:
object BiMap {
private[BiMap] trait MethodDistinctor
implicit object MethodDistinctor extends MethodDistinctor
}
case class BiMap[X, Y](map: Map[X, Y]) {
def this(tuples: (X,Y)*) = this(tuples.toMap)
private val reverseMap = map map (_.swap)
require(map.size == reverseMap.size, "no 1 to 1 relation")
def apply(x: X): Y = map(x)
def apply(y: Y)(implicit d: BiMap.MethodDistinctor): X = reverseMap(y)
val domain = map.keys
val codomain = reverseMap.keys
}
val biMap = new BiMap(1 -> "A", 2 -> "B")
println(biMap(1)) // A
println(biMap("B")) // 2
Конечно, для <->
можно добавить синтаксис вместо ->
.
Ответ 2
В Guava есть bimap, которые вы можете использовать вместе с
import scala.collection.JavaConversions._
Ответ 3
Вот быстрая обертка Scala для Guava BiMap
.
import com.google.common.{collect => guava}
import scala.collection.JavaConversions._
import scala.collection.mutable
import scala.languageFeature.implicitConversions
class MutableBiMap[A, B] private (
private val g: guava.BiMap[A, B] = new guava.HashBiMap[A, B]()) {
def inverse: MutableBiMap[B, A] = new MutableBiMap[B, A](g.inverse)
}
object MutableBiMap {
def empty[A, B]: MutableBiMap[A, B] = new MutableBiMap()
implicit def toMap[A, B] (x: MutableBiMap[A, B]): mutable.Map[A,B] = x.g
}
Ответ 4
Я не думаю, что он существует из коробки, потому что общее поведение нелегко извлечь
Как обрабатывать значения, соответствующие нескольким клавишам в чистом api?
Однако для конкретных случаев это хорошее упражнение, которое может помочь. Он должен быть обновлен, потому что не используется хэш и получение ключа или значения O (n).
Но идея состоит в том, чтобы вы могли написать что-то похожее на то, что вы предлагаете, но используя Seq вместо Map...
С помощью неявных и черт, плюс find, вы можете эмулировать то, что вам нужно, с помощью чистого api (fromKey
, fromValue
).
Специфичность заключается в том, что значение не должно появляться в нескольких местах... В этой реализации по крайней мере.
trait BiMapEntry[K, V] {
def key:K
def value:V
}
trait Sem[K] {
def k:K
def <->[V](v:V):BiMapEntry[K, V] = new BiMapEntry[K, V]() { val key = k; val value = v}
}
trait BiMap[K, V] {
def fromKey(k:K):Option[V]
def fromValue(v:V):Option[K]
}
object BiMap {
implicit def fromInt(i:Int):Sem[Int] = new Sem[Int] {
def k = i
}
implicit def fromSeq[K, V](s:Seq[BiMapEntry[K, V]]) = new BiMap[K, V] {
def fromKey(k:K):Option[V] = s.find(_.key == k).map(_.value)
def fromValue(v:V):Option[K] = s.find(_.value == v).map(_.key)
}
}
object test extends App {
import BiMap._
val a = 1 <-> "a"
val s = Seq(1 <-> "a", 2 <-> "b")
println(s.fromKey(2))
println(s.fromValue("a"))
}
Ответ 5
У меня действительно простой BiMap
в Scala:
case class BiMap[A, B](elems: (A, B)*) {
def groupBy[X, Y](pairs: Seq[(X, Y)]) = pairs groupBy {_._1} mapValues {_ map {_._2} toSet}
val (left, right) = (groupBy(elems), groupBy(elems map {_.swap}))
def apply(key: A) = left(key)
def apply[C: ClassTag](key: B) = right(key)
}
Использование:
val biMap = BiMap(1 -> "x", 2 -> "y", 3 -> "x", 1 -> "y")
assert(biMap(1) == Set("x", "y"))
assert(biMap("x") == Set(1, 3))