如何对其中的值进行排序

时间:2013-12-26 17:28:18

标签: arrays list scala hashmap

我是一名新手,在使用PHP多年后尝试学习Scala。我试图解决的问题非常简单,但不确定如何在Scala中完成。基本上我正在阅读具有人名,姓和成绩的文件中的内容。我需要读取文件并按等级对名称进行排序。

文件是这样的。

Aoe Samore 3.1
Boe Sbmore 2.2
Coe Scmore 3.9
Doe Sdmore 2.4
Eoe Semore 3.5
Foe Sfmore 2.6
Goe Sgmore 3.7
Hoe Shmore 2.9
Ioe Simore 3.1
Joe Sjmore 1.2
Koe Skmore 3.2
Loe Slmore 4.0

最终结果应该只显示

Loe Slmore 4
Coe Scmore 3.9
Goe Sgmore 3.7
Eoe Semore 3.5
Koe Skmore 3.2
Aoe Samore 3.1
Ioe Simore 3.1
Hoe Shmore 2.9
Foe Sfmore 2.6
Doe Sdmore 2.4
Boe Sbmore 2.2
Joe Sjmore 1.2

这就是我在PHP中的表现

$content = file_get_contents('grd.txt');
$lines = explode("\n",$content);

$grades_with_information = [];

foreach($lines as $line) {
    $temp = explode(' ',$line);
    $temp[2] = (float)$temp[2];
    $grades_with_information[] = $temp;
}
usort($grades_with_information, function($a, $b) {
    if ($a[2] == $b[2]) {
        return 0;
    }
    return ($a[2] > $b[2]) ? -1 : 1;
});

foreach($grades_with_information as $grade_with_information){
    echo  $grade_with_information[0].' '
          .$grade_with_information[1].' '
          .$grade_with_information[2]
          .'<br>';

}

到目前为止,我们如何在Scala中执行此操作

val source = scala.io.Source.fromFile("grd.txt")
val lines = source.getLines()
for (a <- lines) {

}
source.close()

有任何建议/帮助/线索吗?

4 个答案:

答案 0 :(得分:3)

val lines = source.getLines().toSeq.sortBy(x=>x.split(" ")(2).toFloat).reverse

答案 1 :(得分:2)

如果这是一次性代码并且您不关心如何订购具有相同等级的行,那么

lines.toArray.sortBy( -_.split(' ')(2).toFloat )

其中lines是输入行的任何Traversable[String],例如

scala.io.Source.fromFile("grd.txt").getLines

将对它们进行排序。请注意,在空间字符 ' '上拆分比包含单个空格" "的字符串更有效。因为我们否定了比较的等级值,所以我们不必在排序后反转结果。

但是你不希望被抓到为这样的代码贡献一个真实的项目 - 对于大型数据集而言,这是非常低效的,只是部分地对数据进行排序,并且不是特别清楚。

通常,您希望以某种方式对具有相同等级的行进行排序,例如按姓氏和名字的升序排列。通常最简单的方法就是将数据转换为您想要排序的元组 - 元组的compare方法逐个比较 - 然后将排序的元组映射到所需的最终形式:

lines.map( _ split ' ' ).map{
  case Array(first,last,grade) => ( -grade.toFloat, last, first )
}.toArray.sorted.map{ case (g,l,f) => (f,l,-g) }

这种方法也比使用调用sortBy的函数调用toFloat更有效,因为这会导致每次转换多次值 - 每次将值与另一个值进行比较时。最好先预先转换数据,然后在性能很重要的情况下对其进行排序。

当然,通过使用x(0),x(1)和x(2)可以缩短上述时间,而不是将数组解构为first,last和grade,但是使用名称绑定更加清晰

我们安排通过否定等级值来对等级进行降序排序,但这并未推广到非数字数据类型。更一般地说,您可以通过显式compare提供自己的Ordering方法,在这种情况下,您也可以创建最终形式的元组并编写处理该方法的方法(避免最终map 1}}将数据放入最终形式):

lines.map( _ split ' ' ).map{
  case Array(first,last,grade) => ( first, last, grade.toFloat )
}.toArray.sorted( {
  type T = (String,String,Float)  // first name, last name, grade
  new Ordering[T] {
    // grade DESCENDING, then last name, then first name
    def compare( a:T, b:T ) = {
      val cmp3 = b._3 compare a._3  // descending, so b first
      if ( cmp3 == 0 ) {
        val cmp2 = a._2 compare b._2
        if ( cmp2 == 0 ) a._1 compare b._1 else cmp2
      } else
        cmp3
    }
  }
} )

