我是一名新手,在使用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()
有任何建议/帮助/线索吗?
答案 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
}
}
} )
这对于大型数据集更好,原因有两个:
sorted
在通过java.util.Arrays.sort
)在一系列对象上调用时会得到的。但请注意,quicksort不是一个稳定的排序!我们在这里需要一个稳定的排序,因为我们正在比较所有字段,所以我们可以使用quicksort。sorted
方法将复制数组并对其进行排序。这意味着如果你有数百万行,当你真的只需要一个时,堆上有两个巨大的对象 - 我们在完成排序后不需要未排序的数据。像这样的大型物体通常直接从世代存储中分配出来,在收集之前它们会暂停一段时间。就地排序可以防止不必要地滥用堆,更不用说不必要地复制数据所花费的时间。 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)
Iterator
将toSeq
转换为内存中的集合。