【问题标题】:Sorting a list of generic objects对通用对象列表进行排序
【发布时间】:2018-03-06 15:47:52
【问题描述】:

我需要编写一个对Seq[T] 对象执行排序的通用代码。我知道在我们知道base class 及其attributes 之前,它不可能执行排序操作。在查看了answer 之后,我使用了这段代码,我的要求是处理尽可能多的自定义数据类型。

case class Country(name: String, id : Int)
type CountrySorter = (Country, Country) => Boolean
def byName : CountrySorter = (c1:Country, c2:Country) => c1.name < c2.name
def byId : CountrySorter = (c1:Country, c2:Country) => (c1.id < c2.id)

val sortingMap = Map[String, CountrySorter](
  "sortByCountryName" -> byName ,
  "soryByCountryId" -> byId
 )

函数调用

def sort[T]( input : Seq[T], criteria : String) : Seq[T] = {
  input.sortWith(sortingMap(criteria))
}

input.sortWith(sortingMap(criteria)) 在这里我收到错误,因为 sortWith 函数只采用 Country 类型而不是 T 类型。

【问题讨论】:

    标签: scala


    【解决方案1】:

    如果您想使用sortWith 定义您的排序,这是一种方法:

    case class Country(name: String, id : Int)
    
    type Sorter[T] = (T, T) => Boolean
    type CountrySorter = Sorter[Country]
    
    def byName : CountrySorter = (c1, c2) => c1.name < c2.name
    def byId : CountrySorter = (c1, c2) => c1.id < c2.id
    
    def sort[T](input: Seq[T], sorter: Sorter[T]): Seq[T] = {
      input.sortWith(sorter)
    }
    
    val countries = List(Country("Australia", 61), Country("USA", 1), Country("France", 33))
    
    sort(countries, byName)
    // res1: Seq[Country] = List(Country(Australia,61), Country(France,33), Country(USA,1))
    
    sort(countries, byId)
    // res2: Seq[Country] = List(Country(USA,1), Country(France,33), Country(Australia,61))
    

    【讨论】:

      【解决方案2】:

      使用带有字符串类型键的Map 对国家/地区进行排序很容易出错。更好的选择是通过Ordering[A] 类型类利用Scala 中的排序机制。

      你可以这样使用它:

      def sort[T](input : Seq[T])(implicit order: Ordering[T]): Seq[T] = {
        input.sorted
      }
      

      这里的关键是在范围内有正确的排序。您可以在范围内创建单个临时排序:

      def main(args: Array[String]): Unit = {
        implicit val byIdOrdering = Ordering.by((country: Country) => country.id)
      
        val countries: Seq[Country] = ???
        sort(countries)
      }
      

      您可以在案例类的伴侣中定义排序并显式导入:

      object Country {
        implicit val byIdOrdering: Ordering[Country] = 
           Ordering.by((country: Country) => country.id)
      
        implicit val byNameOrdering: Ordering[Country] = 
           Ordering.by((country: Country) => country.name)
      }
      
      import Country.byNameOrdering
      def main(args: Array[String]): Unit = {
        val countries: Seq[Country] = ???
        sort(countries)
      }
      

      如果您有这样的订购规则,也可以使用low priority implicits trick

      trait LowPriorityCountryImplicits {
        implicit val byNameOrdering: Ordering[Country] = 
          Ordering.by((country: Country) => country.name)
      }
      
      object HighPriorityCountryImplicits extends LowPriorityCountryImplicits {
        implicit val byIdOrdering: Ordering[Country] = 
          Ordering.by((country: Country) => country.id)
      }
      
      import HighPriorityCountryImplicits._
      def main(args: Array[String]): Unit = {
        val countries: Seq[Country] = ???
        sort(countries)
      }
      

      如果需要,甚至可以显式传递排序:

      def main(args: Array[String]): Unit = {
        val countries: Seq[Country] = ???
        sort(countries)(Country.byNameOrdering)
      }
      

      【讨论】:

      • 使用implicit 不是满足此要求的正确方法,因为我应该动态更改排序顺序,即我需要为每个新排序顺序引入一个新函数。
      • @PuneethReddyV 动态的动态如何?你如何决定?您始终可以通过自己提供排序显式传递。
      • 在我的要求中,我将从 UI 中获取一个字符串,该字符串描述了排序顺序。您已经为这种方法使用了显式的,这没有任何问题,但是对于这个要求,我怎么可能仅通过 string 值将排序顺序带入范围?
      • @PuneethReddyV 我明白你的意思。我们可以通过在字符串上切换/if-elseing 并在范围内产生正确的顺序来完成这项工作,但它最终可能比使用 sortWith 更麻烦。
      • 谢谢你的回答和我的评论。
      【解决方案3】:

      使用上述答案后,我已使用以下代码满足此要求

      作为所有子案例类的父级的通用特征,即仅包含执行排序的字段

       sealed trait Generic{
          def name : String = ???
          def id : Int = ???
          def place : String = ???
        }
      
       //case class which need to be sorted 
        case class Capital( 
            countryName : String, 
            override val id: Int, 
            override val place:String
       ) extends Generic
      
        case class Country(
               override val name: String, 
               override val id: Int
        ) extends Generic
      

      排序类型

        type Sorter[T] = (T, T) => Boolean
        type CountrySorter = Sorter[Generic]
        type CapitalSorter = Sorter[Generic]
      

      排序顺序

        def byName : CountrySorter = (c1, c2) => c1.name < c2.name
      
        def byId : CountrySorter = (c1, c2) => c1.id < c2.id
      
        def byPlace : CapitalSorter = (s1, s2) => s1.place > s2.place
      

      排序方法

        def sort[T](input: Seq[T], sorter: Sorter[T]): Seq[T] = {
          input.sortWith(sorter)
        }
      

      用于保存排序顺序的数据结构。

        val mapper = Map[String, Sorter[Generic]](
              "name" -> byName, 
              "id" -> byId, 
              "place" -> byPlace
             )
      

      输入

        val countries = List(Country("Australia", 61), Country("USA", 1), Country("France", 33))
        val headQuaters = List(
          Capital("Australia", 61, "Melbourne"), 
          Capital("America", 1, "New York"), 
          Capital("France", 33, "Paris"), 
          Capital("India", 65, "New Delhi")
       )
      

      输出

        println(sort(countries,mapper("id")))
       //List(Country(USA,1), Country(France,33), Country(Australia,61))
      
        println(sort(headQuaters , mapper("place")))
        //List(Capital(France,33,Paris), Capital(America,1,New York), Capital(India,65,New Delhi), Capital(Australia,61,Melbourne))
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-10-20
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多