Extending an object with properties in Coffeescript? - coffeescript

I have an array of string literals and I want to loop over them, parse them as JSON and add a property to the resulting object. I want to assign the result of this to a variable.
And I want it to look pretty :)
Right now I am doing this:
strings = ['{"a": "A", "b": "B"}', '{"c": "C", "d": "D"}']
objects = for string in strings
obj = JSON.parse string
obj.e = 'E'
obj
this gives me an array looking like this:
[{ a: 'A', b: 'B', e:'E' }, { c: 'C', d: 'D', e:'E' }]
Now this works but it looks a bit ugly. I guess what I'd like is something like http://documentcloud.github.com/underscore/#extend (but I don't want to include underscore just for that one method)
I found this issue: https://github.com/jashkenas/coffee-script/issues/880
and this pullrequest: https://github.com/jashkenas/coffee-script/pull/2177
but the pullrequest is open and the issue is closed so I assume there's at least no operator for doing this?
But when writing that code I can't help thinking that there's got to be a better way, so any tips would be welcome.

Here is some reference: http://coffeescript.org/documentation/docs/helpers.html
extend = exports.extend = (object, properties) ->
for key, val of properties
object[key] = val
object
strings = ['{"a": "A", "b": "B"}', '{"c": "C", "d": "D"}']
objects = for string in strings
extend JSON.parse(string), e: 'E'

Related

What is the difference between the + operator and std.mergePatch in Jsonnet?

Jsonnet's std.mergePatch implements RFC7396, but in my naive testing I didn't find a different between the way it behaved and the + operator; e.g. the + operator respects x+ syntax. std.mergePatch is implemented in Jsonnet itself, which seems to imply that it is different than the + operator, which I'm assuming is a builtin.
What is different about the semantics of these two ways of merging?
Jsonnet's + and std.mergePatch are completely different operations. The + operator operates only on a single level and std.mergePatch traverses the object recursively and merges the nested objects. It's easiest to explain with an example:
local foo = { a: {b1: {c1: 42}}},
bar = { a: {b2: {c2: 2}}};
foo + bar
Output:
{
"a": {
"b2": {
"c2": 2
}
}
}
Note that the bar.a completely replaced foo.a. With + all fields in the second object override the fields in the first object. Compare that with the result of using std.mergePatch(foo, bar).
{
"a": {
"b1": {
"c1": 42
},
"b2": {
"c2": 2
}
}
}
Since both foo and bar have a field a, is is merged and the final results contains both b1 and b2.
So to reiterate, + is a "flat" operation which overrides the fields of the first object with the fields of the second object.
This is not the end of the story, though. You mentioned field+: value syntax and I will try to explain what it really does. In Jsonnet + is not just overwriting, but inheritance in OO sense. It creates an object which is the result of the second object inheriting from the first one. It's a bit exotic to have an operator for that – in all mainstream languages such relationships are statically defined. In Jsonnet, when you do foo + bar, the bar object has access to stuff from foo through super:
{ a: 2 } + { a_plus_1: super.a + 1}
This results in:
{
"a": 2,
"a_plus_1": 3
}
You can use this feature to merge the fields deeper inside:
{ a: {b: {c1: 1}, d: 1}} +
{ a: super.a + {b: {c2: 2} } }
Resulting in:
{
"a": {
"b": {
"c2": 2
},
"d": 1
}
}
This is a bit repetitive, though (it would be annoying if the field name was longer). So we have a nice syntax sugar for that:
{ a: {b: {c1: 1} , d: 1}} +
{ a+: {b: {c2: 2}} }
Please note that in these examples we only did the merging for one particular field we chose. We still replaced the value of a.b. This is much more flexible, because in many cases you can't just naively merge all stuff inside (sometimes a nested object is "atomic" and should be replaced completely).
The version in +: works in the same way as the version with super. The subtle difference is that +: actually translates to something like if field in super then super.field + val else val, so it also returns the same value when super is not provided at all or doesn't have this particular field. For example {a +: {b: 42}} evaluates just fine to {a: { b: 42 }}.
Mandatory sermon: while + is very powerful, please don't abuse it. Consider using using functions instead of inheritance when you need to parameterize something.

Gatling: transform findAll to sorted list

I'm new to scala and Gatling. I'm trying to transform the result of findAll into a sorted list and then return a String representation of the sorted list. I can't seem to do this with the following code:
http(requestTitle)
.post(serverUrl)
.body(ElFileBody(sendMessageFile))
.header("correlation-id", correlationId)
.check(status.is(200),
jsonPath("$.data.sendMessage.targetedRecipients").findAll.transform(recipients => {
println("recipients class: " + recipients.getClass)
var mutable = scala.collection.mutable.ListBuffer(recipients: _*)
var sortedRecipients = mutable.sortWith(_ < _)
println("users sorted "+ usersSorted)
usersSorted.mkString(",")
}).is(expectedMessageRecipients))
Recipients is of type scala.collection.immutable.Vector.
I thought I would be able to convert the immutable collection into a mutable collection using scala.collection.mutable.ListBuffer. Any help would be appreciated, thanks.
I don't think your problem is immutability, it's JSON parsing vs Gatling's .find and .findAll methods.
I'm going to make a guess that your response looks something like...
{"data":{"sendMessage":{"targetedRecipients":[1,4,2,3]}}}
in which case Gatling's .findAll method will return a vector (it always does if it finds something), but it will only have one element which will be "[1,4,2,3]" - ie: a string representing json data, so sorting the collection of a single element naturally achieves nothing. To get .findAll to behave like you seem to be expecting, you would need a response something like...
{"data":
{"sendMessage":
{"targetedRecipients":
[{"recipientId":1},
{"recipientId":4},
{"recipientId":2},
{"recipientId":3}]
}}}
which you could use .jsonPath("$..recipientId").findAllto turn into a Vector[String] of the Ids.
So assuming you are indeed just getting a single string representation of an array of values, you could use a straight transform to generate an array and sort (as you tried in your example)
Here's a working version
val data = """{"data":{"sendMessage":{"targetedRecipients":[1,4,2,3]}}}"""
def sortedArray : ScenarioBuilder = scenario("sorting an array")
.exec(http("test call")
.post("http://httpbin.org/anything")
.body(StringBody(data)).asJson
.check(
status.is(200),
jsonPath("$.json.data.sendMessage.targetedRecipients")
.find
.transform(_
.drop(1)
.dropRight(1)
.split(",")
.toVector
.sortWith(_<_)
)
.saveAs("received")
))
.exec(session => {
println(s"received: ${session("received").as[Vector[String]]}")
session
})
There is no reason to use mutable collection if all you want is to sort the result:
Vector(5,4,3,2,1).sortWith(_ < _).mkString(", ") // "1, 2, 3, 4, 5"
To use ListBuffer you have to copy all elements into newly allocated object, so it isn't even more optimal in any way. Same with vars - you can use vals as you don't update the reference
println(s"recipients class: ${recipients.getClass}")
val result = recipients.sortWith(_ < _).mkString(", ")
println(s"users sorted $result")
result

