Monday, February 5, 2018

Scala Sorting with Another Json's Key

Leave a Comment

I am new to Scala and please help me out. I have 2 Json file. I want to sort first json with a key from the second json.

For eg: First Json

{     "id": 1,     "response" : [{             "user_id" : 1,             "products" : [                 {                     "product_id": 10,                     "price": 200                 },                 {                     "product_id": 13,                     "price": 210                 },                 {                     "product_id": 9,                     "price": 320                 }             ]          },{             "user_id" : 2,             "products" : [                 {                     "product_id": 15,                     "price": 200                 },                 {                     "product_id": 13,                     "price": 210                 },                 {                     "product_id": 8,                     "price": 320                 }             ]         }     ] } 

And My Second Json

{     "sort": [         {             "user_id": 1,             "products": [                 {                     "id": 8,                     "rank": 5                 },                 {                     "id": 9,                     "rank": 1                 },                 {                     "id": 10,                     "rank": 3                 },                 {                     "id": 13,                     "rank": 2                 },{                     "id": 15,                     "rank": 6                 },{                     "id": 17,                     "rank": 4                 },{                     "id": 20,                     "rank": 7                 },{                     "id": 21,                     "rank": 8                 },{                     "id": 23,                     "rank": 9                 }             ]         },{             "user_id": 2,             "products": [                 {                     "id": 8,                     "rank": 5                 },                 {                     "id": 9,                     "rank": 1                 },                 {                     "id": 10,                     "rank": 3                 },                 {                     "id": 13,                     "rank": 2                 },{                     "id": 15,                     "rank": 6                 },{                     "id": 17,                     "rank": 4                 },{                     "id": 20,                     "rank": 7                 },{                     "id": 21,                     "rank": 8                 },{                     "id": 23,                     "rank": 9                 }             ]         }     ] } 

I want to sort my first json with respect to the rank I have in second Json.

Output should be like each user should have his products in sorted order based on the rank that is specified for each user on the second JSON.

This is so far what I have tried

def sortedRes() = Action.async {     val url = //1st json url     val sortUrl = //2nd json url     ws.url(url).get().map { response =>       val value: JsValue = Json.parse(response.body)       val result: Either[Exception, SampleResponses] = value.validate[SampleResponses] match {         case JsSuccess(searchResponse, _) =>           Right(searchResponse)         case JsError(errors) =>           Left(new Exception("Couldn't parse Search API response"))       }        val values: List[SampleResponse] = result.right.get.responses        ws.url(sortUrl).get().map { response =>         val sorted: JsValue = Json.parse(response.body)           val sortRespResult: Either[Exception, Sort] = sorted.validate[Sort] match {           case JsSuccess(sortResponse, _) =>             Right(sortResponse)           case JsError(errors) =>             Left(new Exception("Couldn't parse because of these errors : " + errors))         }          val prodRankDetails: List[SampleRank] = sortRespResult.right.get.sort          println("prod = " + prodRankDetails.head.products.sortWith(_.rank > _.rank))       }       Ok(Json.toJson(result.right.get))     }   } 

In the last print statement I got the second json's first users product sorted. What I am trying to get is my first json sorted based on the second user.

Here is my model class

package models  import play.api.libs.functional.syntax._ import play.api.libs.json.Reads._ import play.api.libs.json._ // Combinator syntax   object sample {   case class SampleProduct(productId:Int, price: Int)   case class SampleResponse(userId: Int, products: List[SampleProduct])   case class SampleResponses(id: Int, responses: List[SampleResponse])   case class SampleRankedProduct(id: Int, rank: Int)   case class SampleRank(userId: Int, products: List[SampleRankedProduct])   case class Sort(sort: List[SampleRank])    implicit val productReads: Reads[SampleProduct] = (     (JsPath \ "product_id").read[Int] and       (JsPath \ "price").read[Int]     )(SampleProduct.apply _)    implicit val productWrites: Writes[SampleProduct] = (     (JsPath \ "product_id").write[Int] and       (JsPath \ "price ").write[Int]     )(unlift(SampleProduct.unapply))    implicit val responseReads: Reads[SampleResponse] = (     (JsPath \ "user_id").read[Int] and       (JsPath \ "products").read[List[SampleProduct]]     )(SampleResponse.apply _)    implicit val responseWrites: Writes[SampleResponse] = (     (JsPath \ "user_id").write[Int] and       (JsPath \ "products").write[List[SampleProduct]]     )(unlift(SampleResponse.unapply))    implicit val responsesReads: Reads[SampleResponses] = (     (JsPath \ "id").read[Int] and       (JsPath \ "response").read[List[SampleResponse]]     )(SampleResponses.apply _)    implicit val responsesWrites: Writes[SampleResponses] = (     (JsPath \ "id").write[Int] and       (JsPath \ "response").write[List[SampleResponse]]     )(unlift(SampleResponses.unapply))    implicit val rankedProductReads: Reads[SampleRankedProduct] = (     (JsPath \ "id").read[Int] and       (JsPath \ "rank").read[Int]     )(SampleRankedProduct.apply _)    implicit val rankedProductWrites: Writes[SampleRankedProduct] = (     (JsPath \ "id").write[Int] and       (JsPath \ "rank ").write[Int]     )(unlift(SampleRankedProduct.unapply))    implicit val rankReads: Reads[SampleRank] = (     (JsPath \ "user_id").read[Int] and       (JsPath \ "products").read[List[SampleRankedProduct]]     )(SampleRank.apply _)    implicit val rankWrites: Writes[SampleRank] = (     (JsPath \ "user_id").write[Int] and       (JsPath \ "products").write[List[SampleRankedProduct]]     )(unlift(SampleRank.unapply))    implicit val sortReads: Reads[Sort] = (JsPath \ "sort").read[List[SampleRank]].map(x ⇒ Sort(x))    implicit val sortWrites: Writes[Sort] = (__ \ "sort").write[List[SampleRank]].contramap { (person: Sort) => person.sort } } 

1 Answers

Answers 1

An approach for sorting the first JSON using the ranking from the second one, is something like that:

.... val prodRankDetails: List[SampleRank] = sortRespResult.right.get.sort  val sortedResults = values.copy(   responses = values.responses.map { resultForUser =>     val rankingForUser = prodRankDetails       .find(_.userId == resultForUser.userId)       .getOrElse(throw new RuntimeException(s"Rank not found for user ${resultForUser.userId}"))       .products       .map(product => (product.id, product.rank))       .toMap      resultForUser.copy(       products = resultForUser.products.sortWith((a, b) =>         rankingForUser.getOrElse(a.productId, Int.MaxValue) < rankingForUser.getOrElse(b.productId, Int.MaxValue))     )    } ) Ok(Json.toJson(sortedResults)) 

that should be performed just after you got prodRankDetails. And also the response will be available inside the .map as you are managing a Future. So you have to move also the Ok(...) inside that map and to flatMap the outer Future.

Hope this helps.

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment