【问题标题】:Create an array of a range of letters starting after A and finishing after Z ie (B to AC)创建从 A 开始并在 Z 之后结束的一系列字母的数组,即(B 到 AC)
【发布时间】:2017-06-15 04:56:50
【问题描述】:

我正在使用 Roo Gem 并希望从基于标准 A1 语法的电子表格中提取数据。

我在电子表格中有超出 Z 的列,因此 Excel 会处理整个 AA、AB、AC 列的位置。

我想为列 W 到 AH 创建一个数组。

Ruby 似乎不喜欢上限超出 Z 但还没有从 A 开始??

任何想法如何("B".."AC").to_a 而不是[]

这是irb中的基本问题。

("A".."Z").to_a
#=> ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]
("B".."Z").to_a
#=> ["B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]
("A".."AC").to_a
#=> ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "AA", "AB", "AC"]
("B".."AC").to_a
#=> []

【问题讨论】:

  • 对不起,我想保持简短,所以使用数组范围语法。我可以接受替代方案,只要它简短且易于维护。
  • ('B'..'Z').to_a + ('A'..'AC').to_a太丑了?
  • 是的,我刚做了那个。我现在会使用它,但我想我可能会遗漏一些明显的东西
  • 有趣。我希望String#upto 能够正确处理这个问题,但它也失败了。我已经提交了bug report

标签: ruby string range


【解决方案1】:

这个怎么样?

("A".."AC").to_a.drop(1)

您可以删除任意数量的elements,它只涉及 1 个范围和 1 个数组创建..

整数可能被替换为返回字母在字母表中的位置的东西。

class Array
  def from(column)
    drop(find_index(column).to_i)
  end
end

("A".."AC").to_a.from('F')
#=> ["F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "AA", "AB", "AC"]

直接使用Range 类,感谢@sagarpandya82

class Range
  def from(column)
    to_a.drop(find_index(column).to_i)
  end
end

("A".."AC").from('F')
#=> ["F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "AA", "AB", "AC"]

【讨论】:

  • 嗯,很好,你从 B 开始,很容易算出要删除多少个字母。但如果它在中间的某个地方可能会导致过多的思考。但它很短,我会给你:)
  • 是的。整数可能会被替换为返回字母在字母表中的位置的东西。不管怎样,我会给它更多的时间看看其他人的想法。我现在正在使用它,但除非出现更精简的东西,否则我会接受它作为答案。干杯
  • 当然,在那之前我也在考虑更好的解决方案.. :)
  • 您可以在您的 Array 补丁中删除所有对 self 的引用。
  • 建议:用to_a.drop(find_index(column).to_i) 修补Range 类。然后你可以写("A".."AC").from('F')等。
【解决方案2】:

使用Kernel#loop 构建一个空数组。一旦当前值等于第二个参数,循环就会中断。要返回新建的数组o,我们将o 作为参数传递给break,默认情况下返回nil

def cols a, b
  loop.with_object([]) do |_, o|
    o << a
    break(o) if a == b
    a = a.next
  end
end  

cols('W','AH')
 #=> ["W", "X", "Y", "Z", "AA", "AB", "AC", "AD", "AE", "AF", "AG", "AH"]

cols("A","Z")
 #=> ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M",
 #    "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]

cols("B","Z")
 #=>  ["B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N",
 #     "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]

cols("A","AC")
 #=> ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M",
 #    "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
 #    "AA", "AB", "AC"]

cols("B","AC")
 #=> ["B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N",
 #    "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "AA",
 #    "AB", "AC"]

【讨论】:

    【解决方案3】:

    对此的数学答案是:

    A => AH = (A => W) + (W=> AH)

    所以 W => AH = (A => AH) - (A => W)

    这个的程序化答案:

    ("A".."AH").to_a - ("A"..."W").to_a
    #=> ["W", "X", "Y", "Z", "AA", "AB", "AC", "AD", "AE", "AF", "AG", "AH"]
    

    第二个范围内的... 使其具有独占性,即没有“W”。

    更一般的答案是

    r = "W".."AH"
    
    ("A"..r.end).to_a - ("A"...r.begin).to_a
    #=> ["W", "X", "Y", "Z", "AA", "AB", "AC", "AD", "AE", "AF", "AG", "AH"]
    

    【讨论】:

    • 有趣的方法。我已将您的 (string.ord-1).chr 替换为独家范围。
    【解决方案4】:

    Ruby 的 String#succ 以 Excel 递增列名的方式递增字母:

    'Z'.succ #=> "AA"
    

    因此,如果您知道可以通过succ 访问目标值,则可以使用简单的循环:

    ary = ['W']
    ary << ary.last.succ until ary.last == 'AH'
    
    ary #=> ["W", "X", "Y", "Z", "AA", "AB", "AC", "AD", "AE", "AF", "AG", "AH"]
    

    但是用错误的值,很容易变成死循环。


    要获得更强大的解决方案,您可以编写自定义类:

    class Column
      attr_reader :name
    
      def initialize(name)
        raise ArgumentError if name =~ /[^A-Z]/
        @name = name
      end
    
      def <=>(other)
        [name.length, name] <=> [other.name.length, other.name]
      end
    
      def succ
        Column.new(name.succ)
      end
    end
    

    它基本上只是包装了列名,但它也考虑了名称的length

    [name.length, name] <=> [other.name.length, other.name]
    

    这意味着较长的名称出现在较短的名称之后。长度相同的名称按字典顺序进行比较。

    这允许您生成想要的序列:

    r = Column.new('W')..Column.new('AH')
    r.map(&:name)
    #=> ["W", "X", "Y", "Z", "AA", "AB", "AC", "AD", "AE", "AF", "AG", "AH"]
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-06-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多