I am using a map to associate certain values with a tuple (Int, Double) where the int is the order they appeared and the double the number of times they show (it is not, but is clearer like this using int and double to distinguish)
The tricky part is that I want to use different monoids for each element of the tuple, for the int I want to keep the min value, to remember first appearance, while for the double I want to use the addition monoid So for an existing key we have:
val map1 = Map("a" -> (1, 5.0), "b" -> (2, 4.0), "c" -> (3, 8.0))
val map2 = Map("b" -> (4, 1.0))
val merge = map1.toMap |+| map2.toMap
// Map(a -> (1, 5.0), b -> (2, 5.0), c -> (3, 8.0))
And for a new key we have:
val map2 = Map("d" -> (4, 1.0))
val merge2 = map1.toMap |+| map2.toMap
// Map(a -> (1, 5.0), b -> (2, 4.0), c -> (3, 8.0), d -> (4, 1.0))
I can not find a way do this, i can obviously use the addition monoid, and i can use the minval one, but i can not see how to combine them. Any help appreciated! thanks
You can use scalaz.std.tuple.tuple2Monoid
explicitly with the two monoids you want:
import scalaz.Monoid
implicit val countMonoid: Monoid[(Int, Double)] = scalaz.std.tuple.tuple2Monoid(
Monoid.instance[Int](math.min(_, _), Int.MaxValue),
Monoid.instance[Double](_ + _, 0)
)
And then:
scala> import scalaz.std.map._, scalaz.syntax.monoid._
import scalaz.std.map._
import scalaz.syntax.monoid._
scala> val map1 = Map("a" -> (1, 5.0), "b" -> (2, 4.0), "c" -> (3, 8.0))
map1: scala.collection.immutable.Map[String,(Int, Double)] = Map(a -> (1,5.0), b -> (2,4.0), c -> (3,8.0))
scala> val map2 = Map("b" -> (4, 1.0))
map2: scala.collection.immutable.Map[String,(Int, Double)] = Map(b -> (4,1.0))
scala> val merge = map1.toMap |+| map2.toMap
merge: scala.collection.immutable.Map[String,(Int, Double)] = Map(a -> (1,5.0), b -> (2,5.0), c -> (3,8.0))
scala> val map2 = Map("d" -> (4, 1.0))
map2: scala.collection.immutable.Map[String,(Int, Double)] = Map(d -> (4,1.0))
scala> val merge2 = map1.toMap |+| map2.toMap
merge2: scala.collection.immutable.Map[String,(Int, Double)] = Map(a -> (1,5.0), b -> (2,4.0), c -> (3,8.0), d -> (4,1.0))
This isn't really ideal, though, since the type (Int, Double)
can be used to represent lots of different things, and you've just defined a monoid instance that might turn up in places you or your users don't expect. Personally I'd use a case class instead:
case class Count(order: Int, number: Double)
And then define the instance in the Count
companion object, either explicitly or via the countMonoid
above and an IsoSet[Count, (Int, Double)]
.