Is it possible to combine two js.Dynamic objects - scala.js

Say I have
val obj1 = js.Dynamic.literal(attr1 = "x")
val obj2 = js.Dynamic.literal(attr2 = "y")
and I would like to combine obj1 and obj2 to yield and object of
{attr1 = "x", attr2 = "y"}
Does js.Dynamic have a method to yield such a combination?

There is no existing method doing that. In ES6 there is Object.assign, but it is not supported in IE.
I recommend re-implementing it in Scala.js:
def mergeJSObjects(objs: js.Dynamic*): js.Dynamic = {
val result = js.Dictionary.empty[Any]
for (source <- objs) {
for ((key, value) <- source.asInstanceOf[js.Dictionary[Any]])
result(key) = value
}
result.asInstanceOf[js.Dynamic]
}
And now you can simply do
mergeJSObjects(obj1, obj2)

I put the same question to the scala.js forums and was pointed to js.Object.assign

Related

What is a good implementation of generic 2D Array literals in scala3 based on tuples?

The following code is a proof-of-concept for an uncluttered way to declare 2D array literals in scala3. It's based on this answer to a related question, implemented for scala2:
https://stackoverflow.com/a/13863525/666886
The non-generic code below provides a clean Array literal declaration, but is inflexible w.r.t. type and dimension. It would be better to derive array dimensions from tuple arity and number of rows.
#!/usr/bin/env scala3
object Array2d {
def main(args: Array[String]): Unit = {
prettyPrintArray()
}
type X=Int
type Tuple26 = (X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X)
type Array2d = Array[Array[X]]
def apply(tuples: Tuple26 *): Array2d = {
for {
tupe <- tuples
row = for ( i <- tupe.toList ) yield i
} yield row.toArray
}.toArray
lazy val letterFrequencies = Array2d(
(46,615,763,839,1745,325,628,651,1011,128,573,1319,797,1123,884,726,49,1642,2241,1162,631,299,408,97,659,184),
(15,103,128,202,597,49,126,107,358,32,123,321,171,226,483,38,6,439,565,233,340,21,58,21,227,40),
(63,128,106,218,689,86,75,407,526,14,241,369,197,307,627,208,5,507,675,343,361,60,66,24,226,28),
(39,202,218,149,1257,108,183,175,634,35,140,407,212,418,692,194,10,596,763,272,409,97,184,52,348,46),
(745,597,689,1257,919,396,568,583,1214,111,504,1315,726,1083,1217,763,34,1876,2323,1223,662,437,455,199,655,187),
(25,49,86,108,396,118,75,72,295,11,68,258,58,129,248,26,7,299,415,208,205,14,50,20,151,24),
(28,126,75,183,568,75,99,141,424,24,55,324,146,416,476,129,3,423,536,206,326,48,66,4,244,28),
(51,107,407,175,583,72,141,53,399,21,145,282,198,238,488,174,6,342,715,454,285,44,158,14,207,23),
(11,358,526,634,1214,295,424,399,169,71,392,853,518,884,594,467,50,970,1471,853,335,246,215,104,323,130),
(28,32,14,35,111,11,24,21,71,2,31,41,28,52,98,26,1,45,112,33,75,9,12,1,38,3),
(73,123,241,140,504,68,55,145,392,31,61,247,102,300,376,152,8,338,744,184,240,21,86,2,224,20),
(319,321,369,407,1315,258,324,282,853,41,247,250,341,369,875,395,10,468,1237,491,566,173,201,61,463,50),
(97,171,197,212,726,58,146,198,518,28,102,341,102,257,555,195,6,429,769,273,401,33,61,40,260,44),
(123,226,307,418,1083,129,416,238,884,52,300,369,257,159,849,265,18,533,1027,537,499,113,220,45,374,65),
(84,483,627,692,1217,248,476,488,594,98,376,875,555,849,525,566,18,1096,1658,856,462,175,329,85,521,130),
(26,38,208,194,763,26,129,174,467,26,152,395,195,265,566,127,8,488,874,347,353,35,83,24,333,28),
(9,6,5,10,34,7,3,6,50,1,8,10,6,18,18,8,1,16,37,25,96,0,2,0,7,1),
(642,439,507,596,1876,299,423,342,970,45,338,468,429,533,1096,488,16,246,1461,789,673,189,234,52,491,77),
(241,565,675,763,2323,415,536,715,1471,112,744,1237,769,1027,1658,874,37,1461,717,1367,1088,221,484,86,598,105),
(162,233,343,272,1223,208,206,454,853,33,184,491,273,537,856,347,25,789,1367,254,548,72,202,49,384,52),
(31,340,361,409,662,205,326,285,335,75,240,566,401,499,462,353,96,673,1088,548,73,61,41,32,317,51),
(99,21,60,97,437,14,48,44,246,9,21,173,33,113,175,35,0,189,221,72,61,20,25,10,63,7),
(8,58,66,184,455,50,66,158,215,12,86,201,61,220,329,83,2,234,484,202,41,25,11,12,133,19),
(7,21,24,52,199,20,4,14,104,1,2,61,40,45,85,24,0,52,86,49,32,10,12,1,34,2),
(59,227,226,348,655,151,244,207,323,38,224,463,260,374,521,333,7,491,598,384,317,63,133,34,43,54),
(84,40,28,46,187,24,28,23,130,3,20,50,44,65,130,28,1,77,105,52,51,7,19,2,54,42),
)
def prettyPrintArray(): Unit = {
val alphabet = "abcdefghijklmnopqrstuvwxyz"
val toprow = alphabet.map { "%4s".format(_) }.mkString(",")
printf("// %s\n", toprow)
for (a <- alphabet){
val freqs: Seq[String] = for {
b <- alphabet
(x,y) = (alphabet.indexOf(a), alphabet.indexOf(b))
freq = letterFrequencies(x)(y)
} yield "%4d".format(freq)
printf("/* %s */ (%s),\n", a, freqs.mkString(","))
}
}
}
The following works, but the same thing based on the new scala 3 type IArray would be nice to have as well.
#!/usr/bin/env scala3
// Example of declaring a generic NxN Array[Array[_]] in scala3 by using
// scala 3 tuple extensions, for a concise declaration of a 2d array.
// An improved version would avoid the deprecated ClassTag reference,
// and the use of `asInstanceOf[List[T]]`.
// Perhaps `shapeless 3` would help in this regard?
object Array2d {
def main(args: Array[String]): Unit = {
prettyPrintArray()
}
// it would be nice if a Tuple with a single homogeneous field Type
// automatically converted to an Array of that type, rather
// than an Array[AnyRef]
import scala.reflect.ClassTag
def apply[T:ClassTag](tuples: Tuple *): Array[Array[T]] = {
val rows: Seq[Array[T]] = for {
tupe <- tuples
row: Seq[T] = tupe.toList.asInstanceOf[List[T]]
arr: Array[T] = (for (i:T <- row) yield i).toArray
} yield arr
rows.toArray
}
// this table is useful for analyzing nytimes Wordle guesses.
lazy final val letterFrequencies = Array2d[Int](
(46,615,763,839,1745,325,628,651,1011,128,573,1319,797,1123,884,726,49,1642,2241,1162,631,299,408,97,659,184),
(15,103,128,202,597,49,126,107,358,32,123,321,171,226,483,38,6,439,565,233,340,21,58,21,227,40),
(63,128,106,218,689,86,75,407,526,14,241,369,197,307,627,208,5,507,675,343,361,60,66,24,226,28),
(39,202,218,149,1257,108,183,175,634,35,140,407,212,418,692,194,10,596,763,272,409,97,184,52,348,46),
(745,597,689,1257,919,396,568,583,1214,111,504,1315,726,1083,1217,763,34,1876,2323,1223,662,437,455,199,655,187),
(25,49,86,108,396,118,75,72,295,11,68,258,58,129,248,26,7,299,415,208,205,14,50,20,151,24),
(28,126,75,183,568,75,99,141,424,24,55,324,146,416,476,129,3,423,536,206,326,48,66,4,244,28),
(51,107,407,175,583,72,141,53,399,21,145,282,198,238,488,174,6,342,715,454,285,44,158,14,207,23),
(11,358,526,634,1214,295,424,399,169,71,392,853,518,884,594,467,50,970,1471,853,335,246,215,104,323,130),
(28,32,14,35,111,11,24,21,71,2,31,41,28,52,98,26,1,45,112,33,75,9,12,1,38,3),
(73,123,241,140,504,68,55,145,392,31,61,247,102,300,376,152,8,338,744,184,240,21,86,2,224,20),
(319,321,369,407,1315,258,324,282,853,41,247,250,341,369,875,395,10,468,1237,491,566,173,201,61,463,50),
(97,171,197,212,726,58,146,198,518,28,102,341,102,257,555,195,6,429,769,273,401,33,61,40,260,44),
(123,226,307,418,1083,129,416,238,884,52,300,369,257,159,849,265,18,533,1027,537,499,113,220,45,374,65),
(84,483,627,692,1217,248,476,488,594,98,376,875,555,849,525,566,18,1096,1658,856,462,175,329,85,521,130),
(26,38,208,194,763,26,129,174,467,26,152,395,195,265,566,127,8,488,874,347,353,35,83,24,333,28),
(9,6,5,10,34,7,3,6,50,1,8,10,6,18,18,8,1,16,37,25,96,0,2,0,7,1),
(642,439,507,596,1876,299,423,342,970,45,338,468,429,533,1096,488,16,246,1461,789,673,189,234,52,491,77),
(241,565,675,763,2323,415,536,715,1471,112,744,1237,769,1027,1658,874,37,1461,717,1367,1088,221,484,86,598,105),
(162,233,343,272,1223,208,206,454,853,33,184,491,273,537,856,347,25,789,1367,254,548,72,202,49,384,52),
(31,340,361,409,662,205,326,285,335,75,240,566,401,499,462,353,96,673,1088,548,73,61,41,32,317,51),
(99,21,60,97,437,14,48,44,246,9,21,173,33,113,175,35,0,189,221,72,61,20,25,10,63,7),
(8,58,66,184,455,50,66,158,215,12,86,201,61,220,329,83,2,234,484,202,41,25,11,12,133,19),
(7,21,24,52,199,20,4,14,104,1,2,61,40,45,85,24,0,52,86,49,32,10,12,1,34,2),
(59,227,226,348,655,151,244,207,323,38,224,463,260,374,521,333,7,491,598,384,317,63,133,34,43,54),
(84,40,28,46,187,24,28,23,130,3,20,50,44,65,130,28,1,77,105,52,51,7,19,2,54,42),
)
def prettyPrintArray(): Unit = {
val alphabet = "abcdefghijklmnopqrstuvwxyz"
val toprow = alphabet.map { "%4s".format(_) }.mkString(",")
printf("// %s\n", toprow)
for (a <- alphabet){
val freqs: Seq[String] = for {
b <- alphabet
(x,y) = (alphabet.indexOf(a), alphabet.indexOf(b))
freq = letterFrequencies(x)(y)
} yield "%4d".format(freq)
printf("/* %s */ (%s),\n", a, freqs.mkString(","))
}
}
}

