List[Map[Int,Object]] sorting - scala

Hi I am new in scala and I don't know how to change following code:
def makeProfil(listProfils: List[Profil]): List[Profil] ={
// var newList = List[Profil]
var ll = List[Map[Int,Profil]]()
listProfils.foreach( item => {
var count = item.czasOgladania.get
if(item.czyKupil.get) count = count *4 ;
if(item.czyPrzeczytal.get) count = count *3 ;
if(item.czyWKarcie.get) count = count *2 ;
ll ::= Map (count -> item)
} )
}
I want to sort ll list by element count from and return sorted List[Profil] as the result. I tried various things but none of them work good.

List has a method, sortWith, which sorts your list, where you can provide the criteria for sorting the list. The criteria is a function, accepting two arguments (two profiles), and result is Boolean, which indicates which one of them is "greater".
So, you can do the following:
ll.sortWith((p1, p2) =>
getCount(p1) > getCount(p2)
)
where
def getCount(profil: Profil) = {
var count = profil.czasOgladania.get
if(profil.czyKupil.get) count = count *4 ;
if(profil.czyPrzeczytal.get) count = count *3 ;
if(profil.czyWKarcie.get) count = count *2 ;
count
}
Update
BTW, it seems that profil.czasOgladania, profil.czyKupil etc., are Options. In that case you should first check if they are defined, and than perform computations. You may define default values, e.g.
// if profil.czasOgladania is defined, get the value. Else, return 10.
val count = profil.czasOgladania.getOrElse(10)
or:
if(profil.czyWKarcie.getOrElse(false)) count = count *2

Here's the whole thing without any mutable states (vars are bad practice). First you map the list of profiles to a list of (count, profile) tuples. The map doesn't seem necessary. Then you sort the list by the first item in the tuple, then map it to a list of profiles (the second item in the tuple).
def makeProfil(listProfils: List[Profil]): List[Profil] ={
val profileCounts = listProfils.map( item => {
val count = item.czasOgladania.getOrElse(0)
val kupil = if(item.czyKupil.isDefined) 4 else 1
val przeczytal = if(item.czyPrzeczytal.isDefined) 3 else 1;
val wKarcie = if(item.czyWKarcie.isDefined) 2 else 1 ;
val finalCount = count * kupil * przeczytal * wKarcie
(count, item)
} )
profileCounts.sortBy( _._1).map(_._2)
}

You can also use sortBy:
def makeProfil(listProfils: List[Profil]): List[Profil] = {
def getCount(profil: Profil) = {
var count = profil.czasOgladania.get
if (profil.czyKupil.get) count *= 4
if (profil.czyPrzeczytal.get) count *= 3
if (profil.czyWKarcie.get) count *= 2
count
}
listProfils.sortBy(p => getCount(p))
}

Related

For comprehension use case

I have a list that for example has 101 record, i want to send data in batch of 10.
list.grouped(10) will return 10 lists having 10 records and 1 list with one record.
I want to send all ten lists with 10 records to futureSend method and want to get the list with one record(if exists in this scenario it does but if list has 100 records then return empty list) from this for comprehension. So if the list.size is not equals to ten, i do not want to send the list to futureSend and get that list from for comprehension.
val listRes = list.grouped(10)
for {
list <- listRes
if list.size == 10
_ = futureSend(list) map { result =>
println("sent")
)
}
} yield {}
I want to yield the list with the size less than 10(do not call futureCall method in that case) and if all the lists have size 10 yield empty list.
A for comprehension is the wrong tool for the job.
val listRes = list.grouped(10).toList
listRes.init.foreach{group => futureSend(group); println("sent")}
val last = listRes.last
val remainder = if (last.length < 10) last
else {
futureSend(last)
println("sent")
List.empty[ElementType]
}
If you're on Scala 2.13.x then there's a slightly more efficient option.
val remainder = List.unfold(list.grouped(10)){ itr =>
Option.when(itr.hasNext) {
val thisBatch = itr.next()
if (itr.hasNext || thisBatch.size == 10) {
futureSend(thisBatch)
println("sent")
(List(), itr)
} else (thisBatch, itr)
}
}.flatten

Scala - Count number of adjacent repeated chars in String

