How to map correctly in scala?Ask Question

问题:

I have a list of this object :

case class Alloc(
                         segId: String,
                         paxId: String,
                         cos: String,
                         fare: String,
                         type: String,
                         promo: Boolean,
                         cosLev: String)

, I currently have a list of 10, all attributes have a value except paxId, all object have "". Now I would like to increment each paxId, I mean like having paxId=1 for the first Alloc, 2 for the second, ... and 10 for the tenth. I have tried this :

val allocWithPaxIds: List[Allocation] = for (id <- 1 to 10) {
        allocs.map(alloc=>Alloc.apply(alloc.segId, id, alloc.cos, alloc.fare, alloc.type, false, alloc.cosLev))
    }

allocs containing the alloc without paxIds

I am beginner in scala and I am lost, hope you'll be able to help me.

Thanks in advance

回答1:


You can use zipWithIndex and then use copy to modify just one field of each object

list.zipWithIndex.map{
   case (v, idx) => v.copy(paxId = idx.toString)
}

回答2:


So, just to clarify what's happening with your implementation.

First, you might need to understand how for comprehension works.

https://docs.scala-lang.org/tutorials/FAQ/yield.html

Scala’s “for comprehensions” are syntactic sugar for composition of multiple operations with foreach, map, flatMap, filter or withFilter.

And the fact that your code doesn't yield anything will be translated into forEach method which has return type Unit that means you will never get list out of it.

I will try to modify your code step by step.

for (id <- 1 to 10) {
   allocs.map(alloc => Alloc.apply(alloc.segId, id, alloc.cos, alloc.fare, alloc.type, false, alloc.cosLev))
}

You don't need to explicitly call apply. The apply method is just a syntactic sugar that if you implement it, you can just use it like a function. In this case, case class has done the job for you.

(more on this topic here: https://twitter.github.io/scala_school/basics2.html#apply)

for (id <- 1 to 10) {
   allocs.map(alloc => Alloc(alloc.segId, id, alloc.cos, alloc.fare, alloc.type, false, alloc.cosLev))
}

And you don't need for-comprehension here, also since the things that will be updated are just id and promo so you can use copy provided by case class.

allocs.map(alloc => alloc.copy(id = id, promo = false))

of just

allocs.map(_.copy(id = id, promo = false))

You want to fill up the id from 1 to 10 so you can just zip them together which will return List[(Int, Alloc)] and map it using partial function to do pattern matching and destructure the tuple.

(more on partial function: https://twitter.github.io/scala_school/pattern-matching-and-functional-composition.html#PartialFunction)

allocs
  .zip(1 to 10)
  .map {
    case (alloc, id) => alloc.copy(id = id.toString, promo = false)
  }

And, yes, if you like you can use zipWithIndex as Mikel suggest.

The last thing I want to point out is, I see type as a property of Alloc which has type String.

This might not be related to the question but you can leverage the power of Scala type system more to ensure the correctness of your program. Since less possible value means more predictability. So you might consider using sum type (or union type) instead.

(more on sum type: http://tpolecat.github.io/presentations/algebraic_types.html#11)

标签: list loops
© 2014 TuiCode, Inc.