foreach() in scala returns a Seq. How to get to the item?

I'm trying to add every element of a list to a MutableSet.
Thrift:
Obj {
list<Tag> myList;
}
enum Tag {
...
}
Scala:
val uniqueTags = Set[Tag]()
// obj is of type Obj defined in the thrift above
obj.flatMap(_.myList).foreach(uniqueTags += _)
However the compiler says that I'm trying to add Seq[Tag] instead of Tag. How to get to the element represented by Seq?
Also I'm sure there is another way to initialize the Set with the list directly. I tried obj.flatMap(_.myList).toSet() and Set[Tag](obj.flatMap(_.myList))) but none worked.
you don't need to iterate to find unique, scala does that for you with toSet
Example using toSet:
scala> case class Obj(myList: List[String])
defined class Obj
scala> val obj = Option(Obj(myList = List("metal", "rock", "prog", "pop", "pop")))
obj: Option[Obj] = Some(Obj(List(metal, rock, prog, pop, pop)))
Now, to get unique tags,
scala> val uniqueTags = obj.map(_.myList).getOrElse(List.empty[String]).toSet
uniqueTags: scala.collection.immutable.Set[String] = Set(metal, rock, prog, pop)
foreach are not recommended to mutate stuffs in fp world. The other approach would be to use accumulator pattern - foldLeft,
scala> import scala.collection.immutable.Set
import scala.collection.immutable.Set
scala> obj.map(_.myList).getOrElse(List.empty[String]).foldLeft(Set[String]()){ case (s, e) => s + e }
res15: scala.collection.immutable.Set[String] = Set(metal, rock, prog, pop)
The mutable approach is to use forach as you are doing, (not recommended)
scala> val uniqueTags = scala.collection.mutable.Set[String]()
uniqueTags: scala.collection.mutable.Set[String] = HashSet()
scala> obj.map(_.myList).getOrElse(List.empty[String]).foreach { elem => uniqueTags += elem }
scala> uniqueTags
res13: scala.collection.mutable.Set[String] = HashSet(metal, rock, pop, prog)
I an not sure what is Obj. But in you case: obj.flatMap(_.myList) will give a list of Tag. I think the correct way is:
obj.flatMap(_.myList).foreach(uniqueTags += _)
I think you can use mutable in Scala. Its not a big deal. According to your obj, you may use different way to append the element into set
case class Obj(myList: List[String])
val obj = Obj(List("1", "2", "3"))
// first example when your obj is a single Obj
val uniqueTags = mutable.Set[String]()
// obj is of type Obj defined in the thrift above
obj.myList.foreach(uniqueTags += _)
printf(uniqueTags.toString()) // give you Set(1, 2, 3)
// second example when your obj is a list of Obj
val obj2 = List(obj, obj, obj)
val uniqueTags2 = mutable.Set[String]()
obj2.flatMap(_.myList).foreach(uniqueTags2 += _)
printf(uniqueTags2.toString()) // give you Set(1, 2, 3) also

