创建一个由一系列字母组成的数组,从 A 之后开始,在 Z 之后结束,即(B 到 AC)



我正在使用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
#=> []
这个

怎么样?

("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"]

使用 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"]

一个数学答案是:

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"]

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"]

最新更新