【问题标题】:how I could sort by unequal in ruby?我如何在红宝石中按不等排序?
【发布时间】:2017-07-13 13:34:17
【问题描述】:

我看到这篇文章使用了排序比较器,但是我如何按不等排序

ruby's <=> operator and sort method

我有一条短信

猫 猫 猫 猫 猫 狗 狗 狗 狗 狗 堆 堆 堆 堆 堆 星星 星星 星星 星星 星星 列表 列表 列表 列表 列表

使用逻辑,当 b1 等于 a b2 时,将 b2 放在最后,但当 b1 不等于 b2 时,将 b2 放在 b1 旁边

获取

猫 狗 堆 星星 列表 猫 狗 堆 星星 列表 猫 狗 堆 星星 列表 猫 狗 堆 星星 列表 猫 狗 堆 星星 列表

请帮我解决这个问题

【问题讨论】:

  • 是否需要得到这个精确的输出,还是只需要遵循一个规则,没有两个相邻的项目可以相同?
  • 如果对['a', 'a', 'a', 'b']进行排序,结果应该是什么?应该是['a', 'a', 'a', 'b'],还是['a', 'b', 'a', 'a'],还是因为两个相邻元素相等而抛出错误?
  • 在您的示例中,每个单词有五个。这些组是否总是有相同数量的单词?
  • &lt;=&gt; 运算符,通常称为“宇宙飞船运算符”,它不是相等测试,而是差分测试:'a' &lt;=&gt; 'b'-1,因为 'a' 是第一位的,'b' &lt;=&gt; 'a'1 因为 b 在后面,'b' &lt;=&gt; 'b'0 因为它们是等价的。

标签: ruby list sorting


【解决方案1】:

如果您的数组中的元素分布均匀(不一定已排序)。

arr = ['a','a','a','b','b','b','c','c','c']
arr.uniq * arr.uniq.size #=> ["a", "b", "c", "a", "b", "c", "a", "b", "c"]

警告:如果未排序,则在uniq 期间遇到的第一个唯一元素确定最终的数组排序。

除了 cmets,还有一个更强大的解决方案:

arr = ['a','a','a','b','b','b','c','c','c','d','d','d']
enum = arr.uniq.cycle
enum.take(arr.size)
#=> ["a", "b", "c", "d", "a", "b", "c", "d", "a", "b", "c", "d"]

【讨论】:

  • 虽然这确实回答了以极其简洁的方式发布的实际问题,但它假设唯一元素的长度等于每个元素被列出的次数(例如 5 个单词列出 5 次),但是如果 6 个单词被列出 5 次,这将不会执行相同的操作。
  • @engineersmnky 感谢您的反馈;我已经用另一个我认为实际上更好的答案更新了我的答案。
  • 我完全同意这是非常优雅的(如果可以的话,我会再次投票)
  • sagar,我只是在注意到它与您之前的答案相同后删除了我的答案。我很抱歉,但我有点困惑,因为我早些时候赞成你的回答!我只能假设我的短期记忆变得断断续续。
  • @CarySwoveland 哈哈!无需道歉,感谢您的支持!
【解决方案2】:

我没有看到使用 &lt;=&gt; 比较的解决方案。

但这里有一种方法:

使用Enumerable#chunk,您可以将相同的值组合在一起(如果数组已排序;否则您可以使用group_by)。

然后,您选择根据其值拆分的组,然后转置该数组并将这些值重新连接在一起。

长话短说,

 array.chunk{ |a| a }.map{ |k, v| v }.transpose.flatten

将为您示例中的数据做正确的事情。

请注意,为了使transpose 工作,组必须包含相同数量的元素。

编辑:

这是由工程师在 cmets 中建议的一个更好的版本,即使组的大小不同,它也能正常工作:

array.chunk(&:itself).map(&:last).inject { |memo, e| memo.zip(e) }.flatten

【讨论】:

  • 如果元素不是偶数但仍然排序并且您不介意丢失多余的元素,您可以使用#inject#zip 代替#transpose,例如a.chunk(&amp;:itself).map(&amp;:last).inject { |memo, e| memo.zip(e)}.flatten
  • @engineersmnky 你的版本很棒!我刚刚更新了我的答案。我喜欢&amp;:itself&amp;:last,从现在开始一定会使用它们!
【解决方案3】:

你可以这样做:

arr.group_by {|w| w}.values.transpose.flatten

仅当所有组的单词数相同时才有效。如果您的组的字数不同,则不清楚您在寻找什么。

如果您希望组由最短长度组定义,您可以这样做:

na=arr.group_by {|w| w}.values
len=na.map {|l| l.length}.min
na.map {|l| l[0...len] }.transpose.flatten

如果您想使用sort 算法,您可能希望按照装饰、排序、取消装饰(AKA DSU 或Schwartzian transform)的方式执行一些操作,如下所示:

> arr=['cat']*4+['dog']*3+['list']*2+["star"]
> arr.group_by {|w| w}                     # all same words together
      .values                              # only the sepate lists of words
      .map {|l| l.each_with_index          # enumerate those so that we have [0, word]..[n, word]
      .map {|e,i| [i,e]}}.flatten(1)       # remove one level of nesting
      .sort                                # sort first on the number then on the word
      .map {|l| l[1]}                      # remove the number
["cat", "dog", "list", "star", "cat", "dog", "list", "cat", "dog", "cat"]

这将允许不同长度的词组与较长的组结束朝向数组的前面。

【讨论】:

    【解决方案4】:

    满足实际要求的逻辑:

    使用逻辑,当 b1 等于 a b2 时,将 b2 放在最后,但当 b1 不等于 b2 时,将 b2 放在 b1 旁边

    你可以使用老式的递归

    def process(arr)
      arr.each_with_index.each_cons(2) do |a,b| 
        if a.first == b.first then 
          arr << arr.delete_at(b.last)
          process(arr)
        end
      end
      arr
    end
    

    此方法与描述的完全一样。如果a == bb 被移动到列表的末尾并重新处理整个列表直到没有a == b 的事件

    另一种解决方案,它将输出列表长度基于列表中出现最少的元素并且不需要排序,只要唯一元素在列表中的位置是可接受的排序

    def repeat_unordered_list(arr)
      arr.group_by(&:itself).to_a.tap do |a|
        min_elements = a.min_by{|_,v| v.size}.last.size
        a.replace(a.map(&:first) * min_elements)
      end
    end 
    

    Example(s)

    【讨论】:

    • 我认为您的回答很好,但为什么不使用带有自定义比较器的排序呢?
    • @diegoto Array#sort 将使用火箭飞船 (&lt;=&gt;) 来处理自定义块的实际排序仍必须返回 (-1,0,1) 之一,这使得解决您的问题有点复杂,因为标准比较在这里不起作用,您需要 cat 和 cat 根据它们在 Array 中的位置进行不同的评估。以这种方式进行加权会更加尴尬,因为 cat #2 的权重必须高于 dog#1、stack#1、star#1 和 list#1。
    • 您可以使用Schwartzian transform 以这种方式进行排序。将单词组合在一起;给每个喜欢的单词编号 0 - n,排序,然后删除数字。中提琴。
    • @dawg 我知道如何实现,但更尴尬,实际上并不满足所提出的逻辑。但是感谢您为其他访问者发布该解决方案
    猜你喜欢
    • 2011-07-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-18
    • 1970-01-01
    相关资源
    最近更新 更多