Много-карта ценности в Scala

В Scala 2.8 у меня есть неизменная карта с несколькими значениями для каждого ключа:

Map[T,Iterable[U]]

Существует ли превосходящее представление? Во-вторых, как Вы генерировали бы такую карту от

Iterable[(T,U)]

? Я в настоящее время использую:

def toGroupedMap[T,U](vals: Iterable[(T,U)]): Map[T,Iterable[U]] =
  vals.groupBy(_._1).map({ case (s,it) => (s,it.map(_._2)) }).toMap

Который работает, но чувствует себя неуклюжим.

Править: Я должен указать, что работаю с неизменными данными. Существует ли неизменный эквивалент MultiMap?

5
задан David Crawshaw 3 February 2010 в 17:48
поделиться

3 ответа

Если вам действительно не нужна неизменяемость, тогда, как говорили другие, MultiMap - это то, что вам нужно. Если вам действительно нужна неизменяемость, то выбранный вами подход так же прост, как и все остальное; там нет ничего встроенного (AFAIK), и любое создание неизменяемой MultiMap потребует гораздо больше работы, чем метод, который у вас есть.

Будет ли представление лучше, зависит от вашего использования. Вы часто хотите делать что-то со всеми значениями, соответствующими одному ключу? Можно ли вставить на карту одно и то же значение несколько раз? Если да, то ваше представление правильное.

Если вы хотите, чтобы одно и то же значение было вставлено не более одного раза в один ключ, вам следует использовать Set [U] вместо Iterable [U] (что легко сделать с помощью добавление .toSet в it.map (_._ 2) ).

Если вам не нравится иметь дело с наборами / итерациями и вы просто терпите это (то есть вы действительно предпочитаете иметь пары ключ-значение, а не пары ключ-набор значений), вам придется написать класс-оболочку вокруг карта, которая представляет единый интерфейс карты и будет делать правильные вещи с +, - и итератором.

Вот пример, который оказался немного длиннее, чем я ожидал (здесь отформатирован для вырезания и вставки в REPL):

import scala.collection._
class MapSet[A,B](
  val sets: Map[A,Set[B]] = Map[A,Set[B]]()
) extends Map[A,B] with MapLike[A,B,MapSet[A,B]] {
  def get(key: A) = sets.getOrElse(key,Set[B]()).headOption
  def iterator = new Iterator[(A,B)] {
    private val seti = sets.iterator
    private var thiskey:Option[A] = None
    private var singles:Iterator[B] = Nil.iterator
    private def readyNext {
      while (seti.hasNext && !singles.hasNext) {
        val kv = seti.next
        thiskey = Some(kv._1)
        singles = kv._2.iterator
      }
    }
    def hasNext = {
      if (singles.hasNext) true
      else {
        readyNext
        singles.hasNext
      }
    }
    def next = {
      if (singles.hasNext) (thiskey.get , singles.next)
      else {
        readyNext
        (thiskey.get , singles.next)
      }
    }
  }
  def +[B1 >: B](kv: (A,B1)):MapSet[A,B] = {
    val value:B = kv._2.asInstanceOf[B]
    new MapSet( sets + ((kv._1 , sets.getOrElse(kv._1,Set[B]()) + value)) )
  }
  def -(key: A):MapSet[A,B] = new MapSet( sets - key )
  def -(kv: (A,B)):MapSet[A,B] = {
    val got = sets.get(kv._1)
    if (got.isEmpty || !got.get.contains(kv._2)) this
    else new MapSet( sets + ((kv._1 , got.get - kv._2)) )
  }
  override def empty = new MapSet( Map[A,Set[B]]() )
}

, и мы видим, что это работает так, как нужно:

scala> new MapSet() ++ List(1->"Hi",2->"there",1->"Hello",3->"Bye")
res0: scala.collection.Map[Int,java.lang.String] = Map(1 -> Hi, 1 -> Hello, 2 -> there, 3 -> Bye)

scala> res0 + (2->"ya")
res1: scala.collection.Map[Int,java.lang.String] = Map(1 -> Hi, 1 -> Hello, 2 -> there, 2 -> ya, 3 -> Bye)

scala> res1 - 1
res2: scala.collection.Map[Int,java.lang.String] = Map(2 -> there, 2 -> ya, 3 -> Bye)

(хотя, если вы хотел вернуть MapSet после ++, вам нужно переопределить ++; иерархия Map не имеет собственных конструкторов, которые позаботились бы о таких вещах).

4
ответ дан 14 December 2019 в 13:35
поделиться

Я использую что-то подобное:

#define ASSERT(lhs, op, rhs) assert_template((lhs##op##rhs), "(" #lhs #op #rhs ")", lhs, rhs, __FILE__, __LINE__)

template <typename t1, typename t2>
void assert_template(const bool result, const char expr[], t1 lhs, t2 rhs, const char file_name[], const long line_number)
{
    if (!result)
    {
        std::cerr << "Assertion failed";
        std::cerr << "    " << expr;
        std::cerr << "    lhs = " << lhs;
        std::cerr << "    rhs = " << rhs;
        std::cerr << "    File: \"" << file_name << "\"";
        std::cerr << "    Line: " << std::dec << line_number;

        throw "Assertion failed";
    }
};

Синтаксис использования немного странный, как в ASSERT (vec.size (), >, 1) или ASSERT (ошибка, =, 0) . Верхняя сторона состоит в том, что она также распечатывает значения левой и правой стороны. В Windows мне также нравится вводить GetLastError () и WSAGetLastError ().

-121--2276664-

sudo apt-get install php5-mcrypt

работает на ubuntu.

-121--4546883-

Просмотрите смесь MultiMap для карты.

2
ответ дан 14 December 2019 в 13:35
поделиться

Мультимап - это то, что вам нужно. Вот пример создания одного и последующего добавления в него записей из списка [(String, Int)]. Я уверен, что есть способ получше.

scala> val a = new collection.mutable.HashMap[String, collection.mutable.Set[Int]]() with collection.mutable.MultiMap[String, Int]
a: scala.collection.mutable.HashMap[String,scala.collection.mutable.Set[Int]] with scala.collection.mutable.MultiMap[String,Int] = Map()

scala> List(("a", 1), ("a", 2), ("b", 3)).map(e => a.addBinding(e._1, e._2))                                                      
res0: List[scala.collection.mutable.HashMap[String,scala.collection.mutable.Set[Int]] with scala.collection.mutable.MultiMap[String,Int]] = List(Map(a -> Set(1, 2), b -> Set(3)), Map(a -> Set(1, 2), b -> Set(3)), Map(a -> Set(1, 2), b -> Set(3)))

scala> a("a")
res2: scala.collection.mutable.Set[Int] = Set(1, 2)
0
ответ дан 14 December 2019 в 13:35
поделиться
Другие вопросы по тегам:

Похожие вопросы: