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
}
Related
I want that the if block returns Right(List[PracticeQuestionTags]) but I am not able to do so. The if/else returns Either
//I get java.util.List[Result]
val resultList:java.util.List[Result] = transaction.scan(scan);
if(resultList.isEmpty == false){
val listIterator = resultList.listIterator()
val finalList:List[PracticeQuestionTag] = List()
//this returns Unit. How do I make it return List[PracticeQuestionTags]
val answer = while(listIterator.hasNext){
val result = listIterator.next()
val convertedResult:PracticeQuestionTag = rowToModel(result) //rowToModel takes Result and converts it into PracticeQuestionTag
finalList ++ List(convertedResult) //Add to List. I assumed that the while will return List[PracticeQuestionTag] because it is the last statement of the block but the while returns Unit
}
Right(answer) //answer is Unit, The block is returning Right[Nothing,Unit] :(
} else {Left(Error)}
Change the java.util.List list to a Scala List as soon as possible. Then you can handle it in Scala fashion.
import scala.jdk.CollectionConverters._
val resultList = transaction.scan(scan).asScala.toList
Either.cond( resultList.nonEmpty
, resultList.map(rowToModel(_))
, new Error)
Your finalList: List[PracticeQuestionTag] = List() is immutable scala list. So you can not change it, meaning there is no way to add, remove or do change to this list.
One way to achieve this is by using scala functional approach. Another is using a mutable list, then adding to that and that list can be final value of if expression.
Also, a while expression always evaluates to Unit, it will never have any value. You can use while to create your answer and then return it seperately.
val resultList: java.util.List[Result] = transaction.scan(scan)
if (resultList.isEmpty) {
Left(Error)
}
else {
val listIterator = resultList.listIterator()
val listBuffer: scala.collection.mutable.ListBuffer[PracticeQuestionTag] =
scala.collection.mutable.ListBuffer()
while (listIterator.hasNext) {
val result = listIterator.next()
val convertedResult: PracticeQuestionTag = rowToModel(result)
listBuffer.append(convertedResult)
}
Right(listBuffer.toList)
}
I have an ArrayBuffer of Strings as below:
var myList = ArrayBuffer[String]()
myList += "abc"
myList += "def"
Now, I'm trying to update the String in the ArrayBuffer based on some condition:
for(item <- myList){
if(some condition){
item = "updatedstring"
}
}
When I try to do this, I get an error saying val cannot be reassigned. Why do I get this error even though I have declared myList as a var? If I cannot update it this way, how else can I update the elements when iterating through the ArrayBuffer? I'm new to Scala, so I apologize if I got this all wrong.
The first thing I would point out is that item is not the same as myList - it's an element within myList, and the way that Scala iteration works, it's a val. There are various reasons for this related to immutability which I won't get into here.
I would recommend this instead:
val myNewList = myList.map(originalString =>
if (someCondition) "xyz"
else originalString
)
Then, if you feel so inclined, you could do myList = myNewList (or just forgo having a myNewList entirely and do myList = myList.map(...)).
In the for loop item is a local val. Therefore, it cannot be changed.
You could:
either iterate through the array and update each item
myList.zipWithIndex foreach { case (item, index) if (condition) => myList.update(index, "updated") }
or create a new ArrayBuffer
myList = (0 until myList.length).map { index =>
val item = myList(index)
if (condition) "updated" else item
}
Suppose I have these classes: Foo(id: String) and Bar(fooId: String)
I have a list of Foo objects, I want to create a list of Bar objects according to the id in the Foo objects. If a bar exists in the list I don't want to be created again and added to the list. I can use Set to solve this problem but I don't like it that way. It is just not right.
def createBars() = {
var barList = List[Bar]()
for {
f ← fooList
bar = findOrCreateBar(f, barList)
barList = barList:+bar //error
} yield bar
}
def findOrCreateBar(f: Foo, barList: List[Bar]): Bar = {
barList match {
case Nil ⇒
Bar(f.id)
case _ ⇒
val bars = for {
bar ← barList
if f.id == bar.id
} yield bar
if (bars.isEmpty) {
Bar(f.id)
} else {
bars.head
}
}
}
I have a few issues:
first the code above does not work because I get compile error in barList:+bar in createBars. The :+ cannot be resolved! Also the compiler says that the var for declaring the barList could be val! So it seems that the compiler does not see the barList in the barList = barList:+bar as a list!
second If I replace the var of barList to val I can't reassign the changed list to barList
third I am using for comprehension in the createBars because I thought I have no other choice to return the barList. Using annotations and such just for a break in a regular foor-loop is not a nice idea, imho!
Please help!
What about
fooList.distinct.map(foo=>Bar(foo.id))
It is simple, efficient and keep the original list order.
Really, it is waaaaay better to use a Set. It's going to be much faster and much clearer.
fooList.map(foo => new Bar(foo.id)).toSet
But, anyway, here's a much cleaner version of what you're trying to do (ie, still uses a loop and List and var etc.):
def createBars2(fooList: List[Foo]) = {
var barList = Vector[Bar]()
for (foo <- fooList if !barList.exists(_.fooId == foo.id)) {
barList :+= Bar(foo.id)
}
barList.toList
}
val fooList = List(Foo("a"), Foo("b"), Foo("c"), Foo("b"))
println(createBars2(fooList))
You code has several flaws.
First of all, try to avoid mutable variables where possible.
Then, it's not efficient to search existent elements in List, as that requires O(n) time for single search and makes your algorithm run in quadric time.
And, at last, adding element at the end of List requires O(n) time too. If you can't avoid iteratively building of the list, use something mutable, like List.newBuilder.
Back to your problem, I'll solve it using recursive function, that takes and returns immutable values. (Don't look at ugly hack at the beginning, it's for demonstration purposes only).
var globalBarCount = 0
case class Foo(id: String)
case class Bar(fooId: String) {
// this is hack to track Bar instances
// never do this in real code
val barId = { globalBarCount += 1; globalBarCount }
override def toString: String = s"Bar#$barId($fooId)"
}
def createBars(foos:List[Foo]) = {
def createBars0(fooToProcess: List[Foo], existentBarsIndex: Map[String, Bar],
reversedResultAccum: List[Bar]): List[Bar] =
fooToProcess match {
case Nil => reversedResultAccum.reverse
case headFoo :: tail =>
val newBar = existentBarsIndex.getOrElse(headFoo.id, Bar(headFoo.id))
createBars0(tail, existentBarsIndex + (headFoo.id -> newBar), newBar :: reversedResultAccum)
}
createBars0(foos, Map.empty, Nil)
}
val fooList = List(Foo("foo1"), Foo("foo2"), Foo("foo3"), Foo("foo1"))
val barList = createBars(fooList)
println(barList)
Result is:
List(Bar#1(foo1), Bar#2(foo2), Bar#3(foo3), Bar#1(foo1))
UPD As I promised, adding new approaches.
You can use foldLeft, this approach basically does the same as above, but is shorter and thus preferred.
val barList2 = fooList.foldLeft((List.empty[Bar], Map.empty[String, Bar])) {
(accum, foo) =>
val (resultListReversed, alreadyCreatedBars) = accum
val newBar = alreadyCreatedBars.getOrElse(foo.id, new Bar(foo.id))
(newBar :: resultListReversed, alreadyCreatedBars + (foo.id -> newBar))
}._1.reverse
And the final approach. This one is really short, but you'd rather avoid using it, as it is not recommended to use mutable variables inside map.
import scala.collection.mutable
val barList3 = {
val createdBars = mutable.HashMap[String, Bar]()
fooList.map(foo => createdBars.getOrElseUpdate(foo.id, new Bar(foo.id)))
}
Here is the function,
def checkSupport(request: checkSupportRequest): Future[checkSupportResponse] = {
val lookupClient = Thrift.newIface[lookupClient[Future]](/*some initializers*/)
val req = lookupIPRequest(request.getIps, /*Unimportant variables*/)
var result = new checkSupportResponse()
lookupClient.performLookup(req).map { resp =>
// Returns a map of IP to Country Code (String:String)
val ip_cc_map = for {
(ip, lxn) <- resp.found
sr <- lxn.simpleResult
cc <- sr.countryCode
} yield (ip, cc)
// Returns a map of IP to Boolean (String:Boolean)
val not_found_map = for {
(ip) <- resp.notFound
} yield (ip, false)
val op_supp_temp_map = ip_cc_map.map {
case (ip, cc) => someLookup(cc) //This returns a boolean value
}
val op_supp_map = op_supp_temp_map.toList //List of Booleans
val ips_found = ip_cc_map.map { // Intended to become a list of IPs
case (ip, cc) => ip
}
val final_op_supp_map = ips_found zip op_supp_map // Converted to Map (String:Boolean)
final_map = final_op_supp_map ++ not_found_map
result.set_supported(final_result.toList.toMap.mapValues(Boolean.box))
}
Future.value(result)
}
The performLookup is a futurized call. Is it kosher to do things as above i.e. take the output of the futurized call and map it. Perform two back-to-back for comprehensions on it to obtain the necessary maps and then subsequently in the same for, combine the two results and return the same. For some reason, this compiles but when I'm trying to unit-test it, it appears as if the performLookup never ran. Any help would be appreciated.
Also I am in my unit-test doing an Await.result() on the checkSupport() call.
I'm assuming that by Future.value(result) you actually mean Future.successful(result).
The result variable inside the closure is different from the result variable outside of it, so you're never actually modifying the result variable that you're declaring at the beginning of your function. So this is why it looks like your Future never ran.
As a side-note, even if you mutate the state of result, you don't need to declare it as var, val is good here (var would only be necessary if you were reassigning it by doing result = ...).
So you can modify your function like this:
def checkSupport(request: checkSupportRequest): Future[checkSupportResponse] = {
// declare lookupClient and req as before, but not res
lookupClient.performLookup(req).map { resp =>
// do all your processing like before
val result = new checkSupportResponse()
result.setOperator_supported(final_result.toList.toMap.mapValues(Boolean.box))
// We want "result" to be the result of the computation
result
}
}
I was wondering if I can tune the following Scala code :
def removeDuplicates(listOfTuple: List[(Class1,Class2)]): List[(Class1,Class2)] = {
var listNoDuplicates: List[(Class1, Class2)] = Nil
for (outerIndex <- 0 until listOfTuple.size) {
if (outerIndex != listOfTuple.size - 1)
for (innerIndex <- outerIndex + 1 until listOfTuple.size) {
if (listOfTuple(i)._1.flag.equals(listOfTuple(j)._1.flag))
listNoDuplicates = listOfTuple(i) :: listNoDuplicates
}
}
listNoDuplicates
}
Usually if you have someting looking like:
var accumulator: A = new A
for( b <- collection ) {
accumulator = update(accumulator, b)
}
val result = accumulator
can be converted in something like:
val result = collection.foldLeft( new A ){ (acc,b) => update( acc, b ) }
So here we can first use a map to force the unicity of flags. Supposing the flag has a type F:
val result = listOfTuples.foldLeft( Map[F,(ClassA,ClassB)] ){
( map, tuple ) => map + ( tuple._1.flag -> tuple )
}
Then the remaining tuples can be extracted from the map and converted to a list:
val uniqList = map.values.toList
It will keep the last tuple encoutered, if you want to keep the first one, replace foldLeft by foldRight, and invert the argument of the lambda.
Example:
case class ClassA( flag: Int )
case class ClassB( value: Int )
val listOfTuples =
List( (ClassA(1),ClassB(2)), (ClassA(3),ClassB(4)), (ClassA(1),ClassB(-1)) )
val result = listOfTuples.foldRight( Map[Int,(ClassA,ClassB)]() ) {
( tuple, map ) => map + ( tuple._1.flag -> tuple )
}
val uniqList = result.values.toList
//uniqList: List((ClassA(1),ClassB(2)), (ClassA(3),ClassB(4)))
Edit: If you need to retain the order of the initial list, use instead:
val uniqList = listOfTuples.filter( result.values.toSet )
This compiles, but as I can't test it it's hard to say if it does "The Right Thing" (tm):
def removeDuplicates(listOfTuple: List[(Class1,Class2)]): List[(Class1,Class2)] =
(for {outerIndex <- 0 until listOfTuple.size
if outerIndex != listOfTuple.size - 1
innerIndex <- outerIndex + 1 until listOfTuple.size
if listOfTuple(i)._1.flag == listOfTuple(j)._1.flag
} yield listOfTuple(i)).reverse.toList
Note that you can use == instead of equals (use eq if you need reference equality).
BTW: https://codereview.stackexchange.com/ is better suited for this type of question.
Do not use index with lists (like listOfTuple(i)). Index on lists have very lousy performance. So, some ways...
The easiest:
def removeDuplicates(listOfTuple: List[(Class1,Class2)]): List[(Class1,Class2)] =
SortedSet(listOfTuple: _*)(Ordering by (_._1.flag)).toList
This will preserve the last element of the list. If you want it to preserve the first element, pass listOfTuple.reverse instead. Because of the sorting, performance is, at best, O(nlogn). So, here's a faster way, using a mutable HashSet:
def removeDuplicates(listOfTuple: List[(Class1,Class2)]): List[(Class1,Class2)] = {
// Produce a hash map to find the duplicates
import scala.collection.mutable.HashSet
val seen = HashSet[Flag]()
// now fold
listOfTuple.foldLeft(Nil: List[(Class1,Class2)]) {
case (acc, el) =>
val result = if (seen(el._1.flag)) acc else el :: acc
seen += el._1.flag
result
}.reverse
}
One can avoid using a mutable HashSet in two ways:
Make seen a var, so that it can be updated.
Pass the set along with the list being created in the fold. The case then becomes:
case ((seen, acc), el) =>