【问题标题】:Sort array of hashes by a value按值对哈希数组进行排序
【发布时间】:2015-05-12 05:35:19
【问题描述】:

我有一个包含哈希的数组。我想按created_at 值对其进行排序。下面是数组结构的一个例子:

注意,我已经写了人类可读的日期,值将是时间戳。

[
  {"group1"=>[
                  {:item1=>[{"name" => "Tim", "created_at"=>"4 weeks ago"}]}, 
                  {:item2=>[{"name" => "Jim", "created_at"=>"3 weeks ago"}]}, 
                  {:item3=>[{"name" => "Ted", "created_at"=>"2 weeks ago"}]}, 
             ]
  }, 
  {"group2"=>[
               {:item1=>[{"name" => "Sally", "created_at"=>"1 month ago"}]}, 
               {:item2=>[{"name" => "Willa", "created_at"=>"2 months ago"}]}, 
               {:item3=>[{"name" => "Sammi", "created_at"=>"4 months ago"}]}, 
             ] 
  },
  {"group3"=>[
                 {:item1=>[{"name" => "Jeff", "created_at"=>"1 month ago"}]}, 
                 {:item2=>[{"name" => "Lois", "created_at"=>"1 day ago"}]}, 
                 {:item3=>[{"name" => "Lisa", "created_at"=>"1 week ago"}]}, 
             ] 
  }
]

我想整理以上数据,以便输出首先是 group3,因为它包含一个 item 和 1 天前的 created_at 值。接下来是group1,因为它包含一个值为 2 周前的项目,group2 将是最后一个,因为它最近的日期是一个月前。

如何重新排列这些数据?

我在想我可能不得不做一些类似的事情

 array_of_nested_hashes.each do |a|
      a.sort_by { |k, v| v[:created_at] }
 end

按日期对每个组中的数据进行排序,然后按第一个哈希的日期对每个组进行排序 - 因为这将是每个组中最近的哈希,给我完全排序的哈希,如下所示:

[
  {"group3"=>[
                 {:item2=>[{"name" => "Lois", "created_at"=>"1 day ago"}]}, 
                 {:item3=>[{"name" => "Lisa", "created_at"=>"1 week ago"}]}, 
                 {:item1=>[{"name" => "Jeff", "created_at"=>"1 month ago"}]},
             ] 
  },
  {"group1"=>[
                  {:item3=>[{"name" => "Ted", "created_at"=>"2 weeks ago"}]}, 
                  {:item2=>[{"name" => "Jim", "created_at"=>"3 weeks ago"}]}, 
                  {:item1=>[{"name" => "Tim", "created_at"=>"4 weeks ago"}]},
             ]
  }, 
  {"group2"=>[
               {:item1=>[{"name" => "Sally", "created_at"=>"1 month ago"}]}, 
               {:item2=>[{"name" => "Willa", "created_at"=>"2 months ago"}]}, 
               {:item3=>[{"name" => "Sammi", "created_at"=>"4 months ago"}]}, 
             ] 
  },
]

【问题讨论】:

  • 你能告诉我们想要的输出是什么样的吗?时间戳也很好,否则排序将查看整数,然后查看下一个字母。即 1 小时前 > 1 天前,尽管您的示例似乎没问题。
  • 在这种情况下,所需的输出是重新排序的哈希。碰巧你之前回答了我的一个问题-顺便说一句,我正在使用的哈希值与以前相同。
  • 谢谢,在 group_3 的输出中,为什么 1 个月高于 1 天?
  • 好点,我只是专注于根据内容将“组”按正确的顺序排列,而不是完全整理出来,我在问题中添加了更多内容以显示我的想法那。很抱歉说这是我想要的输出,因为我不需要保留嵌套哈希的顺序。
  • 我误解了你的问题吗?我的理解是,您想按每个"group" 的最新"created_at" 值进行排序,并将所有值转换为天数(例如,出于以下目的,"2 weeks" 将转换为14 天数种类)。根据这个假设,如果"group3" 的三个"created_at" 值是"2 weeks", "6 days", "3 months",它仍然会排在第一位,因为"6 days" 将胜过"2 weeks ago""1 month ago"。这是我做出的假设,但我看到其他人给出的答案做出了不同的假设。

标签: ruby-on-rails arrays ruby hash


【解决方案1】:

这是我的尝试。工作流程是:

1) 对所有内部数组进行排序以获得最大值(即最近的 数字时间戳)到第一个索引。

2) 在内层数组中已知位置(索引0)的最大值,根据第一个的值对外层数组进行排序 在它们的内部数组中索引。

# Part 1
outer_list.map! do |h|
    Hash[h.map do |k, v|
        v = v.sort_by do |hsh|
            hsh.first[1][0]['created_at'].to_i
        end.reverse!
        [k, v]
    end]
end

# Part 2
sorted = outer_list.sort_by do |h|
    h.first[1][0].first[1][0]['created_at'].to_i
end.reverse!

【讨论】:

  • 我的想法是这样的,谢谢你的建议,我会试试的。从外观上看,这就是我所需要的。非常感谢
  • @MohammadAbuShady - 在他的问题中,他提到created_at 实际上是一个数字时间戳,而不是一个字符串。
  • 哇,那个代码块太误导人了..我是根据字符串来思考的..谢谢
  • @MohammadAbuShady,我也很困惑,因为它们是字符串。问题是否因编辑而改变? (直到问题发布后三个小时左右我才看到问题。)
  • @CarySwoveland - 问题的第 4 句提到了它。
【解决方案2】:

编辑:

这是对问题正确解释的答案:

arr = [
  {"g1"=>[{i1: [{"ca"=>-28}]}, {i2: [{"ca"=>-21}]}, {i3: [{"ca"=>-14} ]}]}, 
  {"g2"=>[{i1: [{"ca"=>-30}]}, {i2: [{"ca"=>-60}]}, {i3: [{"ca"=>-120}]}]},
  {"g3"=>[{i1: [{"ca"=>-30}]}, {i2: [{"ca"=>-1}]},  {i3: [{"ca"=>-7}  ]}]}
]

arr.sort_by { |h| h.first.last.map { |g| g["ca"] }.max }.reverse
  #=> [{"g3"=>...}, {"g1"=>...}, {"g2"=>...}]

下面的大部分解释也适用于这个答案。

这是您可以做到的一种方式,让arr 表示您希望排序的哈希数组。

代码

PER_SIZE = { 'day'=>1, 'week'=>7, 'month'=>30 }

arr.sort_by do |g|
  g.first.last.map do |h|
    n, period = h.first.last.first["created_at"].scan(/(\d+) ([a-rt-z]+)/).first
    n.to_i * PER_SIZE[period]
  end.min
end
  #=>[{"group3"=>[{:item2=>[{"name"=>"Lois", "created_at"=>"1 day ago"}]},
  #               {:item3=>[{"name"=>"Lisa", "created_at"=>"1 week ago"}]},
  #               {:item1=>[{"name"=>"Jeff", "created_at"=>"1 month ago"}]}]},
  #   {"group1"=>[{:item3=>[{"name"=>"Ted", "created_at"=>"2 weeks ago"}]},
  #               {:item2=>[{"name"=>"Jim", "created_at"=>"3 weeks ago"}]},
  #               {:item1=>[{"name"=>"Tim", "created_at"=>"4 weeks ago"}]}]},
  #   {"group2"=>[{:item1=>[{"name"=>"Sally", "created_at"=>"1 month ago"}]},
  #               {:item2=>[{"name"=>"Willa", "created_at"=>"2 months ago"}]},
  #               {:item3=>[{"name"=>"Sammi", "created_at"=>"4 months ago"}]}]}]

说明

排序可以通过将每个日期字符串转换为天数来完成。我们首先将变量分配给枚举器arr.sort_by。然后我们可以使用Enumerator#next 获取枚举器的每个值,然后将其传递给块。

enum = arr.sort_by
  #=> #<Enumerator:
  #     [{"group1"=>[{:item1=>
  #       [{"name"=>"Tim", "created_at"=>"4 weeks ago"}]},...
  #   :sort_by>

现在将枚举数的第一个值赋给块变量:

g = enum.next
  #=> {"group1"=>[{:item1=>[{"name"=>"Tim", "created_at"=>"4 weeks ago"}]},
  #               {:item2=>[{"name"=>"Jim", "created_at"=>"3 weeks ago"}]},
  #               {:item3=>[{"name"=>"Ted", "created_at"=>"2 weeks ago"}]}]} 
arr1 = g.first.last
  #=> ["group1", [{:item1=>[{"name"=>"Tim", "created_at"=>"4 weeks ago"}]},
  #               {:item2=>[{"name"=>"Jim", "created_at"=>"3 weeks ago"}]},
  #               {:item3=>[{"name"=>"Ted", "created_at"=>"2 weeks ago"}]}]]
arr1
  #=> [{:item1=>[{"name"=>"Tim", "created_at"=>"4 weeks ago"}]},
  #    {:item2=>[{"name"=>"Jim", "created_at"=>"3 weeks ago"}]},
  #    {:item3=>[{"name"=>"Ted", "created_at"=>"2 weeks ago"}]}] 

maparr 的第一个元素传递给块,将其分配给块变量:

h = {:item1=>[{"name"=>"Tim", "created_at"=>"4 weeks ago"}]}

arr2 = h.first.last
  #=> [{"name"=>"Tim", "created_at"=>"4 weeks ago"}] 

s = arr2.first["created_at"]
  #=> "4 weeks ago" 
arr3 = s.scan(/(\d+) ([a-rt-z]+)/)
  #=> [["4", "week"]] 
n, period = arr3.first
  #=> ["4", "week"] 
n      #=> "4" 
period #=> "week" 
n.to_i * PER_SIZE[period]
  #=> 4 * PER_SIZE['week']
  #=> 4 * 7 => 28

同样,arr1 的第二个和第三个元素分别映射到2114(天)。然后我们计算:

[28, 21, 14].min
  #=> 14

这是sort_by 用于arr[0] 的值。同样,arr[1]sort_by 值为:

[30, 60, 120].min
  #=> 30

对于arr[2] 是:

[30, 1, 7].min
  #=> 1

因此,arr 被排序为:

[arr[3], arr[1], arr[2]]

【讨论】:

  • 顺便说一句,我发现这个宝石可以让你的方法更容易github.com/mojombo/chronic
  • 谢谢,@MohammadAbuShady。我希望这对编写涉及以不同方式表示的日期的应用程序的人很有帮助。
  • 这对于那些希望将字符串转换为日期的人来说是一个很好的答案,我相信其他人会发现这非常有用。看起来不错,cmets和解释很有用,我相信这对人们会有很大的帮助。
  • 您会看到我进行了编辑以解决实际问题。
【解决方案3】:

知道它们实际上是时间戳之后..

这是我的答案

obj = {that huge array} 
sorted_obj = obj.sort_by do |groups|
  groups.values.map do |items|
    items.map do |item|
      item.values.flatten.first['created_at']
    end.max
  end
end

【讨论】:

  • 感谢您的回答,非常感谢,不幸的是它没有提供正确的输出,因为后面的值被从“项目”哈希中删除,所以只有最新的。即使“项目”的最终内容的实际顺序并不重要,我也需要所有值。
猜你喜欢
  • 2020-04-05
  • 2011-03-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-28
  • 1970-01-01
  • 2019-04-25
相关资源
最近更新 更多