Replace all elements of a Seq from a String

I have a String and a Seq like :
Array[String] = Array(a, the, an)
String = "This is a sentence that includes articles a, an and the"
I want to replace each element of the Seq within the String with ""
Currently, I'm doing something like :
val a = Array("a" , "the", "an" )
var str = "This is a sentence that includes articles a, an and the"
a.foldLeft( "" ){ (x,y) => str=str.replaceAll(s"\\b${x}\\b", ""); str }
It seems to be working but doesn't look very Scala-ish mostly because of the re-assignment of the string for each iteration.
Is there any other way to do this?
This seems to be the correct variant:
a.foldLeft(str){ case (acc,item) => acc.replaceAll(s"\\b${item}\\b", "")}
It's just
a.foldLeft(str) { (x,y) => x.replaceAll(s"\\b${y}\\b", "") }
For foldLeft, x is already the intermediate result you want, no need to store it in a var.
(As a side note, your original code doesn't work correctly in general: if a is empty, it'll return "" instead of str.)

Converting JSON field names in argonaut

I'm writing a library to convert JSON responses from an API for backwards compatibility reasons. And what I need to do is take in arbitrary JSON, and change certain field names. I'm using scala and argonaut, but I don't see any way in the docs or examples of changing the FIELD names, only the values.
I don't know of a particularly nice way to do this, but it's not too awful to write a helper that will replace a field in an object and then use that in a cursor with withObject:
def renameField(before: JsonField, after: JsonField)(obj: JsonObject) =
obj(before).map(v => (obj - before) + (after, v)).getOrElse(obj)
Parse.parseOption("""{ "a": { "b": { "c": 1 } } }""").flatMap { json =>
(json.hcursor --\ "a").withFocus(_.withObject(renameField("b", "z"))).undo
}
This will return Some({"a":{"z":{"c":1}}}) as expected.
I ended up folding over the object I need to convert and adding to a map, and then creating a new json object.
val conversionMap = Map("a" -> "b")
Json(
j.objectOrEmpty.toMap.foldLeft(Map.empty[JsonField, Json]) {
case (acc, (key, value)) =>
acc.updated(conversionMap.getOrElse(key, key), j.fieldOrNull(key))
}.toSeq: _*
)

The most idiomatic way to perform conditional concatenation of strings in Scala

I'm curious what would be the best way to build a String value via sequential appending of text chunks, if some of chunks dynamically depend on external conditions. The solution should be idiomatic for Scala without much speed and memory penalties.
For instance, how one could re-write the following Java method in Scala?
public String test(boolean b) {
StringBuilder s = new StringBuilder();
s.append("a").append(1);
if (b) {
s.append("b").append(2);
}
s.append("c").append(3);
return s.toString();
}
Since Scala is both functional and imperative, the term idiomatic depends on which paradigm you prefer to follow. You've solved the problem following the imperative paradigm. Here's one of the ways you could do it functionally:
def test( b : Boolean ) =
"a1" +
( if( b ) "b2" else "" ) +
"c3"
These days, idiomatic means string interpolation.
s"a1${if(b) "b2" else ""}c3"
You can even nest the string interpolation:
s"a1${if(b) s"$someMethod" else ""}"
What about making the different components of the string functions in their own right? They have to make a decision, which is responsibility enough for a function in my book.
def test(flag: Boolean) = {
def a = "a1"
def b = if (flag) "b2" else ""
def c = "c3"
a + b + c
}
The added advantage of this is it clearly breaks apart the different components of the final string, while giving an overview of how they fit together at a high level, unencumbered by anything else, at the end.
As #om-nom-nom said, yours is already sufficiently idiomatic code
def test(b: Boolean): String = {
val sb = new StringBuilder
sb.append("a").append(1)
if (b) sb.append("b").append(2)
sb.append("c").append(3)
sb.toString
}
I can suggest an alternative version, but it's not necessarily more performant or "scala-ish"
def test2(b: Boolean): String = "%s%d%s%s%s%d".format(
"a",
1,
if (b) "b" else "",
if (b) 2 else "",
"c",
3)
In scala, a String can be treated as a sequence of characters. Thus, an idiomatic functional way to solve your problem would be with map:
"abc".map( c => c match {
case 'a' => "a1"
case 'b' => if(b) "b2" else ""
case 'c' => "c3"
case _ =>
}).mkString("")