【问题标题】:Sort an array according to the elements of another array根据另一个数组的元素对数组进行排序
【发布时间】:2012-08-11 07:10:01
【问题描述】:

我有一个 id 数组

a1 = [1, 2, 3, 4, 5]  

我还有另一个对象数组,其 ids 以随机顺序排列

a2 = [(obj_with_id_5), (obj_with_id_2), (obj_with_id_1), (obj_with_id_3), (obj_with_id_4)]  

现在我需要根据 a1 中 id 的顺序对 a2 进行排序。所以 a2 现在应该变成:

[(obj_with_id_1), (id_2), (id_3), (id_4), (id_5)]  

a1 可能是 [3, 2, 5, 4, 1] 或任何顺序,但 a2 应对应于 a1 中 id 的顺序。

我喜欢这样:

a1.each_with_index do |id, idx|
  found_idx = a1.find_index { |c| c.id == id }
  replace_elem = a2[found_idx]
  a2[found_idx] = a2[idx]
  a2[idx] = replace_elem
end  

但是如果 a2 的元素顺序与 a1 完全相反,这仍然可能会遇到 O(n^2) 时间。有人可以告诉我排序a2的最有效方法吗?

【问题讨论】:

    标签: ruby arrays algorithm sorting


    【解决方案1】:

    如果有什么比显而易见的方式快得多,我会感到惊讶:

    a2.sort_by{|x| a1.index x.id}
    

    【讨论】:

    • 假设 a1 已排序(并且它来自问题 stmt)并且您用于 a1 的容器可以利用 a1 已排序的事实,那么我同意这会比 O( n^2).
    • 没有 a1 被排序不是优势,我不知道你为什么会这么想。这种方式很快,因为它是内置的。试图击败内置的 sort_by 对我来说似乎是浪费时间。
    • a1 被排序是一个优势。如果已排序,则索引操作应在 O(log n) 时间内运行(假设二进制搜索),如果未排序,则索引将在 O(n) 时间内运行。
    • 这种方法也非常快,但是使用哈希就像比光速还快。我用这两种方法对 10,000 个数字进行了测试(只是为了测试)。您的方法在平均上花费了 1.3 秒,但使用哈希值在平均上花费了 0.009 秒..
    • -1 表示此方法。例如:x - 1000 个元素的数组,未排序 x2 - 相同元素的数组,已排序 Benchmark.bm { |t| t.report('test1') { x.index_by { |c| c }.values_at(*x2).compact } t.report('test2') { x.sort_by { |v| x2.index v } } } test1 真实:0.000709 test2 真实:0.048563
    【解决方案2】:
    hash_object = objects.each_with_object({}) do |obj, hash| 
      hash[obj.object_id] = obj
    end
    
    [1, 2, 3, 4, 5].map { |index| hash_object[index] }
    #=> array of objects in id's order
    

    我相信运行时间会是O(n)

    【讨论】:

    • 我相信这将是 O(n^2)。实际排序是 O(n),但准备步骤会使其变成 n^2
    • 我不同意,构建哈希表需要 O(n),看这里en.wikipedia.org/wiki/Hash_table
    • 是的,构建哈希表是 O(n) 时间。排序是 O(n) 时间。所以你有2xO(n)......嗯......这将小于n ^ 2。我站得更正了。好收获!
    • 第一步似乎和使用hash_object = objects.index_by(&:object_id)一样
    • @kamal:它是 O(n),但不执行所要求的操作 - 它将返回 [nil, nil, nil, nil, nil],除非 object_ids 恰好是数字 1 到 5。要使其工作,您需要获取 object_ids 并对它们进行排序,这不会比objects.index_by(&:object_id) 更好。此外,这里没有必要解释 O(n) 声明,但请注意 O(n log n) 下限仅适用于比较排序。
    【解决方案3】:

    我喜欢接受的答案,但在 ActiveSupport 中有 index_by,它可以更轻松地创建初始哈希。见Cleanest way to create a Hash from an Array

    事实上,由于 Enumerable 也支持 index_by,因此您可以在一行中完成此操作:

    a2.index_by(&:id).values_at(*a1)
    

    【讨论】:

    • 这仅适用于原始列表中没有任何重复项的情况。 Index by 将覆盖任何重复的 id。这对您来说可能是也可能不是问题。
    【解决方案4】:

    Eric Woodruff's Answer 的启发,我想出了以下香草 Ruby 解决方案:

    a2.group_by(&:object_id).values_at(*a1).flatten(1)
    

    方法文档:

    【讨论】:

    • 我最喜欢这个解决方案。它很有效(我怀疑比@pguardiario 的解决方案更有效),而且重要的是,它允许a2 的两个元素具有相同的“id”值。该问题并未说明 id 的唯一性,但一些答案(包括所选答案)取决于 id 的唯一性。
    猜你喜欢
    • 1970-01-01
    • 2018-02-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多