Concatenate multiple list

I would like to know how to concatenate several list using a loop. Here is an example of what I'm trying to do:
object MyObj {
var objs = Set (
MyObj("MyObj1", anotherObjList),
MyObj("MyObj2", anotherObjList)
)
val list = List.empty[AnotherObj]
def findAll = for (obj <- objs) List.concat(list, obj.anotherObjList)
}
I would like the function findAll to concatenate lists from object of the set objs.
Try this:
objs.flatMap(_.anotherObjList)
It doesn't use a for, but that's probably the most concise and readable way to do it in Scala.
Use reduce
Something like this:
objs.reduce((a,b) => a.anotherObjList ++ b.anotherObjList)
Or if the Set can be empty, use foldLeft:
objs.foldLeft(List.empty[AnotherObj],(a,b) => a.anotherObjList ++ b.anotherObjList)
In your code example, you are using a val list which cannot be reassigned. When you do List.concat(list, obj.anotherObjList) you are creating a new list that concats the empty list to the current obj's anotherObjList, but the result is never used, so list will still be empty after the execution of the for-loop.
If you really need to use a imperative for-loop, either use an immutable collection and assign it to a var which can be reassigned from the for-loop's body or use a mutable collection:
object MyObj {
var objs = Set(
MyObj("MyObj1", anotherObjList),
MyObj("MyObj1", anotherObjList),
)
def findAllLoop1 = {
var list = List.empty
for (obj <- objs) list = list ++ obj.anotherObjList
list
}
def findAllLoop2 = {
val buf = collection.mutable.ListBuffer[Int]() // replace Int with your actual type of elements
for (obj <- objs) buf ++= obj.anotherObjList
}
}
But If you don't have to use a imperative loop for some reason, I would strongly suggest to use a functional alternative:
object MyObj {
var objs = Set(
MyObj("MyObj1", anotherObjList),
MyObj("MyObj1", anotherObjList),
)
def findAll =
objs.flatMap(_.anotherObjList) // if Set as return type is okay
def findAll: List[Int] =
objs.flatMap(_.anotherObjList)(collection.breakOut) // if elements are of type Int and you want to get a List at the end, not a Set
}

