Scala 使用求和逻辑通过 属性 从列表转换为映射
Scala convert from list to map by property with sum logic
我是 Scala 的新手,正在尝试使用如下所示的一些求和逻辑从列表转换为映射。
case class ProductProperty(name:String, value:Option[String]= None, options:List[OptionItem]=List())
case class OptionItem(title:Option[String], description:Option[String] = None , price:Int)
val properties = List(ProductProperty(name = "size", value = Some("val1") , options = Some(List(OptionItem(price = 10) , OptionItem(price = 204))),
ProductProperty(name = "size", value = Some("val2") , options = Some(List(OptionItem(price = 122) , OptionItem(price = 240))),
ProductProperty(name = "color", value = Some("val3") , options = Some(List(OptionItem(price = 101) , OptionItem(price = 204))),
ProductProperty(name = "color", value = Some("val13") , options = Some(List(OptionItem(price = 102) , OptionItem(price = 120))),
ProductProperty(name = "Quantity", value = Some("ssval3") , options = Some(List(OptionItem(price = 1011) , OptionItem(price = 204))),
ProductProperty(name = "Quantity", value = Some("ssval13") , options = Some(List(OptionItem(price = 1102) , OptionItem(price = 1210))
)
我需要将其展平并根据他们的名称和计算价格创建新地图。
Map {
"size" -> total price,
"color" -> total price,
"Quantity" -> total price
}
到目前为止我尝试过的:
val optList = properties.map( list =>
list.map(op => op.options
.flatMap(i => List(ProductProperty(name = op.name, value = Some(i.value)))))
.flatten
)
val totalPrice = price.add(optList.map(list =>
list.map(_.options)
.flatten.map(_.price.getOrElse(0)).sum)
.getOrElse(0))
Map((optList.map(_.name).getOrElse(List())) -> totalPrice)
但这是不正确的。
第一个问题是更正示例数据以使其编译。
case class OptionItem(title :Option[String] = None
,description :Option[String] = None
,price :Int)
case class ProductProperty(name :String
,value :Option[String] = None
,options :List[OptionItem] = List())
val properties =
List(ProductProperty("size", Some("val1"), List(OptionItem(price = 10), OptionItem(price = 204)))
,ProductProperty("size", Some("val2"), List(OptionItem(price = 122), OptionItem(price = 240)))
,ProductProperty("color", Some("val3"), List(OptionItem(price = 101), OptionItem(price = 204)))
,ProductProperty("color", Some("val13"), List(OptionItem(price = 102), OptionItem(price = 120)))
,ProductProperty("Quantity", Some("ssval3"), List(OptionItem(price = 1011), OptionItem(price = 204)))
,ProductProperty("Quantity", Some("ssval13"), List(OptionItem(price = 1102), OptionItem(price = 1210))))
之后,应用 groupBy()
、map()
和 fold()
或 reduce()
就是一件简单的事情。 Scala 2.13.x 同时提供所有 3 个。
properties.groupMapReduce(_.name)(_.options.map(_.price).sum)(_+_)
//res0: Map[String,Int] = Map(size -> 576, color -> 527, Quantity -> 3527)
对于旧的 Scala 版本,您必须将其分解为更小的步骤。
properties.groupBy(_.name)
.map{case (k,v) => k -> v.map(_.options.map(_.price).sum).sum}
补充jwvh的回答,可以使用cats (也在2.12
)稍微简化一下代码:
import cats.syntax.all._
val result = properties.foldMap {
case ProductProperty(name, _, options) =>
Map(name -> options.foldMap(_.price))
}
// result: Map[String,Int] = Map(size -> 576, color -> 527, Quantity -> 3527)
这里的技巧是 foldMap
将 map
和 foldLeft
合并为一个步骤;所以 options.foldMap(_.price)
本质上与 options.map(_.price).foldLeft(0)(_ + _)
相同
但是,最有趣的部分是 properties.foldMap
,因为 Monoid
for Maps
基本上将两个映射合并在一起,当它们具有相同的键时,它只是将其值组合在一起.
可以看到代码运行 here.
我是 Scala 的新手,正在尝试使用如下所示的一些求和逻辑从列表转换为映射。
case class ProductProperty(name:String, value:Option[String]= None, options:List[OptionItem]=List())
case class OptionItem(title:Option[String], description:Option[String] = None , price:Int)
val properties = List(ProductProperty(name = "size", value = Some("val1") , options = Some(List(OptionItem(price = 10) , OptionItem(price = 204))),
ProductProperty(name = "size", value = Some("val2") , options = Some(List(OptionItem(price = 122) , OptionItem(price = 240))),
ProductProperty(name = "color", value = Some("val3") , options = Some(List(OptionItem(price = 101) , OptionItem(price = 204))),
ProductProperty(name = "color", value = Some("val13") , options = Some(List(OptionItem(price = 102) , OptionItem(price = 120))),
ProductProperty(name = "Quantity", value = Some("ssval3") , options = Some(List(OptionItem(price = 1011) , OptionItem(price = 204))),
ProductProperty(name = "Quantity", value = Some("ssval13") , options = Some(List(OptionItem(price = 1102) , OptionItem(price = 1210))
)
我需要将其展平并根据他们的名称和计算价格创建新地图。
Map {
"size" -> total price,
"color" -> total price,
"Quantity" -> total price
}
到目前为止我尝试过的:
val optList = properties.map( list =>
list.map(op => op.options
.flatMap(i => List(ProductProperty(name = op.name, value = Some(i.value)))))
.flatten
)
val totalPrice = price.add(optList.map(list =>
list.map(_.options)
.flatten.map(_.price.getOrElse(0)).sum)
.getOrElse(0))
Map((optList.map(_.name).getOrElse(List())) -> totalPrice)
但这是不正确的。
第一个问题是更正示例数据以使其编译。
case class OptionItem(title :Option[String] = None
,description :Option[String] = None
,price :Int)
case class ProductProperty(name :String
,value :Option[String] = None
,options :List[OptionItem] = List())
val properties =
List(ProductProperty("size", Some("val1"), List(OptionItem(price = 10), OptionItem(price = 204)))
,ProductProperty("size", Some("val2"), List(OptionItem(price = 122), OptionItem(price = 240)))
,ProductProperty("color", Some("val3"), List(OptionItem(price = 101), OptionItem(price = 204)))
,ProductProperty("color", Some("val13"), List(OptionItem(price = 102), OptionItem(price = 120)))
,ProductProperty("Quantity", Some("ssval3"), List(OptionItem(price = 1011), OptionItem(price = 204)))
,ProductProperty("Quantity", Some("ssval13"), List(OptionItem(price = 1102), OptionItem(price = 1210))))
之后,应用 groupBy()
、map()
和 fold()
或 reduce()
就是一件简单的事情。 Scala 2.13.x 同时提供所有 3 个。
properties.groupMapReduce(_.name)(_.options.map(_.price).sum)(_+_)
//res0: Map[String,Int] = Map(size -> 576, color -> 527, Quantity -> 3527)
对于旧的 Scala 版本,您必须将其分解为更小的步骤。
properties.groupBy(_.name)
.map{case (k,v) => k -> v.map(_.options.map(_.price).sum).sum}
补充jwvh的回答,可以使用cats (也在2.12
)稍微简化一下代码:
import cats.syntax.all._
val result = properties.foldMap {
case ProductProperty(name, _, options) =>
Map(name -> options.foldMap(_.price))
}
// result: Map[String,Int] = Map(size -> 576, color -> 527, Quantity -> 3527)
这里的技巧是 foldMap
将 map
和 foldLeft
合并为一个步骤;所以 options.foldMap(_.price)
本质上与 options.map(_.price).foldLeft(0)(_ + _)
但是,最有趣的部分是 properties.foldMap
,因为 Monoid
for Maps
基本上将两个映射合并在一起,当它们具有相同的键时,它只是将其值组合在一起.
可以看到代码运行 here.