如果有很多条目并且此代码对性能至关重要,您应该使用Sorting.quickSort对阵列进行快速就地排序:

val grades = lines.map( _ split ' ' ).map{
  case Array(first,last,grade) => ( first, last, grade.toFloat )
}.toArray

scala.util.Sorting.quickSort(grades)( {
  type T = (String,String,Float)
  new Ordering[T] {
    // grade DESCENDING, then last name, then first name
    def compare( a:T, b:T ) = {
      val cmp3 = b._3 compare a._3  // descending, so b to a
      if ( cmp3 == 0 ) {
        val cmp2 = a._2 compare b._2
        if ( cmp2 == 0 ) a._1 compare b._1 else cmp2
      } else
        cmp3
    }
  }
} )

这对于大型数据集更好,原因有两个:

  1. 快速排序算法比mergesort更快(这是sorted在通过java.util.Arrays.sort)在一系列对象上调用时会得到的。但请注意,quicksort不是一个稳定的排序!我们在这里需要一个稳定的排序,因为我们正在比较所有字段,所以我们可以使用quicksort。
  2. sorted方法将复制数组并对其进行排序。这意味着如果你有数百万行,当你真的只需要一个时,堆上有两个巨大的对象 - 我们在完成排序后不需要未排序的数据。像这样的大型物体通常直接从世代存储中分配出来,在收集之前它们会暂停一段时间。就地排序可以防止不必要地滥用堆,更不用说不必要地复制数据所花费的时间。
  3. compare方法很难看,因为我们只是使用元组;如果是我,我会定义一个案例类来保存数据并将数据映射到该实例:

    case class Grade( first:String, last:String, grade:Float )
    val grades = lines.map( _ split ' ' ).map{
      case Array(first,last,grade) => Grade( first, last, grade.toFloat )
    }.toArray
    

    然后你可以更干净地编写compare方法:

    scala.util.Sorting.quickSort(grades)(
      new Ordering[Grade] {
        def compare( a:Grade, b:Grade ) = {
          val gradeCmp = b.grade compare a.grade  // descending, so b to a
          if ( gradeCmp == 0 ) {
            val lastCmp = a.last compare b.last
            if ( lastCmp == 0 ) a.first compare b.first else lastCmp
          } else
            gradeCmp
        }
      }
    )
    

答案 2 :(得分:1)

使用三引号粘贴数据。

"""Aoe Samore 3.1
Boe Sbmore 2.2
Coe Scmore 3.9
Doe Sdmore 2.4
Eoe Semore 3.5
Foe Sfmore 2.6
Goe Sgmore 3.7
Hoe Shmore 2.9
Ioe Simore 3.1
Joe Sjmore 1.2
Koe Skmore 3.2
Loe Slmore 4.0"""

然后执行一些字符串操作,按第二列排序,然后反转以获得降序。

scala> data.lines.map(s => s.split(" ")).toList.sortBy(row => row(2).toFloat).reverse

res16: List[Array[String]] = List(Array(Loe, Slmore, 4.0), Array(Coe, Scmore, 3.9), Array(Goe, Sgmore, 3.7), Array(Eoe, Semore, 3.5), Array(Koe, Skmore, 3.2), Array(Ioe, Simore, 3.1), Array(Aoe, Samore, 3.1), Array(Hoe, Shmore, 2.9), Array(Foe, Sfmore, 2.6), Array(Doe, Sdmore, 2.4), Array(Boe, Sbmore, 2.2), Array(Joe, Sjmore, 1.2))

要将其转换为所需的字符串格式,请执行更多字符串操作。

scala> res16.map(_.mkString(" ")).mkString("\n")
res17: String = 
Loe Slmore 4.0
Coe Scmore 3.9
Goe Sgmore 3.7
Eoe Semore 3.5
Koe Skmore 3.2
Ioe Simore 3.1
Aoe Samore 3.1
Hoe Shmore 2.9
Foe Sfmore 2.6
Doe Sdmore 2.4
Boe Sbmore 2.2
Joe Sjmore 1.2

答案 3 :(得分:1)

def getDouble(line: String) = line.split(" ").apply(2).toDouble

for (l <- lines.toSeq.sortBy(i => - getDouble(i)))
  println(l)
  1. 使用IteratortoSeq转换为内存中的集合。
  2. 按否定的双倍值排序。
  3. 打印行或排序集合。