Multiple if else statements to get non-empty value from a map in Scala

I have a string to string map, and its value can be an empty string. I want to assign a non-empty value to a variable to use it somewhere. Is there a better way to write this in Scala?
import scala.collection.mutable
var keyvalue = mutable.Map.empty[String, String]
keyvalue += ("key1" -> "value1")
var myvalue = ""
if (keyvalue.get("key1").isDefined &&
keyvalue("key1").length > 0) {
myvalue = keyvalue("key1")
}
else if (keyvalue.get("key2").isDefined &&
keyvalue("key2").length > 0) {
myvalue = keyvalue("key2")
}
else if (keyvalue.get("key3").isDefined &&
keyvalue("key3").length > 0) {
myvalue = keyvalue("key3")
}
A more idiomatic way would be to use filter to check the length of the string contained in the Option, then orElse and getOrElse to assign to a val. A crude example:
def getKey(key: String): Option[String] = keyvalue.get(key).filter(_.length > 0)
val myvalue: String = getKey("key1")
.orElse(getKey("key2"))
.orElse(getKey("key3"))
.getOrElse("")
Here's a similar way to do it with an arbitrary list of fallback keys. Using a view and collectFirst, we will only evaluate keyvalue.get for only as many times as we need to (or all, if there are no matches).
val myvalue: String = List("key1", "key2", "key3").view
.map(keyvalue.get)
.collectFirst { case Some(value) if(value.length > 0) => value }
.getOrElse("")
Mmm, it seems it took me too long to devise a generic solution and other answer was accepted, but here it goes:
def getOrTryAgain(map: mutable.Map[String, String], keys: List[String]): Option[String] =
{
if(keys.isEmpty)
None
else
map.get(keys.head).filter(_.length > 0).orElse(getOrTryAgain(map, keys.tail))
}
val myvalue2 = getOrTryAgain(keyvalue, List("key1", "key2", "key3"))
This one you can use to check for as many keys as you want.

How to mimic Scala's Map/Array assignment syntax in my own class

Following is a simple map entry assignment:
scala> var myl = mutable.Map[String,String]()
myl: scala.collection.mutable.Map[String,String] = Map()
myl("abc") = "123"
I would like to mimic that assignment structure in my own class that works with mutable Tuple's. Now, "getting" a value from a Map is achieved via the "apply" method:
e.g mutable.HashMap:
override def apply(key: A): B = {
val result = findEntry(key)
if (result eq null) default(key)
else result.value
}
I was not however able to find how the map entry is "set" via myMap("myKey") = "myval". A pointer to the Scala source code to do that would be appreciated. Thanks.
The method you want to implement is called update() and takes two parameters, one for the input value passed in parentheses and the other for the assigned value.
class QueryParams {
var params = ""
def update(name: String, value: String) { params += s"$name=$value&" }
}
For example:
val p = new QueryParams()
p("q") = "SFO"
p("start") = "10"
p("rows") = "10"
p.params