【问题标题】:Sorting an array by entry properties, with optional grouping按条目属性对数组进行排序,可选分组
【发布时间】:2013-07-28 13:54:43
【问题描述】:

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

[
    {"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 获得正确的结果,但没有成功。

【问题讨论】:

  • 应该由它的兄弟姐妹出现 但是顺序在哪里?假设您有 3 个类别为“猴子”的条目,它们出现在排序列表的哪个位置?通过第一个“猴子”条目?第二?第三?以上都不是?
  • 好问题,抱歉我没有澄清。它们应该出现在第一个条目中,有效地插入它们,将以下条目“向下”推:)
  • @Borodin 的解决方案是最简单且性能最好的。它大约是所选答案的两倍:gist.github.com/dresselm/6099437

标签: ruby arrays sorting


【解决方案1】:
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}]

【讨论】:

  • @tessi 我也拿了你的示例数组,只是为了测试我的解决方案,如果它工作得很好.. :))
  • 呵呵,似乎有效 :) 顺便说一句:刚刚对您的代码进行了小幅编辑,以便可以将其复制粘贴到 IRB 中而不会出现语法错误。
  • @tessi 没关系.. 我明白了... :)
  • 我没有想到使用"timestamp" 作为替代值来防止nil 项目分组。这很有意义。
【解决方案2】:

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

首先sort通过tiemstamp整个数组,然后使用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

【讨论】:

  • 这不是 OP 所说的预期输出.. :)
  • 起初,我以为这是 OP 想要的,但它与 OP 的预期输出不匹配。我仍然不明白 OP 想要做什么。
  • 啊,我明白了。时间戳排序顺序需要向后,具有非零category 的哈希组按其最新时间戳排序。固定。
  • 我觉得你的最终解决方案绝对是最好的。它简单而高效。我建议您更新您的答案,以描述您在sort_by 中使用0 的原因,突出显示or Object.new 技巧并突出使用values 与使用map 的其他解决方案。
  • @JesperRasmussen:这更多是因为你的问题是一个有趣的问题,而不是我们内心的善良:)
【解决方案3】:

这里需要的技巧是分配一个 unique 组而不是 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}
]

【讨论】:

  • 听起来很有趣 :) 但是时间戳应该是反向排序的
  • @tessi:我看不到指定的位置,但是是的似乎与示例相匹配。谢谢,我会修复它。 . .
  • 刚刚从例子中得到,你是对的,他的描述中没有指定:)
  • 是的,很抱歉我没有更准确地说明:)
  • @tessi:是的,我在尝试颠倒顺序时设法打破了它。现在知道了,谢谢!
【解决方案4】:

看起来有点难看,但确实有效:

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 给了我们想要的数组。

【讨论】:

  • 嗯,我有点期待解决方案不是很漂亮:) 这不是一个简单的问题,因为除非设置了类别,否则时间戳是主要顺序。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-02-20
  • 2010-11-02
  • 1970-01-01
相关资源
最近更新 更多