Scala accessing attribute value in nested Seq collection - scala

I have a nested Seq collection below and I'm trying to access the value of the height attribute inside the Banner attribute.
Consider that there may exist multiple Banners in the list and multiple Campaigns in the Seq collection. What is the best way to get the value of height of each Banner in the Banner's List?
I've tried to do it like below but it only prints the value of the entire banner, e.g., 'Banner(1,https://business.eskimi.com/wp-content/uploads/2020/06/openGraph.jpeg,300,250)'. I want it to print the value 250 only.
case class Campaign(id: Int, country: String, targeting: Targeting, banners: List[Banner], bid: Double)
case class Targeting(targetedSiteIds: Seq[String])
case class Banner(id: Int, src: String, width: Int, height: Int)
val lists = Seq(
Campaign( // Campaign
1, // Id
"LT", // Country
Targeting( // Targeting
Seq("0006a522ce0f4bbbbaa6b3c38cafaa0f") // TargetedSiteIds
),
List( // Banners
Banner( // Banner
1, // id
"https://business.eskimi.com/wp-content/uploads/2020/06/openGraph.jpeg", // URL source
300, // width
250 // height
)
),
5d // price
)
)
activeCampaigns.foreach(_.banners.foreach(el=>println(el)))

This question is not well written, I have no idea what you're trying to achieve, but maybe you can make something out of this.
If I wanted to print those values I would do something like this
activeCampaigns.foreach(_.banners.foreach(el => println(el.height)))

Related

How to combine two objects in a List by summing a member

Given this case class:
case class Categories(fruit: String, amount: Double, mappedTo: String)
I have a list containing the following:
List(
Categories("Others",22.38394964594807,"Others"),
Categories("Others",77.6160503540519,"Others")
)
I want to combine two elements in the list by summing up their amount if they are in the same category, so that the end result in this case would be:
List(Categories("Others",99.99999999999997,"Others"))
How can I do that?
Since groupMapReduce was introduced in Scala 2.13, I'll try to provide another approch to Martinjn's great answer.
Assuming we have:
case class Categories(Fruit: String, amount: Double, mappedTo: String)
val categories = List(
Categories("Apple",22.38394964594807,"Others"),
Categories("Apple",77.6160503540519,"Others")
)
If you want to aggregate by both mappedTo and Fruit
val result = categories.groupBy(c => (c.Fruit, c.mappedTo)).map {
case ((fruit, mappedTo), categories) => Categories(fruit, categories.map(_.amount).sum, mappedTo)
}
Code run can be found at Scastie.
If you want to aggregate only by mappedTo, and choose a random Fruit, you can do:
val result = categories.groupBy(c => c.mappedTo).map {
case (mappedTo, categories) => Categories(categories.head.Fruit, categories.map(_.amount).sum, mappedTo)
}
Code run can be found at Scastie
You want to group your list entries by category, and reduce them to a single value. There is groupMapReduce for that, which groups entries, and then maps the group (you don't need this) and reduces the group to a single value.
given
case class Category(category: String, amount: Double)
if you have a val myList: List[Category], then you want to group on Category#category, and reduce them by merging the members, summing up the amount.
that gives
myList.groupMapReduce(_.category) //group
(identity) //map. We don't need to map, so we use the identity mapping
{
case (Category(name, amount1), Category(_, amount2)) =>
Category(name, amount1 + amount2) }
} //reduce, combine each elements by taking the name, and summing the amojunts
In theory just a groupReduce would have been enough, but that doesn't exist, so we're stuck with the identity here.

Looking for help in Nested groupBy with scalikejdbc?

I am using Scala 2.12 and have required libraries downloaded via build.sbt.
I have my DB output in below format.
Basically, it is like per valuation date and book, there can be multiple currency data.
I have group by on book (majorly), which will have list of Pnl data based on currency.
Just the rough representation:
{ Bookid: 1234,
BookName: EQUITY,
PnlBreakdown: [currency: cad, actual_pnl_local: 100, actual_pnl_cde: 100], [currency: usd, actual_pnl_local: 100, actual_pnl_cde: 130]
}
Basically. Key will be book and value will be list of pnl data.
I have a case class defined as below:
case class PnlData(valuation_date: Option[String], currency: Option[String],pnl_status: Option[String],actual_pnl_local: Option[String] ,actual_pnl_cde: Option[String], actual_pnl_local_me_adj: Option[String] ,actual_pnl_cde_me_adj: Option[String] ) {
override def toString():String= {
s"valuation_date=$valuation_date,currency=$currency,pnl_status=$pnl_status,actual_pnl_local=$actual_pnl_local,actual_pnl_cde=$actual_pnl_cde,actual_pnl_local_me_adj=$actual_pnl_local_me_adj,actual_pnl_cde_me_adj=$actual_pnl_cde_me_adj"
}
}
case class BookLevelDaily(book_id: Option[String], book: Option[String], pnlBreakdown: List[SaPnlData]){
override def toString():String= {
s"book_id=$book_id,book=$book,pnl=$pnlBreakdown"
}
}
Basically, my final object is of type BookLevelDaily.
How do I translate the DB output (above) to my BookLevelDaily object?
I can convert the entire result to the list, but further how should I do groupBy?
val list: List[BookLevelDaily] =
sql"""
|SELECT QUERY TO GET ABOVE RESULTSET
""".stripMargin.map(rs =>
BookLevelDaily(
valuation_date = rs.stringOpt("valuation_date"),
book_id = rs.stringOpt("book_id"),
book = rs.stringOpt("book"),
currency= rs.stringOpt("currency"),
pnl_status= rs.stringOpt("pnl_status"),
actual_pnl_local= rs.stringOpt("actual_pnl_local"),
actual_pnl_cde= rs.stringOpt("actual_pnl_cde"),
actual_pnl_local_me_adj= rs.stringOpt("actual_pnl_local_me_adj"),
actual_pnl_cde_me_adj= rs.stringOpt("actual_pnl_cde_me_adj")
)
).list().apply()
Firstly above is not of type BookLevelDaily. So how to iterate or group by to separate Pnl level data and map it to key (book).
If I understand it correctly, it seems to be a one-to-many relationship (one: book_level_daily, many: pnl_breakdown). If so, check the following documentation.
http://scalikejdbc.org/documentation/one-to-x.html

How can I process this big data in scala IntelliJ?

It has been a few days and i started learning Scala on IntelliJ and I am learning all by myself. Please bear my rookie mistakes. I have a csv file with more than 10,000 rows and 13 columns.
The heading of of the columns are:
Category | Rating | Reviews | Size | Installs | Type | Price | Content Rating | Genres | Last updated | Current Version | Android Version
I did manage to read and display the the csv file with the following code:
import scala.io.Source
object task {
def main(args: Array[String]): Unit = {
for(line <- Source.fromFile("D:/data.csv"))
{
println(line)
}
}
}
The problem with this is that this code displays one alphabet or digit, moves onto the next line and displays the next alphabet or digit. It does not display a row in one line.
I want to find out the best app for each category (ART_AND_DESIGN, AUTO_AND_VEHICLES, BEAUTY…,) based on its assigned priorities of reviews and ratings. The priorities are defined as 60 % for “reviews” and 40% for “rating” columns respectively. Calculate a value for each category (ART_AND_DESIGN, AUTO_AND_VEHICLES, BEAUTY…,) by using these assigned values of priorities. This value will help us out to find the best app in each category. You can use Priority formula equation as follows.
Priority = ( (((rating/max_rating) * 100) * 0.4) + (((reviews/max_reviews) * 100) * 0.6) )
Here max_rating is maximum rating of given data in same category like category(“ART_AND_DESIGN”) maximum rating is “4.7”, max_reviews is maximum reviews of app in same category like category(“ART_AND_DESIGN”) maximum reviews is “295221”. So priority value will be for first data record of category(“ART_AND_DESIGN”) is:
Rating= 4.1, reviews= 159,
max_rating= 4.7, max_reviews= 295221
My question is, how can i store every column in an array? That is how i plan on computing the data. If there is any other way to solve the above problem, i am open to suggestions.
I can upload a small chunk of the data if anyone wants to.
Source gives you a byte Iterator by default. To iterate through lines, use .getLines:
Source.fromFile(fileName)
.getLines
.foreach(println)
To split lines into arrays, use split (assuming the column values do not include separator):
val arrays = Source.fromFile(fileName).getLines.map(_.split("|"))
It is better to avoid using raw arrays though. Creating a case class makes for much better, readable code:
case class AppData(
category: String,
rating: Int,
reviews: Int,
size: Int,
installs: Int,
`type`: String,
price: Double,
contentRating: Int,
generes: Seq[String],
lastUpdated: Long,
version: String,
androidVersion: String
) {
def priority(maxRating: Int, maxReview: Int) =
if(maxRatings == 0 || maxReviews == 0) 0 else
(rating * 0.4 / maxRating + reviews * 0.6 /maxReview) * 100
}
object AppData {
def apply(str: String) = {
val fields = str.split("|")
assert(fields.length == 12)
AppData(
fields(0),
fields(1).toInt,
fields(2).toInt,
fields(3).toInt,
fields(4).toInt,
fields(5),
fields(6).toDouble,
fields(7).toInt,
fields(8).split(",").toSeq,
fields(9).toLong,
fields(10),
fields(11)
)
}
}
Now you can do what you want pretty neatly:
// Read the data, parse it and group by category
// This gives you a map of categories to a seq of apps
val byCategory = Source.fromFile(fileName)
.map(AppData)
.groupBy(_.category)
// Now, find out max ratings and reviews for each category
// This could be done even nicer with another case class and
// a monoid, but tuple/fold will do too
// It is tempting to use `.mapValues` here, but that's not a good idea
// because .mapValues is LAZY, it will recompute the max every time
// the value is accessed!
val maxes = byVategory.map { case (cat, data) =>
cat ->
data.foldLeft(0 -> 0) { case ((maxRatings, maxReviews), in) =>
(maxRatings max in.rating, maxReviews max in.reviews)
}
}.withDefault( _ => (0,0))
// And finally go through your categories, and find best for each,
// that's it!
val bestByCategory = byCategory.map { case(cat, apps) =>
cat -> apps.maxBy { _.priority.tupled(maxes(cat)) }
}

Perform arithmetic on a list in Scala

I am new to Scala so please forgive me if I am overseeing something extremely basic here. I have the following:
case class Record(
ID: String,
Count: Double)
List(Record("ID1",10.0),Record("ID1",60.0),Record("ID2",50.0),Record("ID3",100.0),Record("ID3",20.0),Record("ID3",10.0))
where x is the ID and y is the Count in Record(x,y). I am able to print this list to the console with println(records).
I am trying to output the following:
ID1,70.0
ID2,50.0
ID3,130.0
which is a summation of the count per ID group. I would like to try the groupBy approach, but I am struggling to be able to parse the ID from each Record in my list in order to begin grouping the counts.
For example, I have considered:
val grouped = records.groupBy(<some_logic_here>)
but the problem is that the objects in the list have Record(x,y) wrapped around it.
Thank you for your help.
case class Record(ID: String,
Count: Double)
val records = List(Record("ID1", 10.0), Record("ID1", 60.0), Record("ID2", 50.0), Record("ID3", 100.0), Record("ID3", 20.0),
Record("ID3", 10.0))
here is the one liner:
val ans = records.groupBy(_.ID).mapValues(_.map(_.Count).sum)
ans.foreach(x => println(s"${x._1},${x._2}"))
case class Record(ID: String, Count: Double)
List(Record("1", 12), Record("1", 13), Record("2", 13))
.groupBy(_.ID)
.map(e => Record(e._1, e._2.map(e => e.Count).sum))
You need to groupBy(ID) and then you get an list, and then you need compute the sum.

Scala map function to remove fields

I have a list of Person objects with many fields and I can easily do:
list.map(person => person.getName)
In order to generate another collection with all the peoples names.
How can you use the map function to create a new collection with all the fields of the Person class, BUT their name though?
In other words, how can you create a new collection out of a given collection which will contain all the elements of your initial collection with some of their fields removed?
You can use unapply method of your case class to extract the members as tuple then remove the things that you don't want from the tuple.
case class Person(name: String, Age: Int, country: String)
// defined class Person
val personList = List(
Person("person_1", 20, "country_1"),
Person("person_2", 30, "country_2")
)
// personList: List[Person] = List(Person(person_1,20,country_1), Person(person_2,30,country_2))
val tupleList = personList.flatMap(person => Person.unapply(person))
// tupleList: List[(String, Int, String)] = List((person_1,20,country_1), (person_2,30,country_2))
val wantedTupleList = tupleList.map({ case (name, age, country) => (age, country) })
// wantedTupleList: List[(Int, String)] = List((20,country_1), (30,country_2))
// the above is more easy to understand but will cause two parses of list
// better is to do it in one parse only, like following
val yourList = personList.flatMap(person => {
Person.unapply(person) match {
case (name, age, country) => (age, country)
}
})
// yourList: List[(Int, String)] = List((20,country_1), (30,country_2))