I have this function that counts the number of adjacent repeated chars inside a String.
def adjacentCount( s: String ) : Int = {
var cont = 0
for (a <- s.sliding(2)) {
if (a(0) == a(1)) cont = cont + 1
}
cont
}
}
But I'm supposed to create a function that does exactly the same, but using only immutable variables or loop instructions, in a "purely" functional way.
You can just use the count method on the Iterator:
val s = "aabcddd"
s.sliding(2).count(p => p(0) == p(1))
// res1: Int = 3

Is this correct use of .par in Scala?

Below code computes a distance metric between two Users as specified by case class :
case class User(name: String, features: Vector[Double])
val ul = for (a <- 1 to 100) yield (User("a", Vector(1, 2, 4)))
var count = 0;
def distance(userA: User, userB: User) = {
val subElements = (userA.features zip userB.features) map {
m => (m._1 - m._2) * (m._1 - m._2)
}
val summed = subElements.sum
val sqRoot = Math.sqrt(summed)
count += 1;
println("count is " + count)
((userA.name, userB.name), sqRoot)
}
val distances = ul.par.map(m => ul.map(m2 => {
(distance(m, m2))
})).toList.flatten
val sortedDistances = distances.groupBy(_._1._1).map(m => (m._1, m._2.sortBy(s => s._2)))
println(sortedDistances.get("a").get.size);
This performs a Cartesian product of comparison 100 users : 10000 comparisons. I'm counting each comparison, represented bu var count
Often the count value will be less than 10000, but the amount of items iterated over is always 10000. Is reason for this that as par spawns multiple threads some of these will finish before the println statement is executed. However all will finish within par code block - before distances.groupBy(_._1._1).map(m => (m._1, m._2.sortBy(s => s._2))) is evaluated.
In your example you have a single un-synchronized variable that you're mutating from multiple threads like you said. This means that each thread, at any time, may have a stale copy of count, so when they increment it they will squash any other writes that have occurred, resulting in a count less than it should be.
You can solve this using the synchronized function,
...
val subElements = (userA.features zip userB.features) map {
m => (m._1 - m._2) * (m._1 - m._2)
}
val summed = subElements.sum
val sqRoot = Math.sqrt(summed)
this.synchronized {
count += 1;
}
println("count is " + count)
((userA.name, userB.name), sqRoot)
...
Using 'this.synchronized' will use the containing object as the lock object. For more information on Scala synchronization I suggest reading Twitter's Scala School.

Scala Connect Four Moving Between Rows

