【问题标题】:ruby array separate "duplicates"红宝石数组分离“重复”
【发布时间】:2019-12-24 06:41:13
【问题描述】:

我有这个包含大写字母和非大写字母(有时还有数字)的单词数组:

my_array = ['ZV2', '4Q3', 'abDc', 'zQ2', 'Def', '4q3', 'Abdc', 'ABDC', 'def', 'zv2', 'DeF', 'zV2', 'DEF']

我想分成几个没有重复单词的数组:

my_array1 = ['ZV2', '4Q3', 'abDc', 'zQ2', 'Def']

my_array2 = ['4q3', 'Abdc', 'def', 'zv2']

my_array3 = ['ABDC', 'DeF', 'zV2']

my_array4 = ['DEF']

最好的方法是什么?

【问题讨论】:

  • 您应该编辑以澄清您的问题。 (这是一个有趣的。)如果你这样做,我会投票重新开放,
  • array.each_with_object([]) { |s,a| d=s.downcase; i = a.find_index { |i| a[i].key?(d) ==false }; i.nil? ? (a << { d=> s }) : a[i][d] = s }.map(&:values)。我在手机上写了这个,所以我没有测试,所以也许我犯了一个小错误。其他读者可以检查我的代码吗? ——
  • 我不知道你想要什么细节。我把想要的输出。所需的输出将用于另一个进程。我不知道如何获得所需的输出,这就是我问这个问题的原因。
  • 你可以用一个例子来阐述你的问题,但通常不足以告诉读者你想做什么。例如,my_array.to_a 创建了一个数组集合,其中不包含大小写无关的重复项,但我认为这不是您想要的。您需要以完整且明确的语言陈述您的问题。例如,...
  • ...(假设我的理解是正确的),“给定一个字符串数组my_array,我希望创建一个数组partition,其中包含来自my_array 的元素数组,这样:1)partition.flatten包含my_array的所有元素;2)对于partition的每个元素a,数组a.map { |s| s.downcase }不包含重复项;3)partition的元素数量为尽可能小。”听起来像法律术语,嗯?你可能不喜欢这个措辞,但我相信它符合完整性和澄清的测试。如果您愿意,可以使用它。

标签: arrays ruby sorting


【解决方案1】:

问题的参数是相当开放的解释,但这里有一种方法:按值的小写对数组进行分组:

> d = my_array.group_by(&:downcase).values
> pp d
[["ZV2", "zv2", "zV2"],
 ["4Q3", "4q3"],
 ["abDc", "Abdc", "ABDC"],
 ["zQ2"],
 ["Def", "def", "DeF", "DEF"]]

这为您提供了一个数组数组,每个数组都包含所有重复项。从这里开始,解决方案应该相对简单:给定这个“行”列表,您需要一个“列”列表。您可以通过创建新数组来构造输出数组,获取每个数组的第 n 个元素,直到没有任何元素为止。

一种(非最佳的,如果这是家庭作业!)的方法是将所有数组填充到一个共同的长度,然后转置结果:

d = my_array.group_by(&:downcase).values
max_len = d.map(&:length).max
padded = d.map {|v| v.fill(nil, v.length..max_len) }

pp padded
[["ZV2", "zv2", "zV2", nil, nil],
 ["4Q3", "4q3", nil, nil, nil],
 ["abDc", "Abdc", "ABDC", nil, nil],
 ["zQ2", nil, nil, nil, nil],
 ["Def", "def", "DeF", "DEF", nil]]

现在您只需转置数组数组并压缩所有 nil:

padded.transpose.map(&:compact).select(&:any?)
=> [["ZV2", "4Q3", "abDc", "zQ2", "Def"], ["zv2", "4q3", "Abdc", "def"], ["zV2", "ABDC", "DeF"], ["DEF"]]

这几乎不是最优雅的方法,但它说明了这个想法。您也许可以使用它来构思更好的解决方案。

【讨论】:

  • 如果你使用Array#zip(而不是transpose)你不需要填充数组。例如,a1 = [1,2,3,4]; a2 = [7,8]; a3 = [4,5,6]; a = [a1, a2, a3]; a.shift.zip(*a) #=> [[1, 7, 4], [2, 8, 5], [3, nil, 6], [4, nil, nil]]。唯一的要求是zip 的接收器(此处为a1)不小于d 中的任何数组。它可能是最大的,甚至可能是[nil]*(d.map(&:size).max)
  • 我故意让答案变得迟钝,以便提供解决方案的形状,而不必给出一个好的作业答案。 :)
【解决方案2】:

您可以减少数组,在需要时拆分为新的子数组:

my_array.each_with_object([]) do |e, acc|
  (
    acc.find { |a| !a.find { |ae| ae.casecmp(e).zero? } } ||
    (acc << []).last
  ) << e
end
#⇒ [["ZV2", "4Q3", "abDc", "zQ2", "Def"],
#   ["4q3", "Abdc", "def", "zv2"],
#   ["ABDC", "DeF", "zV2"],
#   ["DEF"]]

【讨论】:

    【解决方案3】:
    my_array.each_with_object([]) do |s,a|
      d = s.downcase
      idx = a.find_index { |h| h.key?(d) == false }
      if idx.nil?
        a << { d=>s }
      else
        a[idx][d] = s
      end
    end.map(&:values)
      #=> [["ZV2", "4Q3", "abDc", "zQ2", "Def"],
      #    ["4q3", "Abdc", "def", "zv2"],
      #    ["ABDC", "DeF", "zV2"],
      #    ["DEF"]] 
    

    Array#find_index

    我可以通过添加一些puts 语句来最好地解释它是如何工作的。有兴趣的读者可以运行下面的代码来观察一步一步的计算。

    arr = my_array.each_with_object([]) do |s,a|
      d = s.downcase
      puts "s=#{s}, a=#{a}, d=#{d}"
      idx = a.find_index { |h| h.key?(d) == false }
      puts "s=#{s}, a=#{a}, idx.nil?=#{idx.nil?}, idx=#{idx}"
      if idx.nil?
        a << { d=>s }
      else
        a[idx][d] = s
      end
      puts "  after case a=#{a.to_s}"
    end
    arr.map(&:values)
    

    【讨论】:

      猜你喜欢
      • 2014-01-05
      • 2012-09-13
      • 1970-01-01
      • 2012-06-05
      • 2011-08-12
      • 1970-01-01
      • 1970-01-01
      • 2010-12-18
      • 2018-05-17
      相关资源
      最近更新 更多