Есть ли такая вещь, как двунаправленные карты в Scala? - PullRequest
22 голосов
/ 24 марта 2012

Я хотел бы связать 2 столбца уникальных идентификаторов и иметь возможность получить значение первого столбца по значению второго столбца, а также значение второго столбца по значению первого столбца.Что-то вроде

Map(1 <-> "one", 2 <-> "two", 3 <-> "three")

Есть ли такая возможность в Scala?

На самом деле мне нужно еще больше: 3 столбца для выбора любого в триплете, другой в триплете (отдельные значения никогда не будутвстречались не раз на всей карте).Но может помочь и двунаправленная карта с двумя столбцами.

Ответы [ 6 ]

10 голосов
/ 28 марта 2012

Гуава имеет двуногий , который можно использовать вместе с

import scala.collection.JavaConversions._
10 голосов
/ 24 марта 2012

Мой подход 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

Конечно, можно добавить синтаксис для <-> вместо ->.

3 голосов
/ 22 сентября 2014

Вот быстрая оболочка Scala для 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
}
от Guava.
2 голосов
/ 02 мая 2014

У меня действительно просто 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))
1 голос
/ 24 марта 2012

Я не думаю, что он существует "из коробки", потому что общее поведение нелегко извлечь

Как обрабатывать значения, соответствующие нескольким ключам в чистом 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"))

  }
0 голосов
/ 24 мая 2018

Scala является неизменным, и значения назначаются как ссылка, а не как копия, поэтому объем памяти будет использоваться только для хранения ссылки / указателя, который лучше использовать для двух карт, причем тип A является ключом для первого, а тип B является ключом для второго сопоставлены с B и A соответственно, чем время перестановки карт. И реализация подкачки также имеет свой собственный след памяти, и недавно обмененная хэш-карта также будет в памяти до выполнения родительского обратного вызова и вызова сборщика мусора. И если обмен картами требуется чаще, чем фактически, вы используете столько же или больше памяти, чем при наивной реализации двух карт при запуске.

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

def getKeyByValue[A,B](map: Map[A,B], value: B):Option[A] = hashMap.find((a:A,b:B) => b == value)

Код для Scala реализации поиска по ключу:

/** Find entry with given key in table, null if not found.
   */
  @deprecatedOverriding("No sensible way to override findEntry as private findEntry0 is used in multiple places internally.", "2.11.0")
  protected def findEntry(key: A): Entry =
    findEntry0(key, index(elemHashCode(key)))

  private[this] def findEntry0(key: A, h: Int): Entry = {
    var e = table(h).asInstanceOf[Entry]
    while (e != null && !elemEquals(e.key, key)) e = e.next
    e
  }
...