1

I have a Scala Array of 2-tuples like this:

(("A", "2015-11-01"), ("B", "2016-11-11"), ("A", "2017-11-01"), ("B", "2013-11-11"))

I want to create a Map where the key maps to the latest date. So, in the example above, the result should be:

Map ("A" -> "2017-11-01", "B" -> "2016-11-11")

I know how to do it iteratively - but what would be a Scala-way (functional-way) to do this?

1
  • Did you try something already? What issues did you encounter? Commented Nov 6, 2016 at 9:19

2 Answers 2

3

First groupBy key and then pick latest Date.

arr
  .groupBy(_._1)
  .map { case (k, v) => k -> v.maxBy(_._2)._2 }

use mapValues to make it shorter

arr.groupBy(_._1).mapValues(_.maxBy(_._2)._2)

As date (string) is formatted properly max date is the latest date. You need not convert date into time in millis to decide the max date.

Scala REPL

scala> val arr = Array(("A", "2015-11-01"), ("B", "2016-11-11"), ("A", "2017-11-01"), ("B", "2013-11-11"))
arr: Array[(String, String)] = Array((A,2015-11-01), (B,2016-11-11), (A,2017-11-01), (B,2013-11-11))

scala> :paste
// Entering paste mode (ctrl-D to finish)

arr
  .groupBy(_._1)
  .map { case (k, v) => k -> v.maxBy(_._2)._2 }


// Exiting paste mode, now interpreting.

res0: scala.collection.immutable.Map[String,String] = Map(A -> 2017-11-01, B -> 2016-11-11)

date conversion is not needed but if you wish to convert it then go ahead.

date conversion:

//ensure correct date format is given to this method if not it will throw match error at runtime.
def convertStringDateToMillis(str: String): Long = {
 val regex = "(\\d{4})-(\\d{2})-(\\d{2})".r.unanchored
 val regex(year, month, day) = str
 val calendar = Calendar.getInstance()
 calendar.clear()
 calendar.set(Calendar.MONTH, month.toInt)
 calendar.set(Calendar.YEAR, year.toInt)
 calendar.set(Calendar.DAY_OF_MONTH, month.toInt)
 calendar.getTimeInMillis();
}

Solution:

val arr = Array(("A", "2015-11-01"), ("B", "2016-11-11"), ("A", "2017-11-01"), ("B", "2013-11-11"))

arr.groupBy(_._1).map { case (k, v) => k -> v.maxBy(convertStringDateToMillis(_._2))._2 }

Scala REPL

scala> def convertStringDateToMillis(str: String): Long = {
     |  val regex = "(\\d{4})-(\\d{2})-(\\d{2})".r.unanchored
     |  val regex(year, month, day) = str
     |  val calendar = Calendar.getInstance()
     |  calendar.clear()
     |  calendar.set(Calendar.MONTH, month.toInt)
     |  calendar.set(Calendar.YEAR, year.toInt)
     |  calendar.set(Calendar.DAY_OF_MONTH, month.toInt)
     |  calendar.getTimeInMillis();
     | }
convertStringDateToMillis: (str: String)Long

scala> val arr = Array(("A", "2015-11-01"), ("B", "2016-11-11"), ("A", "2017-11-01"), ("B", "2013-11-11"))
arr: Array[(String, String)] = Array((A,2015-11-01), (B,2016-11-11), (A,2017-11-01), (B,2013-11-11))


scala> arr.groupBy(_._1).map { case (k, v) => k -> v.maxBy(x => convertStringDateToMillis(x._2))._2 }
res3: scala.collection.immutable.Map[String,String] = Map(A -> 2017-11-01, B -> 2016-11-11)
Sign up to request clarification or add additional context in comments.

4 Comments

when I do a "arr.groupBy(_._1)" it complains about the "_1" part and says "value _1 is not a member of Product with Serializable". The "arr" is of type ArrayBuffer.. so, I do a "arr.toArray" before I call the groupBy function, but still I see this error. Thanks.
@Darth.Vader .. your array should be this val arr = Array(("A", "2015-11-01"), ("B", "2016-11-11"), ("A", "2017-11-01"), ("B", "2013-11-11"))
my arr variable looks like this: Array((A, 2015-11-01), (B, 2016-11-11), (A, 2017-11-01), (B, 2013-11-11))
Here is the question I asked about this: stackoverflow.com/questions/40451007/…
0

Something like this should work:

array
  .groupBy(_._1)
  .mapValues(_.map(_._2).max)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.