I am trying to make a connect four game in scala. Currently i have it so it prints the board everytime and switches between players asking them to enter which column they want. The problem is I dont know how to change the rows. All 64 moves stay in row 7 (the 1st row). I was thinking of somehow doing a check that will check if there is already an X or O in the spot the user wants to play and just bump up the row. Would i use a if else for this? So if x or o is there move up a row else make move.
// Initialize the grid
val table = Array.fill(9,8)('.')
var i = 0;
while(i < 8){
table(8)(i) = (i+'0').toChar
i = i+1;
}
/* printGrid: Print out the grid provided */
def printGrid(table: Array[Array[Char]]) {
table.foreach( x => println(x.mkString(" ")))
}
var allowedMoves = 64
var spotsLeft = 8
//Player One
def player1(){
printGrid(table)
println("Player 1 it is your turn. Choose a column 0-7")
val move = readInt
table(7)(move) = ('X')
}
//Player Two
def player2(){
printGrid(table)
println("Player 1 it is your turn. Choose a column 0-7")
val move = readInt
table(7)(move) = ('O')
for (turn <- 1 to 32) {
player 1
player 2
}
I am not sure if you're still baffled by my comments, but let me try to give you a more in-depth explanation here.
The code in question is:
//Player One
def player1(){
printGrid(table)
println("Player 1 it is your turn. Choose a column 0-7")
val move = readInt
table(7)(move) = ('X')
}
//Player Two
def player2(){
printGrid(table)
println("Player 1 it is your turn. Choose a column 0-7")
val move = readInt
table(7)(move) = ('O')
}
for (turn <- 1 to 32) {
player1
player2
}
where the players take turns. But before going straight to the answer, let us refactor this code a bit by removing the duplication we have here. player1 and player2 are almost the same implementation. So let us pass the distinct parts as a parameter. The distinct part is the name of the player and the symbol that represents this player in the table. So let us define a class Player:
case class Player(name: String, symbol: Char)
and contract the two functions into one:
def player(player: Player): Unit ={
printGrid(table)
println(s"${player.name} it is your turn. Choose a column 0-7")
val move = readInt
table(7)(move) = player.symbol
}
for (turn <- 1 to 32) {
player(Player("Player 1", 'X'))
player(Player("Player 2", 'O'))
}
Now, we don't have to do everything twice, but the problem is still the same.
Okay, let's say, we are going to use conditionals: If table(7)(move) is occupied, then we choose table(6)(move). However, if this is also occupied, we choose table(5)(move). This goes on, until we find the column is completely full, in which case we may for example want to throw an exception. In code, this would look as follows:
def player(player: Player): Unit = {
printGrid(table)
println(s"${player.name} it is your turn. Choose a column 0-7")
val move = readInt
if (table(7)(move) != '.') {
if (table(6)(move) != '.') {
if (table(5)(move) != '.') {
if (table(4)(move) != '.') {
if (table(3)(move) != '.') {
if (table(2)(move) != '.') {
if (table(1)(move) != '.') {
if (table(0)(move) != '.') {
throw new IllegalArgumentException(s"Column $move is already full")
} else table(0)(move) = player.symbol
} else table(1)(move) = player.symbol
} else table(2)(move) = player.symbol
} else table(3)(move) = player.symbol
} else table(4)(move) = player.symbol
} else table(5)(move) = player.symbol
} else table(6)(move) = player.symbol
} else table(7)(move) = player.symbol
}
Let's run the code and... yay, it works!. But it is terrible code. There is an awful lot of duplication and we couldn't easily make the table bigger.
Okay, what problem do we really want to solve? We want to find the highest index of the row that has a free space at move, i.e., contains a '.' at move.
How could we find this index? There exists a function indexOf that takes an argument x and returns the index of the first occurrence of x in the array. But our array table is a two-dimensional array, and we care only about the value at move in each inner array. Fortunately, every collection in Scala provides a function map to map each element, so we could do: table map (_(move)).
Say that we receive the following array: . . O X O O 2, so the index of the last occurrence of . is 1. But indexOf will return the first index, so indexOf('.') would return 0. We could reverse the array, because finding the first index in the reversed array is equivalent to finding the last index in the array, but this is a bit tricky, as we also need to inverse the index, because the the index in the reversed array is generally not the same as the index in the original array.
Let's apply a little trick: Instead of finding the last index of ., let's find the index of the first element that is not . and subtract one. But the function indexOf does not allow us to pass a not x. However, we can solve this problem by modifying our map function slightly: Instead of table map (_(move)), let's map to table map (_(move) == '.'). Now, we need to find the index of the first false value and subtract one.
The whole solution would look as follows:
def player(player: Player): Unit = {
printGrid(table)
println(s"${player.name} it is your turn. Choose a column 0-7")
val move = readInt
val freeRows = table map (_(move) == '.')
val indexOfLastFreeRow = (freeRows indexOf false) - 1
if (indexOfLastFreeRow == -1) throw new IllegalArgumentException(s"Column $move is already full")
else table(indexOfLastFreeRow)(move) = player.symbol
}
for (turn <- 1 to 32) {
player(Player("Player 1", 'X'))
player(Player("Player 2", 'O'))
}
case class Player(name: String, symbol: Char)
I hope this answer helps. As a final note: I would still not work with plain arrays, but instead define a class Table and Column and let them provide the functionality to add elements, and an appropriate toString to print the table.
case class Table(columns: List[Column]) {
override def toString = (for (i <- 0 until columns.head.values.length) yield {
columns map (_.values(i).toString) reduceLeft (_ + " " + _)
}) reduceLeft (_ + System.lineSeparator + _)
def add(entry: Char, columnIndex: Int): Table = {
val (prefix, column :: suffix) = columns.splitAt(columnIndex)
Table(prefix ++ (column.add(entry) :: suffix))
}
}
object Table {
val EmptyEntry = '.'
def empty(numberOfColumns: Int, numberOfRows: Int): Table =
Table(List.fill(numberOfColumns)(Column.empty(numberOfRows)))
}
case class Column(values: List[Char]) {
def isFull: Boolean = !values.contains(Table.EmptyEntry)
def add(entry: Char): Column = {
if (isFull) this
else {
val (empty, filled) = values.partition(_ == Table.EmptyEntry)
Column(empty.dropRight(1) ++ (entry :: filled))
}
}
}
object Column {
def empty(numberOfRows: Int): Column =
Column(List.fill(numberOfRows)(Table.EmptyEntry))
}

Tune Nested Loop in Scala

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) =>