Самый эффективный способ создания матрицы из карты [дубликат]

Функциональная разница не будет отображаться в этих тестах:

BaseClass bc = new BaseClass();

bc.DoIt();

DerivedClass dc = new DerivedClass();

dc.ShowIt();

В этом примере, вызванная Doit является той, которую вы ожидаете назвать.

In чтобы увидеть разницу, которую вы должны сделать:

BaseClass obj = new DerivedClass();

obj.DoIt();

Вы увидите, если вы запустите этот тест, который в случае 1 (как вы его определили), DoIt() в BaseClass (в том случае, если вы определили его), вызывается DoIt() в DerivedClass.

144
задан Freewind 16 August 2011 в 10:47
поделиться

12 ответов

Scalaz имеет концепцию полугруппы , которая фиксирует то, что вы хотите здесь сделать, и приводит к, возможно, кратчайшему / чистому решению:

scala> import scalaz._
import scalaz._

scala> import Scalaz._
import Scalaz._

scala> val map1 = Map(1 -> 9 , 2 -> 20)
map1: scala.collection.immutable.Map[Int,Int] = Map(1 -> 9, 2 -> 20)

scala> val map2 = Map(1 -> 100, 3 -> 300)
map2: scala.collection.immutable.Map[Int,Int] = Map(1 -> 100, 3 -> 300)

scala> map1 |+| map2
res2: scala.collection.immutable.Map[Int,Int] = Map(1 -> 109, 3 -> 300, 2 -> 20)

В частности, двоичный оператор для Map[K, V] объединяет ключи карт, складывая оператор полугруппы V по любым повторяющимся значениям. Стандартная полугруппа для Int использует оператор сложения, поэтому вы получаете сумму значений для каждого повторяющегося ключа.

Редактирование: немного больше деталей по запросу пользователя482745.

Математически полугруппа представляет собой всего лишь набор значений вместе с оператором, который принимает два значения из этого множества и выдает другое значение из этого набора. Таким образом, целые числа при добавлении являются полугруппой, например: оператор + объединяет два ints для создания другого int.

Вы также можете определить полугруппу по множеству «всех карт с заданным типом ключа и тип значения ", пока вы можете придумать некоторую операцию, которая объединяет две карты для создания новой, которая представляет собой комбинацию двух входов.

Если на обеих картах нет ключей , это тривиально. Если один и тот же ключ существует на обеих картах, нам нужно объединить два значения, к которым привязана клавиша. Хм, разве мы не просто описали оператор, который объединяет два объекта одного типа? Поэтому в Scalaz полугруппа для Map[K, V] существует тогда и только тогда, когда существует полугруппа для V - полугруппа V используется для объединения значений из двух карт, которые назначены одному и тому же ключу.

Так как Int является типом значения здесь, «столкновение» на клавише 1 разрешается путем целочисленного добавления двух отображаемых значений (поскольку это то, что делает оператор полугруппы Int), следовательно 100 + 9. Если бы значения были Strings, столкновение привело бы к конкатенации строк двух отображаемых значений (опять же, потому что это то, что делает оператор полугруппы для String).

(И интересно, поскольку конкатенация строк не является коммутативный, т. е. "a" + "b" != "b" + "a" - результирующая полугрупповая операция также не является. Поэтому map1 |+| map2 отличается от map2 |+| map1 в случае String, но не в случае Int.)

131
ответ дан Erik Allik 19 August 2018 в 02:16
поделиться
  • 1
    Brilliant! Первый практический пример, где scalaz имеет смысл. – soc 16 August 2011 в 12:34
  • 2
    Без шуток! Если вы начнете искать его ... это повсюду. Чтобы процитировать erric torrebone, автор спецификаций и спецификаций2: «Сначала вы изучаете Option, и вы начинаете видеть его повсюду. Затем вы изучаете Прикладное, и это одно и то же. След & Quot; Далее представлены еще более функциональные концепции. И это очень помогает вам структурировать свой код и решать проблемы красиво. – AndreasScheinert 16 August 2011 в 13:00
  • 3
    Фактически, я искал вариант в течение пяти лет, когда наконец нашел Скала. Разница между ссылкой на объект Java, что может равна нулю, а одна, которая не может быть (например, между A и Option[A]), настолько велика, я не мог поверить, что они были действительно одного и того же типа. I просто начал смотреть на Скалаз. Я не уверен, что я достаточно умен ... – Malvolio 17 August 2011 в 01:39
  • 4
    Опция для Java тоже есть, см. Функциональная Java. Не бойтесь, обучение - это весело. И функциональное программирование не учит вас новым вещам (только), но вместо этого предлагает вам помощь программиста с предоставлением терминов, лексики для решения проблем. Вопрос OP - прекрасный пример. Концепция полугруппы настолько проста, что вы каждый день используете ее, например, Строки. Реальная власть появляется, если вы идентифицируете эту абстракцию, назовите ее и, наконец, примените ее к другим типам, а затем просто к String. – AndreasScheinert 17 August 2011 в 17:56
  • 5
    Как возможно, что это приведет к 1 - & gt; (100 + 9)? Можете ли вы показать мне «трассировку стека»? Спасибо. P.S .: Я прошу здесь сделать ответ более ясным. – user482745 2 February 2012 в 14:33

Самый быстрый и простой способ:

val m1 = Map(1 -> 1.0, 3 -> 3.0, 5 -> 5.2)
val m2 = Map(0 -> 10.0, 3 -> 3.0)
val merged = (m2 foldLeft m1) (
  (acc, v) => acc + (v._1 -> (v._2 + acc.getOrElse(v._1, 0.0)))
)

Таким образом, каждый элемент сразу добавляется к карте.

Второй способ ++:

map1 ++ map2.map { case (k,v) => k -> (v + map1.getOrElse(k,0)) }

В отличие от первого способа. Во втором случае для каждого элемента во второй карте будет создан и объединен новый список с предыдущей картой.

Выражение case неявно создает новый список с использованием метода unapply.

0
ответ дан Alexey Kudryashov 19 August 2018 в 02:16
поделиться
map1 ++ ( for ( (k,v) <- map2 ) yield ( k -> ( v + map1.getOrElse(k,0) ) ) )
4
ответ дан AmigoNico 19 August 2018 в 02:16
поделиться

Вы также можете сделать это с помощью Cats .

import cats.implicits._

val map1 = Map(1 -> 9 , 2 -> 20)
val map2 = Map(1 -> 100, 3 -> 300)

map1 combine map2 // Map(2 -> 20, 1 -> 109, 3 -> 300)
1
ответ дан Artsiom Miklushou 19 August 2018 в 02:16
поделиться

У меня есть небольшая функция для выполнения этой работы, это в моей небольшой библиотеке для некоторых часто используемых функций, которые не входят в стандартную библиотеку. Он должен работать для всех типов карт, изменяемых и неизменных, а не только для HashMaps

. Вот использование

scala> import com.daodecode.scalax.collection.extensions._
scala> val merged = Map("1" -> 1, "2" -> 2).mergedWith(Map("1" -> 1, "2" -> 2))(_ + _)
merged: scala.collection.immutable.Map[String,Int] = Map(1 -> 2, 2 -> 4)

https://github.com/jozic/ scalax-collection / blob / master / README.md # mergedwith

И вот тело

def mergedWith(another: Map[K, V])(f: (V, V) => V): Repr =
  if (another.isEmpty) mapLike.asInstanceOf[Repr]
  else {
    val mapBuilder = new mutable.MapBuilder[K, V, Repr](mapLike.asInstanceOf[Repr])
    another.foreach { case (k, v) =>
      mapLike.get(k) match {
        case Some(ev) => mapBuilder += k -> f(ev, v)
        case _ => mapBuilder += k -> v
      }
    }
    mapBuilder.result()
  }

https://github.com/jozic /scalax-collection/blob/master/src%2Fmain%2Fscala%2Fcom%2Fdaodecode%2Fscalax%2Fcollection%2Fextensions%2Fpackage.scala#L190

0
ответ дан Eugene Platonov 19 August 2018 в 02:16
поделиться

Это может быть реализовано как Monoid с простой Scala. Вот пример реализации. С помощью этого подхода мы можем объединить не только 2, но и список карт.

// Monoid trait

trait Monoid[M] {
  def zero: M
  def op(a: M, b: M): M
}

Реализация на основе карты моноидного признака, объединяющего две карты.

val mapMonoid = new Monoid[Map[Int, Int]] {
  override def zero: Map[Int, Int] = Map()

  override def op(a: Map[Int, Int], b: Map[Int, Int]): Map[Int, Int] =
    (a.keySet ++ b.keySet) map { k => 
      (k, a.getOrElse(k, 0) + b.getOrElse(k, 0))
    } toMap
}

Теперь, если у вас есть список карт, которые необходимо объединить (в этом случае всего 2), это можно сделать, как показано ниже.

val map1 = Map(1 -> 9 , 2 -> 20)
val map2 = Map(1 -> 100, 3 -> 300)

val maps = List(map1, map2) // The list can have more maps.

val merged = maps.foldLeft(mapMonoid.zero)(mapMonoid.op)
11
ответ дан Jegan 19 August 2018 в 02:16
поделиться

Это то, что я придумал ...

def mergeMap(m1: Map[Char, Int],  m2: Map[Char, Int]): Map[Char, Int] = {
   var map : Map[Char, Int] = Map[Char, Int]() ++ m1
   for(p <- m2) {
      map = map + (p._1 -> (p._2 + map.getOrElse(p._1,0)))
   }
   map
}
1
ответ дан kaur 19 August 2018 в 02:16
поделиться

