按日期对数组元素进行排序

7

我有以下数组。

dates = ["6/23/2014", "8/5/2014", "8/19/2014", "6/26/2014", "8/19/2014", "8/19/2014",
"7/8/2014", "6/3/2014", "7/30/2014", "7/3/2014", "6/3/2014", "6/26/2014"]

我们的数组存储了10个字符串对象。为了按照我的方式对它们进行排序,我需要重新排列每个元素。

"2014/8/5", "2014/8/19", "2014/8/19", etc.

这样,例如1/1/2014就不会早于12/13/1994。
我尝试过以下方法。
dates.sort_by {|date|date.split(/[\/]/).rotate(-1).join('/')}

我希望你能将这个数组按日期排序,最新的日期排在前面。
实际输出:
8/5/2014
8/19/2014
8/19/2014
8/19/2014
7/8/2014
7/30/2014
7/3/2014
6/3/2014
6/3/2014
6/26/2014

期望输出

8/19/2014
8/19/2014
8/19/2014
8/5/2014
7/30/2014
7/3/2014
6/26/2014
6/3/2014
6/3/2014

解决方案涉及使用 Ruby 数组类 sort_by 方法。我想按日期格式进行排序,其中新日期位于早期日期之前。
我的问题是,如何根据日期对数组进行排序。
如果这是一个愚蠢的问题,请原谅,但我已经搜索了这个网站,找不到任何结果。
提前感谢您的帮助。
2个回答

18

尝试:

dates.sort_by { |s| Date.strptime(s, '%m/%d/%Y') }

如果你希望从最新日期开始:
dates.sort_by { |s| Date.strptime(s, '%m/%d/%Y') }.reverse

6

我不知道为什么你的预期输出只包含12条条目中的9条,并且你提到了一个大小为10的数组,但是我认为你想要排序所有12条。 我更喜欢像@BroiSatse一样的方法,但是我将提供两种不使用Date类方法的方法。 我把它们包括在内是因为它对于那些新接触Ruby的人具有一些有趣的方面。

dates = [
  "6/23/2014", "8/5/2014", "8/19/2014", "6/26/2014", "8/19/2014", "10/19/2014",
  "7/8/2014", "6/3/2014", "7/30/2014", "7/3/2014", "6/3/2014", "6/26/2014"
]

我已经稍微修改了dates,以便包括一个具有两位数月份("10/19/2014")的日期。 按整数数组排序
dates.sort_by { |d| d.split(?/).rotate(-1).map { |e| -e.to_i } }
  #=> ["10/19/2014", "8/19/2014", "8/19/2014", "8/5/2014", "7/30/2014", "7/8/2014",
  #    "7/3/2014", "6/26/2014", "6/26/2014", "6/23/2014", "6/3/2014", "6/3/2014"]

这里是正在发生的事情。
enum = dates.sort_by
  #=> #<Enumerator: [
  #     "6/23/2014", "8/5/2014", "8/19/2014", "6/26/2014", "8/19/2014", "10/19/2014",
  #     "7/8/2014", "6/3/2014", "7/30/2014", "7/3/2014", "6/3/2014", "6/26/2014"
  #   ]:sort_by>

正如你所看到的,enum是一个枚举器。它传递给其块的第一个值是"2014年6月23日",并将其分配给块变量:

d = "6/23/2014"

a = d.split(?/)
  #=> ["6", "23", "2014"]
b = a.rotate(-1)
  #=> ["2014", "6", "23"]
c = b.map { |e| -e.to_i }
  #=> [-2014, -6, -23]

同样地,enum的第三个元素会被转换为:

"8/19/2014".split(?/).rotate(-1).map { |e| -e.to_i }
  #=> [-2014, -8, -19]

sort_by使用Array#<=>来比较元素对。阅读该文档后,您将理解其中的原因:

[-2014, -6, -23] <=> [-2014, -8, -19]
  #=> 1

这意味着在排序中,"8/19/2014"应该排在"6/23/2014"之前。

顺便提一下,必须将月份和日期从字符串转换为整数,因为这些字符串没有对于单个数字有前导零。如果我们将它们保留为字符串,"8" > "15",这不是我们想要的。由于我们无论如何都要转换为整数,所以将它们设置为负数比保留为正数并应用reverse到已排序的数组更容易。

按照年-月-日顺序按字符串排序,并根据需要添加前导零,然后反转

dates.sort_by do |d|
  d.sub(/(\d+)\/(\d+)\/(\d+)/) { "%4d%02d%02d" % [$3, $1, $2].map(&:to_i) }
end.reverse
  #=> ["10/19/2014", "8/19/2014", "8/19/2014", "8/5/2014", "7/30/2014", "7/8/2014",
  #    "7/3/2014", "6/26/2014", "6/26/2014", "6/23/2014", "6/3/2014", "6/3/2014"]

请注意,这里需要说明的是:
arr = dates.map do |d|
  d.sub(/(\d+)\/(\d+)\/(\d+)/) { "%4d%02d%02d" % [$3, $1, $2].map(&:to_i) }
end
  #=> ["20140623", "20140805", "20140819", "20140626", "20140819", "20141019",
  #    "20140708", "20140603", "20140730", "20140703", "20140603", "20140626"]

arr.sort
  #=> ["20140603", "20140603", "20140623", "20140626", "20140626", "20140703",
  #    "20140708", "20140730", "20140805", "20140819", "20140819", "20141019"]

请参阅格式化指令相关的Kernel#sprintf。当使用d指令时,Ruby会将表示整数的字符串视为整数,因此似乎无需使用.map(&:to_i)
正则表达式/(\d+)\/(\d+)\/(\d+)/匹配一个或多个数字(月份),保存在捕获组1中,后跟'/',然后是一个或多个数字(日期),保存在捕获组2中,再跟'/',最后是一个或多个数字(年份),保存在捕获组3中。也可以采用以下方式编写:
/(\d{1,2})\/(\d{1,2})\/(\d{4})/

或者使用命名捕获组:
r = /(?<month>\d{1,2})\/(?<day>\d{1,2})\/(?<year>\d{4})/

dates.sort_by do |d|
  d.sub(r) { "%4d%02d%02d" % [$~[:year], $~[:month], $~[:day]].map(&:to_i) }
end.reverse
  #=> ["10/19/2014", "8/19/2014", "8/19/2014", "8/5/2014", "7/30/2014", "7/8/2014",
  #    "7/3/2014", "6/26/2014", "6/26/2014", "6/23/2014", "6/3/2014", "6/3/2014"]

$~最近的MatchData对象。它等同于Regexp::last_match


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接