Reputation: 3936
I have the following JS code...
const collection=["Thing1","Thing2"];
const doSomething = (value)=>{
switch(value){
case "Thing1":
return {
arrayItems: ["Object 1", "Object 2"]
}
break;
default:
return {
arrayItems: ["Object 3", "Object 4"]
}
}
}
const result = collection.reduce(
(result, value)=> result.concat(doSomething(value).arrayItems),
[]
);
console.log(result);
// doSomething returns { "arrayItems": ["Object 1", "Object 2"] } for Thing1
// and { "arrayItems": ["Object 3", "Object 4"] } for Thing 2
// result should be ["Object 1", "Object 2","Object 3", "Object 4"]
I would now like to turn this into a similar Scala reduce like this...
val collection = ["Thing 1", "Thing 2"]
val result: Array[...] = collection
.reduce(
(array: Array[...], value: String) => {
val objectList = service.doSomething(value)
if (array != null && array.length > 0) {
array.concat(objectList)
} else {
objectList
}
});
// doSomething returns { "arrayItems": ["Object 1", "Object 2"] } for Thing1
// and { "arrayItems": ["Object 3", "Object 4"] } for Thing 2
// result should be ["Object 1", "Object 2","Object 3", "Object 4"]
But when I try this I get...
type mismatch;
found : (Array[...], String) => Array[...]
required: (java.io.Serializable, java.io.Serializable) => java.io.Serializable
val result: Array[...] = doSomething(value).reduce((array: Array[...], value: String)=>{
Is there a way to do this?
Update
I was asked for a background of what I am trying to accomplish, so the basic steps of what I am trying to accomplish are...
Updates per Answers
Thanks guys I didn't know about foldLeft till you brought it up and still working a bit to understand. First here is the working for loop I am trying to avoid...
var array : Array[BaseObject] = Array[BaseObject]()
collection.foreach((value: String) => {
val objectList = doSomething(value).arrayItems
array = array.concat(objectList)
});
I tried this...
val array: List[BaseObject] =
collection.foldLeft(List.empty[BaseObject]){
(myList, value) => {
val list : List[BaseObject] = List.from(doSomething(value).arrayList)
myList ++ list
}
};
But something little must be wrong because when I parse with gson toJson
, I get....
{"head":{...},"next":{}}
Where head is one of the Objects but not the whole set.
Upvotes: 0
Views: 185
Reputation: 4489
Prefer Using FoldLeft as you want to accumulate String
s over a List
.
I assumed that your ...
are String
in the following code :
val result: List[String] = myCollection.foldLeft(List.empty[String]){ (myList, value) => {
val newObject = service.doSomething(value)
myList :+ newObject
}
};
Also :
null
is strongly discouraged in Scala. If your List is empty, that won't even be executed.You can also use placeholders to shorten your code, which will gradually become :
val result: List[String] = myCollection.foldLeft(List.empty[String]){ (myList, value) => {
myList :+ service.doSomething(value)
}
};
then with placeholders
val result: List[String] = myCollection.foldLeft(List.empty[String]){ (_, _) => _ :+ service.doSomething(_)
};
Upvotes: 1
Reputation: 22850
The main problem is that reduce
will return a value of the same type as the one contained in the collection. So, if you have an Array of Strings then the result of reduce
will be a String (oh well, any supertype of String, that is why in this case that strange Serializable appears).
There is a more general version of reduce
which will allow you to provide any type as output, which is foldLeft
that requires an initial value.
Using that, and Lists instead of Arrays we can write this:
def concat(input: List[String])(f: String => List[String]): List[String] =
input.foldLeft(List.empty[String]) {
case (acc, str) => acc ++ f(str)
}
However, appending to a List is somewhat inefficient, so we can rewrite it like this to improve performance.
We will be prepending all results and then we will reverse the list to get the output in the expected order:
def concat(input: List[String])(f: String => List[String]): List[String] =
input.foldLeft(List.empty[String]) {
case (acc, str) => f(str) reverse_::: acc
}.reverse
However, this is a very general operation. We have a collection of some type A
and a function that returns collections of some type B
and we want to apply that function to all elements of the original collection and group the results into a single collection of type B
.
That is the job of flatMap
(which btw it is even more generic; because it works for anything that is a Monad, not only a collection).
def concat(input: List[String])(f: String => List[String]): List[String] =
input.flatMap(f)
I would recommend you to take a look to the Scaladoc as well as follow some tutorial, to learn more about all the operations the stdlib provides.
Also, please do not hesitate in asking any questions.
You can see the code running here.
Upvotes: 1