Scala: how to upsert field value in Monocle - scala

Given the JsonExample in the monocle project, I would like to create a lens where a set call will either replace a value in a key/value pair, or create the key/value pair if it doesnt already exist.
However this seems to represented with either an index (which can compose type safe) or an at, which does not type safe
//for replacing:
(jsObject composeOptional index("age") composePrism jsNumber).set(45)
//for creating:
(jsObject composeLens at("age")).set(JsNumber(45)) <- will accept any old json
Is what I am after possible?
Also could I extend it, such that if age was nested in another JsObject, for example:
val n = (jsObject composeOptional index("nested") composePrism
jsObject composeOptional index("age") composePrism jsNumber).set(45)
Where the key/value pair for "nested" didnt yet exist, that it would create the object at nested and then add the field
n(JsObject(Map.empty)) -> JsObject(Map("nested" -> JsObject("age" -> JsNumber(45)))

let's have a look at index and at signature for JsObject:
def at(field: String): Lens[JsObject, Option[Json]]
def index(field: String): Optional[JsObject, Json]
at is a Lens so its target ('Option[Json]') is always present. It means that we can add, delete and update the Json element at any field of a JsonObject.
import argonaut._, Argonaut._
import monocle.function._
(jObjectPrism composeLens at("name")).set(Some(jString("John")))(Json())
> res0: argonaut.Json = {"name":"John"}
(jObjectPrism composeLens at("name")).set(Some(jString("Robert")))(res0)
> res1: argonaut.Json = {"name":"Robert"}
(jObjectPrism composeLens at("name")).set(None)(res0)
> res2: argonaut.Json = {}
On the other hand, index is an Optional so it is target (Json) may or may not be there. It means that index can only update values but cannot add or delete.
(jObjectPrism composeLens index("name")).set(jString("Robert"))(Json())
> res3: argonaut.Json = {}
(jObjectPrism composeLens index("name")).set(jString("Robert"))(res0)
> res4: argonaut.Json = {"name":"Robert"}
So to come back to your original question, if you want to add or update value at a particular field, you need to use at and wrap the Json in a Some (see res1), it will overwrite or create the Json at that field.

At the moment there is very specific behaviour in the library.
When you composing Optional with some Iso or Prism it's downgrading right hand side argument to POptional and that's trouble.
Iso[A,B] and Prism[A,B] differs from Lens[A,B] and Optional[A,B] in sense that reverseGet can create element of A totally from B while set needs original value of A
So when for Optional and Lens it's totally legit that you can not modify part of the value, while not having this value in orignal Map or JsObject, for Iso and Prism you can define another behaviour.
And while waiting for the issue to be discussed, you can use following workaround
implicit class POptStrictComposition[S, T, A, B](self: POptional[S, T, A, B]) {
def sComposePrism[C, D](other: PPrism[A, B, C, D]) = new POptional[S, T, C, D] {
def getOrModify(s: S): T \/ C =
self.getOrModify(s).flatMap(a => other.getOrModify(a).bimap(self.set(_)(s), identity))
def set(d: D): S => T =
self.set(other.reverseGet(d))
def getOption(s: S): Option[C] =
self.getOption(s) flatMap other.getOption
def modifyF[F[_] : Applicative](f: C => F[D])(s: S): F[T] =
self.modifyF(other.modifyF(f))(s)
def modify(f: C => D): S => T =
self.modify(other.modify(f))
}
def ^!<-?[C, D](o: PPrism[A, B, C, D]) = sComposePrism(o)
def sComposeIso[C, D](other: PIso[A, B, C, D]) = sComposePrism(other.asPrism)
def ^!<->[C, D](o: PIso[A, B, C, D]) = sComposeIso(o)
}
now you can try to change your code to
(jsObject composeOptional index("age") sComposePrism jsNumber).set(45)
and report if it helped

Related

Scala: Creating Options from Seq/Tuple and add to a Sequence

Edit:
Suppose I have a Seq:
Seq(Some("Earth"),Some("Mars"))
I need to add few more elements at start of this sequence. Values to be added are generated based on an Option value.
So I try to do as:
val o = ....//Option calculated here
Seq(o.map(myFunction(_)),"Earth","Mars")
def myFunction(s: String) = s match {
case "x" => Seq(Some("Jupiter"), Some("Venus"))
case "y" => Seq(Some("PLuto"), Some("Mercury"))
}
But map would give me Some(Seq(.....)).
For this kind of problem I recommend checking the Scaladoc and following a technique called type-tetris.
You need this:
def prependIfDefined(data: Option[A], previousElements: Seq[Option[B]]): Seq[Option[B]] =
data.fold(ifEmpty = Seq.empty[Option[B]])(getNewData) ++ previousElements
def getNewData(a: A): Seq[Option[B]] = ???

Scala Add elements to set only by checking specific fields of a case class

I have a case class
case class ApiCall(a: String, b: String, c: String, d: String, e: String, f: String)
I also have a mutable Set: private var apiCalls: mutable.Set[ApiCall] = mutable.Set[ApiCall]()
The problem is, I may get the following ApiCall elements:
ApiCall(a1, b1, c1, d1, e1, f1)
ApiCall(a1, b1, c1, d2, e2, f2)
I have to add elements to the set only if a unique combination of (a, b, c) of the case class ApiCall doesn't already exist. I cannot modify the case class itself because it is being used in multiple places.
Is it possible to add case class elements to the set only by looking at certain fields, and not all?
You might want to use Map instead of Set in your case:
val apiCalls = mutable.Map[(String, String, String), ApiCall]()
Also values are replaced for matching keys inside Map, you might need a separate method to update the API calls map:
def updateApiCalls(call: ApiCall): Unit = {
apiCalls.getOrElseUpdate((call.a, call.b, call.c), call)
()
}
I kind of solved it using a small workaround:
private var _apiCalls: mutable.Set[ApiCall] = mutable.Set[ApiCall]() is my Set of ApiCalls
I wrote a function which will add to set only if there's no 3 part key (a, b, c) already existing in the Set:
def addApiCall(e: X): Unit = {
val m = _x.find(m => m.a == e.a && m.b == e.b && m.c == e.c)
if (m.isEmpty)
_x += e
}
I'm not going to have too many elements in the Set either, so I found this approach easier to handle for me..

Acquiring 2 implicits for scalacheck function

I am using scalacheck and am in the middle of a generic-programming soup right now. The official guide shows this example:
def matrix[T](g: Gen[T]): Gen[Seq[Seq[T]]] = Gen.sized { size =>
val side = scala.math.sqrt(size).asInstanceOf[Int]
Gen.listOfN(side, Gen.listOfN(side, g))
}
Meanwhile, for my test I require a matrix of type Array[Array[T]]. I tried with the following function:
def matrix[T](g: Gen[T]): Gen[Array[Array[T]]] = Gen.sized { size =>
val side = scala.math.sqrt(size).asInstanceOf[Int]
val g1 = Gen.containerOfN[Array, T](side, g)
Gen.containerOfN[Array, Array[T]](side, g1)
}
Here, I run into trouble. The compiler says:
Multiple markers at this line
- not enough arguments for method containerOfN: (implicit b: org.scalacheck.util.Buildable[T,Array])org.scalacheck.Gen[Array[T]].
Unspecified value parameter b.
- could not find implicit value for parameter b: org.scalacheck.util.Buildable[T,Array]
- could not find implicit value for parameter b: org.scalacheck.util.Buildable[T,Array]
- not enough arguments for method containerOfN: (implicit b: org.scalacheck.util.Buildable[T,Array])org.scalacheck.Gen[Array[T]].
Unspecified value parameter b.
I understand that stuff like this is usually remedied by adding implicit parameters to the function, however, i havent made this work yet.
I usually encounter this error when building generic arrays, as an example:
def build[T](n:Int)(implicit m:ClassManifest[T]) = Array.ofDim[T](n)
but, I am afraid I don't fully understand what is happening or why this is needed.
Can someone explain how to make the correct matrix-function along with an example of usage in scalacheck? A thorough explanation of the details about building sequences with implicit class manifests would be very welcome!
edit
import org.scalacheck.util.Buildable._
def matrix[T](g: Gen[T])(implicit b: Buildable[T, Array]): Gen[Array[Array[T]]] = Gen.sized { size =>
val side = scala.math.sqrt(size).asInstanceOf[Int]
val g1 = Gen.containerOfN[Array, T](side, g)
Gen.containerOfN[Array, Array[T]](side, g1)
}
Still doesn't work. Need implicit for Buildable[Array[T],Array]... Don't know how to get this because I can only add 1 implicit argument :/
You're almost there. The important part of the error is could not find implicit value for parameter b: org.scalacheck.util.Buildable[T,Array]
Looking at the method definition of containerOfN
def containerOfN[C[_],T](n: Int, g: Gen[T])(implicit b: Buildable[T,C]): Gen[C[T]] = ...
So, there's your missing argument. You need an implicit argument of type Buildable[T,Array]. Following through to where Buildable is defined in the scalacheck sources, I found that there was an object (org.scalacheck.util.Buildable) that provides implicits that provide Buildables for the common collection types which includes Array. So all you need to bring that into scope. You can do this with:
import org.scalacheck.util.Buildable._
def matrix[T](g: Gen[T]): Gen[Array[Array[T]]] = Gen.sized { size =>
val bT = implicitly[Buildable[T, Array]]
val bArrayT = implicitly[Buildable[Array[T], Array]]
val side = scala.math.sqrt(size).asInstanceOf[Int]
val g1 = Gen.containerOfN[Array, T](side, g)
Gen.containerOfN[Array, Array[T]](side, g1)
}
Or
import org.scalacheck.util.Buildable._
def matrix[T](g: Gen[T])(implicit bT: Buildable[T, Array], bArrayT: Buildable[Array[T], Array]): Gen[Array[Array[T]]] = Gen.sized { size =>
...
}
The particular implicit you need in org.scalacheck.util.Buildable is:
implicit def buildableArray[T](implicit cm: ClassManifest[T]) =
new Buildable[T,Array] {
def builder = mutable.ArrayBuilder.make[T]
}

Scala: Why does SortedMap's mapValues returns a Map and not a SortedMap?

I'm new to Scala.
I'm using SortedMap in my code, and I wanted to use mapValues to create a new map with some transformation on the values.
Instead of returning a new SortedMap, the mapValues function returns a new Map, which I then have to convert to a SortedMap.
For example
val my_map = SortedMap(1 -> "one", 0 -> "zero", 2 -> "two")
val new_map = my_map.mapValues(name => name.toUpperCase)
// returns scala.collection.immutable.Map[Int,java.lang.String] = Map(0 -> ZERO, 1 -> ONE, 2 -> TWO)
val sorted_new_map = SortedMap(new_map.toArray:_ *)
This looks inefficient - the last convertion probably sorts the keys again, or at least verify that they are sorted.
I could use the normal map function which operates both on the keys and the values, and deliberately not change the keys in my transformation function. This looks inefficient too, since the implementation of Map probably assume that the transformation may change the order of the keys (like in the case: my_map.map(tup => (-tup._1, tup._2)) - so it probably "re-sorts" them too.
Is anyone familiar with the internal implementations of Map and SortedMap, and could tell me if my assumptions are correct? Can the compiler recognize automatically that the keys have not been reordered? Is there an internal reason for why mapValues should not return a SortedMap? Is there a better way to transform the map's values without loosing the order of the keys?
Thanks
You've stumbled upon a tricky feature of Scala's Map implementation. The catch that you are missing is that mapValues does not actually return a new Map: it returns a view of a Map. In other words, it wraps your original map in such a way that whenever you access a value it will compute .toUpperCase before returning the value to you.
The upside to this behavior is that Scala won't compute the function for values that aren't accessed, and it won't spend time copying all the data into a new Map. The downside is that the function is re-computed every time that value is accessed. So you might end up doing extra computation if you access the same values many times.
So why does SortedMap not return a SortedMap? Because it's actually returning a Map-wrapper. The underlying Map, then one that is wrapped, is still a SortedMap, so if you were to iterate through, it would still be in sorted order. You and I know that, but the type-checker doesn't. It certainly seems like they could have written it in such a way that it still maintains the SortedMap trait, but they didn't.
You can see in the code that it's not returning a SortedMap, but that the iteration behavior is still going to be sorted:
// from MapLike
override def mapValues[C](f: B => C): Map[A, C] = new DefaultMap[A, C] {
def iterator = for ((k, v) <- self.iterator) yield (k, f(v))
...
The solution to your problem is the same as the solution to getting around the view issue: use .map{ case (k,v) => (k,f(v)) }, as you mentioned in your question.
If you really want that convenience method though, you can do what I do, and write you own, better, version of mapValues:
class EnrichedWithMapVals[T, U, Repr <: GenTraversable[(T, U)]](self: GenTraversableLike[(T, U), Repr]) {
/**
* In a collection of pairs, map a function over the second item of each
* pair. Ensures that the map is computed at call-time, and not returned
* as a view as 'Map.mapValues' would do.
*
* #param f function to map over the second item of each pair
* #return a collection of pairs
*/
def mapVals[R, That](f: U => R)(implicit bf: CanBuildFrom[Repr, (T, R), That]) = {
val b = bf(self.asInstanceOf[Repr])
b.sizeHint(self.size)
for ((k, v) <- self) b += k -> f(v)
b.result
}
}
implicit def enrichWithMapVals[T, U, Repr <: GenTraversable[(T, U)]](self: GenTraversableLike[(T, U), Repr]): EnrichedWithMapVals[T, U, Repr] =
new EnrichedWithMapVals(self)
Now when you call mapVals on a SortedMap you get back a non-view SortedMap:
scala> val m3 = m1.mapVals(_ + 1)
m3: SortedMap[String,Int] = Map(aardvark -> 2, cow -> 6, dog -> 10)
It actually works on any collection of pairs, not just Map implementations:
scala> List(('a,1),('b,2),('c,3)).mapVals(_+1)
res8: List[(Symbol, Int)] = List(('a,2), ('b,3), ('c,4))

Nearest keys in a SortedMap

Given a key k in a SortedMap, how can I efficiently find the largest key m that is less than or equal to k, and also the smallest key n that is greater than or equal to k. Thank you.
Looking at the source code for 2.9.0, the following code seems about to be the best you can do
def getLessOrEqual[A,B](sm: SortedMap[A,B], bound: A): B = {
val key = sm.to(x).lastKey
sm(key)
}
I don't know exactly how the splitting of the RedBlack tree works, but I guess it's something like a O(log n) traversal of the tree/construction of new elements and then a balancing, presumable also O(log n). Then you need to go down the new tree again to get the last key. Unfortunately you can't retrieve the value in the same go. So you have to go down again to fetch the value.
In addition the lastKey might throw an exception and there is no similar method that returns an Option.
I'm waiting for corrections.
Edit and personal comment
The SortedMap area of the std lib seems to be a bit neglected. I'm also missing a mutable SortedMap. And looking through the sources, I noticed that there are some important methods missing (like the one the OP asks for or the ones pointed out in my answer) and also some have bad implementation, like 'last' which is defined by TraversableLike and goes through the complete tree from first to last to obtain the last element.
Edit 2
Now the question is reformulated my answer is not valid anymore (well it wasn't before anyway). I think you have to do the thing I'm describing twice for lessOrEqual and greaterOrEqual. Well you can take a shortcut if you find the equal element.
Scala's SortedSet trait has no method that will give you the closest element to some other element.
It is presently implemented with TreeSet, which is based on RedBlack. The RedBlack tree is not visible through methods on TreeSet, but the protected method tree is protected. Unfortunately, it is basically useless. You'd have to override methods returning TreeSet to return your subclass, but most of them are based on newSet, which is private.
So, in the end, you'd have to duplicate most of TreeSet. On the other hand, it isn't all that much code.
Once you have access to RedBlack, you'd have to implement something similar to RedBlack.Tree's lookup, so you'd have O(logn) performance. That's actually the same complexity of range, though it would certainly do less work.
Alternatively, you'd make a zipper for the tree, so that you could actually navigate through the set in constant time. It would be a lot more work, of course.
Using Scala 2.11.7, the following will give what you want:
scala> val set = SortedSet('a', 'f', 'j', 'z')
set: scala.collection.SortedSet[Char] = TreeSet(a, f, j, z)
scala> val beforeH = set.to('h').last
beforeH: Char = f
scala> val afterH = set.from('h').head
afterH: Char = j
Generally you should use lastOption and headOption as the specified elements may not exist. If you are looking to squeeze a little more efficiency out, you can try replacing from(...).head with keysIteratorFrom(...).head
Sadly, the Scala library only allows to make this type of query efficiently:
and also the smallest key n that is greater than or equal to k.
val n = TreeMap(...).keysIteratorFrom(k).next
You can hack this by keeping two structures, one with normal keys, and one with negated keys. Then you can use the other structure to make the second type of query.
val n = - TreeMap(...).keysIteratorFrom(-k).next
Looks like I should file a ticket to add 'fromIterator' and 'toIterator' methods to 'Sorted' trait.
Well, one option is certainly using java.util.TreeMap.
It has lowerKey and higherKey methods, which do excatly what you want.
I had a similar problem: I wanted to find the closest element to a given key in a SortedMap. I remember the answer to this question being, "You have to hack TreeSet," so when I had to implement it for a project, I found a way to wrap TreeSet without getting into its internals.
I didn't see jazmit's answer, which more closely answers the original poster's question with minimum fuss (two method calls). However, those method calls do more work than needed for this application (multiple tree traversals), and my solution provides lots of hooks where other users can modify it to their own needs.
Here it is:
import scala.collection.immutable.TreeSet
import scala.collection.SortedMap
// generalize the idea of an Ordering to metric sets
trait MetricOrdering[T] extends Ordering[T] {
def distance(x: T, y: T): Double
def compare(x: T, y: T) = {
val d = distance(x, y)
if (d > 0.0) 1
else if (d < 0.0) -1
else 0
}
}
class MetricSortedMap[A, B]
(elems: (A, B)*)
(implicit val ordering: MetricOrdering[A])
extends SortedMap[A, B] {
// while TreeSet searches for an element, keep track of the best it finds
// with *thread-safe* mutable state, of course
private val best = new java.lang.ThreadLocal[(Double, A, B)]
best.set((-1.0, null.asInstanceOf[A], null.asInstanceOf[B]))
private val ord = new MetricOrdering[(A, B)] {
def distance(x: (A, B), y: (A, B)) = {
val diff = ordering.distance(x._1, y._1)
val absdiff = Math.abs(diff)
// the "to" position is a key-null pair; the object of interest
// is the other one
if (absdiff < best.get._1)
(x, y) match {
// in practice, TreeSet always picks this first case, but that's
// insider knowledge
case ((to, null), (pos, obj)) =>
best.set((absdiff, pos, obj))
case ((pos, obj), (to, null)) =>
best.set((absdiff, pos, obj))
case _ =>
}
diff
}
}
// use a TreeSet as a backing (not TreeMap because we need to get
// the whole pair back when we query it)
private val treeSet = TreeSet[(A, B)](elems: _*)(ord)
// find the closest key and return:
// (distance to key, the key, its associated value)
def closest(to: A): (Double, A, B) = {
treeSet.headOption match {
case Some((pos, obj)) =>
best.set((ordering.distance(to, pos), pos, obj))
case None =>
throw new java.util.NoSuchElementException(
"SortedMap has no elements, and hence no closest element")
}
treeSet((to, null.asInstanceOf[B])) // called for side effects
best.get
}
// satisfy the contract (or throw UnsupportedOperationException)
def +[B1 >: B](kv: (A, B1)): SortedMap[A, B1] =
new MetricSortedMap[A, B](
elems :+ (kv._1, kv._2.asInstanceOf[B]): _*)
def -(key: A): SortedMap[A, B] =
new MetricSortedMap[A, B](elems.filter(_._1 != key): _*)
def get(key: A): Option[B] = treeSet.find(_._1 == key).map(_._2)
def iterator: Iterator[(A, B)] = treeSet.iterator
def rangeImpl(from: Option[A], until: Option[A]): SortedMap[A, B] =
new MetricSortedMap[A, B](treeSet.rangeImpl(
from.map((_, null.asInstanceOf[B])),
until.map((_, null.asInstanceOf[B]))).toSeq: _*)
}
// test it with A = Double
implicit val doubleOrdering =
new MetricOrdering[Double] {
def distance(x: Double, y: Double) = x - y
}
// and B = String
val stuff = new MetricSortedMap[Double, String](
3.3 -> "three",
1.1 -> "one",
5.5 -> "five",
4.4 -> "four",
2.2 -> "two")
println(stuff.iterator.toList)
println(stuff.closest(1.5))
println(stuff.closest(1000))
println(stuff.closest(-1000))
println(stuff.closest(3.3))
println(stuff.closest(3.4))
println(stuff.closest(3.2))
I've been doing:
val m = SortedMap(myMap.toSeq:_*)
val offsetMap = (m.toSeq zip m.keys.toSeq.drop(1)).map {
case ( (k,v),newKey) => (newKey,v)
}.toMap
When I want the results of my map off-set by one key. I'm also looking for a better way, preferably without storing an extra map.