以下代码成功,但有更好的方法做同样的事情吗?也许是案例类特有的东西?在下面的代码中,对于我的简单案例类中String类型的每个字段,代码遍历我的案例类的实例列表,并找到该字段的最长字符串的长度。
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.0/knockout-min.js"></script>
Users:
<ul data-bind="foreach: users">
<li>
User <strong data-bind="text: uuid"></strong> -
<a href="#" class="pull-right" data-bind="click: $root.CheckTimeOut">CheckTimeOut Manually</a>
Recent response:
<!-- ko with: recentResponse -->
<span data-bind="text: txt, css: { success: val }" class="msg"></span>
<!-- /ko -->
</li>
</ul>
结果:
case class CrmContractorRow(
id: Long,
bankCharges: String,
overTime: String,
name$id: Long,
mgmtFee: String,
contractDetails$id: Long,
email: String,
copyOfVisa: String)
object Go {
def main(args: Array[String]) {
val a = CrmContractorRow(1,"1","1",4444,"1",1,"1","1")
val b = CrmContractorRow(22,"22","22",22,"55555",22,"nine long","22")
val c = CrmContractorRow(333,"333","333",333,"333",333,"333","333")
val rows = List(a,b,c)
c.getClass.getDeclaredFields.filter(p => p.getType == classOf[String]).foreach{f =>
f.setAccessible(true)
println(f.getName + ": " + rows.map(row => f.get(row).asInstanceOf[String]).maxBy(_.length))
}
}
}
答案 0 :(得分:11)
如果你想使用Shapeless做这种事情,我强烈建议你定义一个自定义类型类来处理复杂的部分,并允许你将这些东西与其余的逻辑分开。
在这种情况下,这听起来像你要特别尝试的一个棘手的部分是从一个案例类的所有String
成员获得从字段名到字符串长度的映射。这是一个类型类:
import shapeless._, shapeless.labelled.FieldType
trait StringFieldLengths[A] { def apply(a: A): Map[String, Int] }
object StringFieldLengths extends LowPriorityStringFieldLengths {
implicit val hnilInstance: StringFieldLengths[HNil] =
new StringFieldLengths[HNil] {
def apply(a: HNil): Map[String, Int] = Map.empty
}
implicit def caseClassInstance[A, R <: HList](implicit
gen: LabelledGeneric.Aux[A, R],
sfl: StringFieldLengths[R]
): StringFieldLengths[A] = new StringFieldLengths[A] {
def apply(a: A): Map[String, Int] = sfl(gen.to(a))
}
implicit def hconsStringInstance[K <: Symbol, T <: HList](implicit
sfl: StringFieldLengths[T],
key: Witness.Aux[K]
): StringFieldLengths[FieldType[K, String] :: T] =
new StringFieldLengths[FieldType[K, String] :: T] {
def apply(a: FieldType[K, String] :: T): Map[String, Int] =
sfl(a.tail).updated(key.value.name, a.head.length)
}
}
sealed class LowPriorityStringFieldLengths {
implicit def hconsInstance[K, V, T <: HList](implicit
sfl: StringFieldLengths[T]
): StringFieldLengths[FieldType[K, V] :: T] =
new StringFieldLengths[FieldType[K, V] :: T] {
def apply(a: FieldType[K, V] :: T): Map[String, Int] = sfl(a.tail)
}
}
这看起来很复杂,但是一旦你开始使用Shapeless,你就会学会在睡梦中写下这种东西。
现在,您可以以相对简单的方式编写操作的逻辑:
def maxStringLengths[A: StringFieldLengths](as: List[A]): Map[String, Int] =
as.map(implicitly[StringFieldLengths[A]].apply).foldLeft(
Map.empty[String, Int]
) {
case (x, y) => x.foldLeft(y) {
case (acc, (k, v)) =>
acc.updated(k, acc.get(k).fold(v)(accV => math.max(accV, v)))
}
}
然后(问题中定义的rows
):
scala> maxStringLengths(rows).foreach(println)
(bankCharges,3)
(overTime,3)
(mgmtFee,5)
(email,9)
(copyOfVisa,3)
这适用于任何案例类。
如果这是一次性的事情,你可以使用运行时反射,或者你可以在Giovanni Caporaletti的答案中使用Poly1
方法 - 它不那么通用,它混合了解决方案的不同部分我不喜欢的方式,但它应该工作得很好。如果这是你做了很多事情的话,我会建议我在这里给出的方法。
答案 1 :(得分:3)
如果你想使用无形来获取案例类的字符串字段并避免反射,你可以这样做:
import shapeless._
import labelled._
trait lowerPriorityfilterStrings extends Poly2 {
implicit def default[A] = at[Vector[(String, String)], A] { case (acc, _) => acc }
}
object filterStrings extends lowerPriorityfilterStrings {
implicit def caseString[K <: Symbol](implicit w: Witness.Aux[K]) = at[Vector[(String, String)], FieldType[K, String]] {
case (acc, x) => acc :+ (w.value.name -> x)
}
}
val gen = LabelledGeneric[CrmContractorRow]
val a = CrmContractorRow(1,"1","1",4444,"1",1,"1","1")
val b = CrmContractorRow(22,"22","22",22,"55555",22,"nine long","22")
val c = CrmContractorRow(333,"333","333",333,"333",333,"333","333")
val rows = List(a,b,c)
val result = rows
// get for each element a Vector of (fieldName -> stringField) pairs for the string fields
.map(r => gen.to(r).foldLeft(Vector[(String, String)]())(filterStrings))
// get the maximum for each "column"
.reduceLeft((best, row) => best.zip(row).map {
case (kv1@(_, v1), (_, v2)) if v1.length > v2.length => kv1
case (_, kv2) => kv2
})
result foreach { case (k, v) => println(s"$k: $v") }
答案 2 :(得分:2)
您可能想要使用Scala反射:
import scala.reflect.runtime.universe._
val rm = runtimeMirror(getClass.getClassLoader)
val instanceMirrors = rows map rm.reflect
typeOf[CrmContractorRow].members collect {
case m: MethodSymbol if m.isCaseAccessor && m.returnType =:= typeOf[String] =>
val maxValue = instanceMirrors map (_.reflectField(m).get.asInstanceOf[String]) maxBy (_.length)
println(s"${m.name}: $maxValue")
}
这样您就可以避免出现以下情况的问题:
case class CrmContractorRow(id: Long, bankCharges: String, overTime: String, name$id: Long, mgmtFee: String, contractDetails$id: Long, email: String, copyOfVisa: String) {
val unwantedVal = "jdjd"
}
干杯
答案 3 :(得分:0)
我已将您的代码重构为更可重用的代码:
import scala.reflect.ClassTag
case class CrmContractorRow(
id: Long,
bankCharges: String,
overTime: String,
name$id: Long,
mgmtFee: String,
contractDetails$id: Long,
email: String,
copyOfVisa: String)
object Go{
def main(args: Array[String]) {
val a = CrmContractorRow(1,"1","1",4444,"1",1,"1","1")
val b = CrmContractorRow(22,"22","22",22,"55555",22,"nine long","22")
val c = CrmContractorRow(333,"333","333",333,"333",333,"333","333")
val rows = List(a,b,c)
val initEmptyColumns = List.fill(a.productArity)(List())
def aggregateColumns[Tin:ClassTag,Tagg](rows: Iterable[Product], aggregate: Iterable[Tin] => Tagg) = {
val columnsWithMatchingType = (0 until rows.head.productArity).filter {
index => rows.head.productElement(index) match {case t: Tin => true; case _ => false}
}
def columnIterable(col: Int) = rows.map(_.productElement(col)).asInstanceOf[Iterable[Tin]]
columnsWithMatchingType.map(index => (index,aggregate(columnIterable(index))))
}
def extractCaseClassFieldNames[T: scala.reflect.ClassTag] = {
scala.reflect.classTag[T].runtimeClass.getDeclaredFields.filter(!_.isSynthetic).map(_.getName)
}
val agg = aggregateColumns[String,String] (rows,_.maxBy(_.length))
val fieldNames = extractCaseClassFieldNames[CrmContractorRow]
agg.map{case (index,value) => fieldNames(index) + ": "+ value}.foreach(println)
}
}
使用无形将摆脱.asInstanceOf,但本质将是相同的。给定代码的主要问题是它不可重复使用,因为聚合逻辑与反射逻辑混合以获取字段名称。