按条目属性对数组进行排序,并使用可选的分组

时间:2013-07-28 13:54:44

标签: ruby arrays sorting

我有一个如下所示的数组:

[
    {"timestamp" => 1347119549, "category" => nil},
    {"timestamp" => 1347119547, "category" => "Monkeys"},
    {"timestamp" => 1347119543, "category" => nil},
    {"timestamp" => 1347119542, "category" => "Monkeys"}
]

我希望按时间戳(降序)对其进行排序,除非它的类别不是nil,在这种情况下它应该与它的“兄弟”一起出现,即使它比未分类的条目“更旧”。我需要对这个数组进行排序,所以看起来像这样:

[
    {"timestamp" => 1347119549, "category" => nil},
    {"timestamp" => 1347119547, "category" => "Monkeys"},
    {"timestamp" => 1347119542, "category" => "Monkeys"},
    {"timestamp" => 1347119543, "category" => nil}
]

我想弄清楚如何使用group_bysort来获得正确的结果,但没有成功。

4 个答案:

答案 0 :(得分:2)

require 'pp'

ar = [
    {"timestamp" => 1347119549, "category" => nil},
    {"timestamp" => 1347119547, "category" => "Monkeys"},
    {"timestamp" => 1347119543, "category" => nil},
    {"timestamp" => 1347119542, "category" => "Monkeys"}
]

pp ar.group_by{|h| h['category'] ? h['category'] : h['timestamp']}.
   map{|k,v| v.sort_by{|h| -h['timestamp']}}.
   sort_by{|a| -a[0]['timestamp']}.flatten
# >> [{"timestamp"=>1347119549, "category"=>nil},
# >>  {"timestamp"=>1347119547, "category"=>"Monkeys"},
# >>  {"timestamp"=>1347119542, "category"=>"Monkeys"},
# >>  {"timestamp"=>1347119543, "category"=>nil}]

require 'pp'

a = [
  {"timestamp"=>1347119549, "category"=>nil},
  {"timestamp"=>1347119547, "category"=>"Monkeys"},
  {"timestamp"=>1347119543, "category"=>nil},
  {"timestamp"=>1347119542, "category"=>"Monkeys"},
  {"timestamp"=>1347119548, "category"=>"Dog"},
  {"timestamp"=>1347119544, "category"=>"Dog"}
]

pp a.group_by{|h| h['category'] ? h['category'] : h['timestamp']}.
   map{|k,v| v.sort_by{|h| -h['timestamp']}}.
   sort_by{|a| -a[0]['timestamp']}.flatten 
# >> [{"timestamp"=>1347119549, "category"=>nil},
# >>  {"timestamp"=>1347119548, "category"=>"Dog"},
# >>  {"timestamp"=>1347119544, "category"=>"Dog"},
# >>  {"timestamp"=>1347119547, "category"=>"Monkeys"},
# >>  {"timestamp"=>1347119542, "category"=>"Monkeys"},
# >>  {"timestamp"=>1347119543, "category"=>nil}]

答案 1 :(得分:1)

看起来有点难看,但它有效:

a = [
  {"timestamp"=>1347119549, "category"=>nil},
  {"timestamp"=>1347119547, "category"=>"Monkeys"},
  {"timestamp"=>1347119543, "category"=>nil},
  {"timestamp"=>1347119542, "category"=>"Monkeys"},
  {"timestamp"=>1347119548, "category"=>"Dog"},
  {"timestamp"=>1347119544, "category"=>"Dog"}
]
groups = a.sort_by {|h| -h['timestamp']}.group_by {|h| h['category']}
sorted = (groups.delete(nil) || []) + groups.values
sorted = sorted.sort_by{|i| i.is_a?(Hash) ? -i['timestamp'] : -i.first['timestamp']}.flatten

这会在sorted中为您提供以下内容:

[
  {"timestamp"=>1347119549, "category"=>nil},
  {"timestamp"=>1347119548, "category"=>"Dog"},
  {"timestamp"=>1347119544, "category"=>"Dog"},
  {"timestamp"=>1347119547, "category"=>"Monkeys"},
  {"timestamp"=>1347119542, "category"=>"Monkeys"},
  {"timestamp"=>1347119543, "category"=>nil}
]

我先按'timestamp'排序,以便稍后对这些组进行排序。

'category'分组后,我会移动数组中nil类别的值。在这里,我使用(groups.delete(nil) || [])以防nil组为空。

现在可以再次按'timestamp'排序,数组的timestamp为其第一个哈希的timestamp

最后flatten为我们提供了所需的数组。

答案 2 :(得分:1)

此处需要的技巧是分配唯一组而不是nil。您只需创建一个通用的Ruby Object即可。

orig = [
  {"timestamp"=>1347119549, "category"=>nil}, 
  {"timestamp"=>1347119547, "category"=>"Monkeys"}, 
  {"timestamp"=>1347119543, "category"=>nil}, 
  {"timestamp"=>1347119542, "category"=>"Monkeys"}]

# The "tricky bit"
grouped = orig.group_by { |x| x["category"] ?  x["category"] : Object.new  }

# Sort the siblings within the groups (note negation causes reverse order)
grouped.values.each { |siblings| siblings.sort_by! { |a| -a["timestamp"] } }

# Sort the list by first (i.e. "best" sort order) timestamp in each group 
sorted_groups = grouped.sort_by { |group_id,siblings| -siblings.first["timestamp"] }

# Remove group ids and flatten the list:
result = sorted_groups.map { |group_id,siblings| siblings }.flatten
=>  [
 {"timestamp"=>1347119549, "category"=>nil}, 
 {"timestamp"=>1347119547, "category"=>"Monkeys"}, 
 {"timestamp"=>1347119542, "category"=>"Monkeys"}, 
 {"timestamp"=>1347119543, "category"=>nil}
]

答案 3 :(得分:1)

只需使用您尝试过的工具即可完成。

首先通过tiemstamp sort整个数组,然后使用group_by按类别分配它们:

arr = [
    {'timestamp' => 1347119549, 'category' => nil},
    {'timestamp' => 1347119547, 'category' => 'Monkeys'},
    {'timestamp' => 1347119543, 'category' => nil},
    {'timestamp' => 1347119542, 'category' => 'Monkeys'},
    {'timestamp' => 1347119541, 'category' => nil},
    {'timestamp' => 1347119548, 'category' => nil},
    {'timestamp' => 1347119545, 'category' => nil},
]

sorted = arr.sort_by { |elem| 0 - elem['timestamp'] }
groups = sorted.group_by { |elem| elem['category'] or Object.new }
sorted = groups.values.flatten

puts sorted

<强>输出

{"timestamp"=>1347119549, "category"=>nil}
{"timestamp"=>1347119548, "category"=>nil}
{"timestamp"=>1347119547, "category"=>"Monkeys"}
{"timestamp"=>1347119542, "category"=>"Monkeys"}
{"timestamp"=>1347119545, "category"=>nil}
{"timestamp"=>1347119543, "category"=>nil}
{"timestamp"=>1347119541, "category"=>nil}

当然,你可以以可读性为代价来管理整个事情。

sorted = arr.sort_by { |elem| 0 - elem['timestamp'] }.group_by { |elem| elem['category'] or Object.new }.values.flatten