scala: union of two maps whose key type is the same and whose value type is a collection of elements, but whose types are different

后端 未结 3 1088
半阙折子戏
半阙折子戏 2020-12-20 06:35

I would like to create a union of two maps whose key type is the same and whose value type is a collection of elements, but whose types are different.

Consider the f

相关标签:
3条回答
  • 2020-12-20 07:16

    This appears to work.

    val peopleToChildrenAndDogs: Map[String, (Seq[Child], Seq[Pet])] = {
      (peopleToChildren.keySet ++ peopleToPets.keySet).map { k =>
        k -> (peopleToChildren.getOrElse(k, Seq())
             ,peopleToPets.getOrElse(k, Seq()))
      }.toMap
    }
    

    Get all the keys. For every key do a getOrElse() on each of the feeder Maps.

    0 讨论(0)
  • 2020-12-20 07:17

    To answer my own question, the following is the way that I solved it, but it seems overly long and complex:

    Welcome to the Ammonite Repl 1.0.2
    (Scala 2.11.11 Java 1.8.0_91)
    If you like Ammonite, please support our development at www.patreon.com/lihaoyi
    @ case class Child(name: String)
    defined class Child
    
    @ val peopleToChildren: Map[String, Seq[Child]] =
        Map("max" -> Seq(Child("a"), Child("b")),
          "yaneeve" -> Seq(Child("y"), Child("d")))
    peopleToChildren: Map[String, Seq[Child]] = Map("max" -> List(Child("a"), Child("b")), "yaneeve" -> List(Child("y"), Child("d")))
    
    @
    
    @ case class Pet(name: String)
    defined class Pet
    
    @ val peopleToPets: Map[String, Seq[Pet]] =
        Map("max" -> Seq(Pet("fifi")),
          "jill" -> Seq(Pet("bobo"), Pet("jack"), Pet("Roger rabbit")))
    peopleToPets: Map[String, Seq[Pet]] = Map("max" -> List(Pet("fifi")), "jill" -> List(Pet("bobo"), Pet("jack"), Pet("Roger rabbit")))
    
    @
    
    @ val peopleToChildrenAndDogs: Map[String, (Seq[Child], Seq[Pet])] = {
        // people may have children
        // people may have pets
        // would like a map from people to a tuple with a potentially empty list of children and a
        //     potentially empty list of pets
    
        val paddedPeopleToChildren =  peopleToChildren.map{ case (person, children) => person -> (children, List.empty[Pet])}
        val paddedPeopleToPets = peopleToPets.map{ case (person, pets) => person ->(List.empty[Child], pets)}
        val notGoodEnough = paddedPeopleToPets ++ paddedPeopleToChildren // this is here to show that it does not work since it overwrites the value of a key - Map(max -> (List(Child(a), Child(b)),List()), jill -> (List(),List(Pet(bobo), Pet(jack), Pet(Roger rabbit))), yaneeve -> (List(Child(y), Child(d)),List()))
    
        val allSeq = paddedPeopleToPets.toSeq ++ paddedPeopleToChildren.toSeq
        val grouped = allSeq.groupBy(_._1).mapValues(_.map { case (_, tup) => tup })
        val solution = grouped.mapValues(_.unzip).mapValues {case (wrappedChildren, wrappedPets) => (wrappedChildren.flatten, wrappedPets.flatten)}
        solution
      }
    peopleToChildrenAndDogs: Map[String, (Seq[Child], Seq[Pet])] = Map(
      "yaneeve" -> (ArrayBuffer(Child("y"), Child("d")), ArrayBuffer()),
      "max" -> (ArrayBuffer(Child("a"), Child("b")), ArrayBuffer(Pet("fifi"))),
      "jill" -> (ArrayBuffer(), ArrayBuffer(Pet("bobo"), Pet("jack"), Pet("Roger rabbit")))
    )
    
    0 讨论(0)
  • 2020-12-20 07:26

    Just for the curious, here's how it could be done using Scalaz:

    import scalaz._, Scalaz._
    
    case class Child(name: String)
    
    val peopleToChildren = Map(
      "max"     -> List(Child("a"), Child("b")), 
      "yaneeve" -> List(Child("y"), Child("d"))
    )
    
    case class Pet(name: String)
    
    val peopleToPets = Map(
      "max"  -> List(Pet("fifi")), 
      "jill" -> List(Pet("bobo"), Pet("jack"), Pet("Roger rabbit"))
    )
    
    val peopleToChildrenAndPets: Map[String, (List[Child], List[Pet])] = 
      peopleToChildren.strengthR(nil[Pet]) |+| peopleToPets.strengthL(nil[Child])
    

    Explanation:

    • nil[Pet] is just an alias for List.empty[Pet]
    • strengthR for a given Functor tuples contained values, so that its parameter is at the right. Here it is equivalent to peopleToChildren.mapValues(v => (v, nil[Pet]))
    • strengthL is the same, but element will be added to the left
    • |+| is an append operator for a given Semigroup. The one here is derived recursively:
      • for Map[K, V], it uses |+| to combine values of type V if a given key exists in both Maps. If the value is only present in one of them, it will be retained as is. Here, V = (List[Child], List[Pet])
      • for tuples (A, B), it again uses |+| to combine both As and Bs. Here, A = List[Child] and B = List[Pet]
      • for lists of any type (as well as strings, vectors or streams) it does concatenation. This is why I had to change type of Map values to be Lists - for generic Seqs this operation is not defined

    Result:

    peopleToChildrenAndPets: Map[String, (List[Child], List[Pet])] = Map(
      "max" -> (List(Child("a"), Child("b")), List(Pet("fifi"))),
      "jill" -> (
        List(),
        List(Pet("bobo"), Pet("jack"), Pet("Roger rabbit"))
      ),
      "yaneeve" -> (List(Child("y"), Child("d")), List())
    )
    
    0 讨论(0)
提交回复
热议问题