1
votes

I'm trying to return a map in scala. Here's my code:

    val interestRegEx = """(\w+) Interests \((.+ Interest)\)""".r
    val singleAttributes = Seq("Sport Interests (Some Interest):Running,Swimming","Something else:True")
    val interests = singleAttributes.map { x =>
      // e.g. ("Sport Interests (Some Interest)", "Running,Swimming") should
      // result in ("Sport Running" -> "Some Interest", "Sport Swimming" -> "Some Interest")
      val attVal = x.split(':')

      attVal(0) match {
        case interestRegEx(interestProduct, interestAmount) =>
          Some(attVal(1).split(",").map { i =>
            Map(s"$interestProduct $i" -> interestAmount)
          }.reduce(_ ++ _))
        case _ => None
      }
    }.fold(Map[String, String]())(_) //.reduce(_ + _)

The problem is trying to reduce the collection to a single Map[String, String]. I thought the fold might work, but since it doesn't perhaps I'd even need to add a reduce(_ + _) afterwards, but that doesn't work either.

The part I don't understand is that IntelliJ tells me that interests has type ((Equals, Equals) => Equals) => Equals. WTF? Where are these Equals coming from, and why isn't it just adding all of the Maps together to return a Map containing all keys & values?

2

2 Answers

2
votes

If we simplify your example we will get:

  val interests = Seq[String]().map { x =>
    Option[Map[String, String]](Map("k" -> "v"))
  }.fold(Map[String, String]())(_)

This means that we are trying to fold Seq[Option[Map[String, String]]] with initial value Map[String, String]():

def fold[A1 >: A](z: A1)(op: (A1, A1) => A1): A1 = foldLeft(z)(op)

From fold definition we can see that compiler expects that Option[Map[String, String]] (each value in folding) and Map[String, String] (init value) should be of the same type.

If you inspect Option hierarchy you will see:

Option -> Product -> Equals

For Map we have the following:

Map -> GenMap -> GenMapLike -> Equals (traits hierarchy is complicated, may be another chains exist).

So we can see tha the nearest common type is Equals.

Second part of your puzzle is (_).

This is treated by compiler as an argument of lambda:

val interests = x => /*omited*/.fold(Map[String, String]())(x)

As we saw x is (A1, A1) => A1. And in our case it is:

(Equals, Equals) => Equals

The result of fold is A1 which is also Equals.

As a result lambda type is:

((Equals, Equals) => Equals) /*<< arg*/ => /*result >>*/ Equals

UPDATE:

To solve your problem I think you should use:

.flatten.reduce(_ ++ _)
1
votes

When you get a trait like Product or Equals as the inferred type of a statement, you can usually bet you have a type mismatch in some higher order function. You typically don't get Any or AnyRef because those only happen if some set of types have nothing in common. One thing that sticks out to me is that your 2nd argument to fold only takes one parameter. Amazingly enough, that typechecks, but it's probably what's giving you the types you don't expect.

I think what you wanted to do is something more like:

val interestRegEx = """(\w+) Interests \((.+ Interest)\)""".r
val singleAttributes = Seq("Sport Interests (Some Interest):Running,Swimming","Something else:True")
val interests = singleAttributes.map { x =>
  // e.g. ("Sport Interests (Some Interest)", "Running,Swimming") should
  // result in ("Sport Running" -> "Some Interest", "Sport Swimming" -> "Some Interest")
  val attVal = x.split(':')

  attVal(0) match {
    case interestRegEx(interestProduct, interestAmount) =>
      attVal(1).split(",").map { i =>
        Map(s"$interestProduct $i" -> interestAmount)
      }.reduce(_ ++ _)
    case _ => Map.empty
  }
}.reduce(_ ++ _)

For this I got:

scala> interests
res0: scala.collection.immutable.Map[_ <: String, String] = Map(Sport Running -> Some Interest, Sport Swimming -> Some Interest)

I got rid of the Option wrapping the Maps in your regex match. Since you're planning on combining the maps, may as well use the empty map as your non-match case. Then I used reduce instead of fold for your final match, much as you had done in the inner map.