Ну, теперь в библиотеке scala (по крайней мере, в 2.10) есть что-то, что вы хотели - объединенная функция. НО он представлен только в HashMap, а не в Map. Это несколько запутанно. Также подпись громоздка - не могу представить, почему мне нужен ключ дважды, и когда мне нужно будет создать пару с другим ключом. Но, тем не менее, он работает и намного чище, чем предыдущие «родные» решения.

val map1 = collection.immutable.HashMap(1 -> 11 , 2 -> 12)
val map2 = collection.immutable.HashMap(1 -> 11 , 2 -> 12)
map1.merged(map2)({ case ((k,v1),(_,v2)) => (k,v1+v2) })

Также в scaladoc упоминалось, что

Метод merged в среднем более эффективен чем совершать обход и восстанавливать новую неизменяемую хэш-карту с нуля или ++.

37
ответ дан Mikhail Golubtsov 19 August 2018 в 02:16
поделиться
  • 1
    На данный момент это только в неизменном Хашмапе, а не изменчивом Хашмапе. – Kevin Wheeler 25 October 2014 в 03:33
  • 2
  • 3
    Я не могу заставить это скомпилировать, похоже, что тип, который он принимает, является закрытым, поэтому я не могу передать типизированную функцию, которая соответствует. – Ryan The Leach 7 July 2015 в 20:20
  • 4
    Кажется, что что-то изменилось в версии 2.11. Проверьте 2.10 scaladoc - scala-lang.org/api/2.10.1/… Существует обычная функция. Но в 2.11 это MergeFunction. – Mikhail Golubtsov 8 July 2015 в 06:26
  • 5
    Все, что изменилось в 2.11, - это введение псевдонима типа для этого конкретного типа функции private type MergeFunction[A1, B1] = ((A1, B1), (A1, B1)) => (A1, B1) – EthanP 10 March 2016 в 21:00

Быстрое решение:

(map1.keySet ++ map2.keySet).map {i=> (i,map1.getOrElse(i,0) + map2.getOrElse(i,0))}.toMap
41
ответ дан Miriam Farber 19 August 2018 в 02:16
поделиться

Я написал сообщение в блоге об этом, проверьте:

http://www.nimrodstech.com/scala-map-merge/

, в основном используя полугруппу scalaz, вы можете достичь этого довольно легко.

будет выглядеть примерно так:

  import scalaz.Scalaz._
  map1 |+| map2
5
ответ дан Nimrod007 19 August 2018 в 02:16
поделиться
  • 1
    Вы должны добавить немного больше подробностей в свой ответ, желательно код реализации. Сделайте это также для других подобных ответов, которые вы опубликовали, и адаптируйте каждый ответ к конкретному вопросу, который был задан. Правило большого пальца: Ответчик должен иметь возможность воспользоваться вашим ответом, не нажимая ссылку в блоге. – Robert Harvey♦ 29 July 2014 в 15:24

Самый короткий ответ, который я знаю об этом, использует только стандартную библиотеку

map1 ++ map2.map{ case (k,v) => k -> (v + map1.getOrElse(k,0)) }
126
ответ дан Rex Kerr 19 August 2018 в 02:16
поделиться
  • 1
  • 2
    Вид опрятной версии: for ((k, v) & lt; - (aa ++ bb)) дает k - & gt; (если ((aa содержит k) & amp; & amp; (bb содержит k)) aa (k) + v else v) – dividebyzero 13 December 2014 в 02:05
  • 3
    Я сделал что-то другое ранее, но вот версия того, что вы сделали, заменив карту на for map1 ++ (for ((k, v) & lt; - map2), получаем k - & gt; (v + map1. getOrElse (к, 0))) – dividebyzero 13 December 2014 в 02:41
  • 4
    @dividebyzero - Да, это именно то, что сделал Амигонико в ответ ниже. – Rex Kerr 13 December 2014 в 08:55
  • 5
    @matt - Scalaz уже сделает это, поэтому я бы сказал, что «существующая библиотека уже делает это». – Rex Kerr 8 November 2015 в 22:54

Ответ Анджей Дойла содержит большое объяснение полугрупп, которое позволяет вам использовать оператор |+| для объединения двух карт и суммировать значения для совпадающих ключей.

Существует много способов определить что-то быть экземпляром typeclass, и в отличие от OP вы можете не захотеть точно суммировать свои ключи. Или, возможно, вы захотите работать на объединении, а не на перекрестке. Scalaz также добавляет дополнительные функции для Map для этой цели:

https://oss.sonatype.org/service/local/repositories/snapshots/archive/org/scalaz/scalaz_2.11 /7.3.0-SNAPSHOT/scalaz_2.11-7.3.0-SNAPSHOT-javadoc.jar/!/index.html#scalaz.std.MapFunctions

Вы можете сделать

import scalaz.Scalaz._

map1 |+| map2 // As per other answers
map1.intersectWith(map2)(_ + _) // Do things other than sum the values
2
ответ дан user1158559 19 August 2018 в 02:16
поделиться
Другие вопросы по тегам:

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