对包含时间或距离的字符串进行排序

我已经为自定义字符串实现了排序算法,该字符串表示田径事件的时间或距离数据。 以下是格式

’10:03.00 – 十分三秒或十英尺三英寸

排序的结果是,对于场事件,最长的投掷或跳跃将是第一个元素,而对于运行事件,最快的时间将是第一个。 以下是我目前用于现场活动的代码。 我没有发布running_event_sort因为它与大于/小于交换的逻辑相同。 虽然它有效,但它看起来过于复杂,需要重构。 我愿意接受建议。 任何帮助都会很棒。

 event_participants.sort!{ |a, b| Participant.field_event_sort(a, b) } class Participant def self.field_event_sort(a, b) a_parts = a.time_distance.scan(/'([\d]*):([\d]*).([\d]*)/) b_parts = b.time_distance.scan(/'([\d]*):([\d]*).([\d]*)/) if(a_parts.empty? || b_parts.empty?) 0 elsif a_parts[0][0] == b_parts[0][0] if a_parts[0][1] == b_parts[0][1] if a_parts[0][2] > b_parts[0][2] -1 elsif a_parts[0][2]  b_parts[0][1] -1 else 1 end elsif a_parts[0][0] > b_parts[0][0] -1 else 1 end end end 

这种情况下#sort_by可以极大地简化您的代码:

 event_participants = event_participants.sort_by do |s| if s =~ /'(\d+):(\d+)\.(\d+)/ [ $1, $2, $3 ].map { |digits| digits.to_i } else [] end end.reverse 

在这里,我将相关时间解析为整数数组,并将其用作数据的排序键。 数组比较是按条目进行的,第一项是最重要的,所以这很有效。

你不做的一件事是将数字转换为你最有可能想做的整数。 否则,您将遇到"100" < "2" #=> true 。 这就是我添加#map步骤的原因。

此外,在你的正则表达式中, \d的方括号是不必要的,但你确实想要转义句点,因此它与所有字符都不匹配。

我给出的代码的一种方式与你给出的代码不匹配的是一条线不包含任何距离的情况。 您的代码会将它们与周围的行进行比较(如果排序算法假设相等是可传递的,则可能会让您遇到麻烦。这是a == bb == c意味着a ==c ,这不是您的情况代码:例如a = "'10:00.1"b = "frog"c="'9:99:9" )。

#sort_by按升序排序,因此对#reverse的调用会将其更改为降序。 #sort_by还具有仅解析比较值一次的优点,而您的算法必须为每次比较解析每一行。

可能你应该拥有TrackTime和FieldDistance模型,而不是像这样实现。 它们不一定需要持久化 – 参与者模型可以在加载时从time_distance创建它们。

您可能希望能够获得两个值之间的差异,以及将来validation值以及排序值。 该模型可以轻松添加这些function。 此外,它将使unit testing更容易。

我还将时间和距离分成两个单独的字段。 在我的经验中,在数据库中使用双重用途的列只会导致痛苦。

我不知道ruby,但是这里有一些类似c的伪代码,可以重构一下。

 /// In c, I would probably shorten this with the ? operator. int compareIntegers(a, b) { int result = 0; if (a < b) { result = -1; } else if (a > b) { result = 1; } return result; } int compareValues(a, b) { int result = 0; if (!/* check for empty*/) { int majorA = /* part before first colon */ int majorB = /* part before first colon */ int minorA = /* part after first colon */ int minorB = /* part after first colon */ /// In c, I would probably shorten this with the ? operator. result = compareIntegers(majorA, majorB); if (result == 0) { result = compareIntegers(minorA, minorB); } } return result; } 

你的例程看起来不错,但你可以删除”’,’:’和’。’ 并将结果视为数字字符串。 换句话说,10’5“将变为1005,10’4”将是1004.1005显然超过1004。

由于高阶元素位于左侧,因此它将自然排序。 出于同样的原因,这也适用于时间。

我同意转换为整数会更简单。 还要注意整数

 if a > b 1 elsif a < b -1 else 0 

可以简化为a<=>b 。 反向使用-(a <=> b)

在这种情况下:

既然你知道你正在使用英尺,英寸和(无论你的第三个测量单位是什么),为什么不只是创建你要比较的两个值的总和?

所以在这两行之后:

a_parts = a.time_distance.scan(/’([\ d] ):( [\ d] )。([\ d] )/)b_parts = b.time_distance.scan(/’([\ d] ):( [\ d] )。([\ d] )/)

生成a_parts和b_parts的总距离:

totalDistanceA = a_parts [0] [0] .to_i * 12 + a_parts [0] [1] .to_i + b_parts [0] [2] .to_i *(无论你的第三个测量单位是一英寸大小)

totalDistanceB = b_parts [0] [0] .to_i * 12 + b_parts [0] [1] .to_i + b_parts [0] [2] .to_i *(无论你的第三个测量单位是一英寸大小)

然后返回这两个值的比较:

totalDistanceA <=> totalDistanceB

请注意,如果a_parts和b_parts为空,则应保留已经进行检查的validation:

a_parts.empty? || b_parts.empty?

对于按时间场景排序,除了使用不同因素(例如,60秒到一分钟)之外,执行完全相同的操作。

为什么不呢

 a_val = a_parts[0][0].to_i * 10000 + a_parts[0][1].to_i * 100 + a_parts[0][2].to_i b_val = b_parts[0][0].to_i * 10000 + b_parts[0][1].to_i * 100 + b_parts[0][2].to_i a_val <=> b_val 

减去等数字没有意义,但它们应该排序好。

您可能要检查[1]和[2]始终是正则表达式中的两位数。