需求分析
并没有什么需求分析
依然是成绩订阅那个程序,自从我开始重构,我发现下面这个方法写的太恶心了,或者说,太java了。
课程名 | 成绩 | 考试时间 |
---|---|---|
A | 90 | 20150721 |
B | 92 | 20150723 |
C | 90 | 20150729 |
D | 97 | 20150801 |
E | 95 | 20160121 |
F | 88 | 20160121 |
G | 90 | 20160125 |
H | 85 | 20160721 |
I | 85 | 20160721 |
J | 80 | 20160921 |
K | 90 | 20160921 |
这是所有的考试成绩,以一个形如下面的Hash结构为元素,组成的一维数组
[
{ name: A, grade: 90, date: 20150721 },
{ name: B, grade: 92, date: 20150723 },
{ name: C, grade: 90, date: 20150729 }
]
由于我订阅成绩通常只需要知道当前学期,而且计算GPA也是分学期计算,所以我需要把这些数据按学期分开来。
通常情况下,同一学期的考试都是在同一天,但考虑到有些同学挂科补考的情况,我必须要考虑到各种客户的需求,有人问重修的怎么算?
算你麻痹 算新学期的考试。
所以我的任务是把这个一维数组,按照学期,变成一个二维数组,第一层是不同的学期,第二层是某学期的所有成绩列表。
我把5.1~11.1之间的考试都当做是第二学期,因为有开学补考嘛。所以算法思路是,通过判断是否为第二学期,如果是将flag置为true,如果不是则置为false,于是循环的时候,前几个都是true,然后到E科目的时候,就变成了false,所以这时候判定到了新学期,把这个下标记录下来,放到res数组里,如此遍历一遍,就可以把转折点的下标记录到一个数组里。
然后再根据res中每一个下标位置,用slice方法切割成绩数组,压入目标数组。
于是写出来了下面这个我后来都看不懂的代码。
def nest_with_date
res = []
z = 0
second_semester = 500...1100
flag = false
@guide_score_list.each do |s|
z += 1
date = s[:date].slice(-4..-1).to_i
res << (z - 1) unless flag == second_semester.include?(date)
flag = second_semester.include?(date)
end
res << z
res.length.times do |i|
@score_list << @guide_score_list.slice((i.zero? ? 0 : res[i - 1])...res[i])
end
end
Array#inject使用方法
于是我想到了最近看到的inject方法,就学习了一下姿势。
用法举例如下
list = [1,2,3,4]
result = list.inject(7) do |res, obj|
res + obj
end
result # => 17
当inject加了参数,这个参数就是第一次迭代时res的初始值,此时res==7,obj==1,也就是第一个元素的值。
执行res+obj,返回值为8,这个8赋值给res,obj指向下一个元素,也就是2.
执行res+obj,返回值为10,这个10赋值给res,obj指向下一个...
以此类推
最后迭代完毕时res值就是整个方法的返回值,赋值给result变量就行了。
如果inject没有传参数,那么res的初始值就是list的第一个元素值——1,obj指向第二个元素值——2,然后同上
魔改重构
def nest_with_date
# Magic rather than readable
@score_list = @guide_score_list.inject([[@guide_score_list.first]]) do |res, s|
next res if s == @guide_score_list.first
l = (5_01...11_01).include?(res.last.last[:date].slice(-4..-1).to_i)
n = (5_01...11_01).include?(s[:date].slice(-4..-1).to_i)
l ^ n ? res << Array[s] : res.last << s
res
end
end
原本14行代码被我强行魔改成6行,注释里还要写上拒绝可读性...
这个算法的思路就更清晰了,实际上算法没什么变化,只是强行吃口语法糖。
我给res赋值了初始值,就是
[[{ name: A, grade: 90, date: 20150721 }]]
l
和n
变量分别表示上一个成绩和当前这个成绩的日期是否在第二学期内
当这两个值相等时则表示在同一个学期,就把这个数据压入最后一个学期的第二层
当这两个值不等时则表示在不同的学期,要新开辟一个学期,所以把这个成绩包装成数组,压入第一层的尾部
而l ^ n
则表示异或关系,就不多说了。
这样目的就达到了。
人生苦短,我选Ruby~