Ruby计算数组中的重复项并打印到新文件中

时间:2013-12-03 17:02:44

标签: ruby arrays duplicates

我需要计算数组中重复项的数量,找出它们出现的次数,然后将它放入文档中......这就是我所做的,现在我对如何继续进行无能为力...... ..数据来自另一个txt文件。如果它有点乱,我道歉,但我现在很困惑。

class Ticket

attr_accessor :ticknum
attr_accessor :serialnum

    def initialize(ticknum,serialnum)
    @ticknum = ticknum
    @serialnum = serialnum

    end
end

class Ticketbook

    def initialize
    @ticketbook = Array.new
    end

    def newticket(ticket)
    @ticketbook << ticket
    @ticketbook.sort! {|x,y| x.ticknum*1000 + x.serialnum <=> y.ticknum*1000 + y.serialnum}

    end

    def soldnumber(tickenum2,serialnum2)
            @ticknum2 = ticknum2
            @serialnumb2 = serialnum2
            @antal = 0
        for i in 0..@ticketbook.length-1
        if @ticknum2 == @ticketbook[i].ticknum && @serialnum2 == @ticketbook[i].serialnum
            @antal +=1
        end
        end
        return @antal   
    end

end

ticketfile = File.open("tickets.txt", "r")

book = Ticketbook.new
ticketfile.each {|line| 
a = line.split(",")

newdoc = Ticket.new(a[0].to_i,a[1].to_i)
book.newticket(newdoc)
}

registernums = File.new("registernums.txt", "w")


for i in (0..@ticketbook.length-1)
registernums.print book[i].@ticketnum.to_i + ", "
registernums.print book[i].@serialnumber.to_i + ", "
registernums.puts book[i].soldnumber(i)
end
print registernums

给了我这个错误: rb 56意外的tIVAR,期待“(”registernums.print book [i]。@ ticketnum.to_i rb 57意外的tIVAR,期待“(”registernums.print book [i]。@ serialnum.to_i

6 个答案:

答案 0 :(得分:1)

您的for循环没有正文,因此您的最后几行在未定义的循环之外引用i

答案 1 :(得分:1)

问题出在这些方面。

registernums.print book[i].@ticketnum.to_i + ", "
registernums.print book[i].@serialnumber.to_i + ", "

要访问任何对象实例变量,您不需要放置@。所以正确的代码应该是

registernums.print book[i].ticketnum.to_i + ", "
registernums.print book[i].serialnumber.to_i + ", "

同样@Jonah指出,应该有一个end来结束最后一个for循环。

答案 2 :(得分:1)

这里有一些问题:

for i in (0..@ticketbook.length-1)
  registernums.print book[i].@ticketnum.to_i + ", "
  registernums.print book[i].@serialnumber.to_i + ", "
  registernums.puts book[i].soldnumber(i)
  print registernums

此代码在TicketBook类之外,因此没有任何实例变量(以@开头)实际可用。

如果要从TicketBook外部访问票证数组,请创建

attr_reader :ticketbook

TicketBook班。

您可能希望用以下内容替换代码:

book.ticketbook.each_with_index do |tb, i|
  registernums.print tb.ticketnum.to_i + ", "
  registernums.print tb.ticketnum.to_i + ", "
  registernums.puts tb.soldnumber(i)
end

答案 3 :(得分:1)

哦,男孩!

在我开始几个要点之前:    - 你过度使用实例变量 - Ticket类没问题,但是Ticketbook(应该是TicketBook)应该只有一个instance_variable(在initialize方法中设置一个),其余的应该是方法范围的本地变量。

  • Ruby命名约定是用_(new_doc,ticket_file等)分隔单词

  • 你几乎不应该使用for循环 - 使用它的唯一原因是编写自己的迭代器,但是你在这里使用数组 - 使用each方法

    • 使用缩进!

现在关于错误:

ticketfile = File.open("tickets.txt", "r")

book = Ticketbook.new
ticketfile.each {|line| 
  a = line.split(",")

  newdoc = Ticket.new(a[0].to_i,a[1].to_i)
  book.newticket(newdoc)
}                                             

registernums = File.new("registernums.txt", "w")


for i in (0..@ticketbook.length-1)                       #  @ticketbook is an instance variable of Ticketbook, you'll get undefined length for nil:NilClass
registernums.print book[i].@ticketnum.to_i + ", "        # book is an instance of Ticketbook, [] is not defined on that class!
registernums.print book[i].@serialnumber.to_i + ", "
registernums.puts book[i].soldnumber(i)
print registernums

您的Ticketbook课程

class Ticketbook

  def initialize
  @ticketbook = Array.new    #personaly would prefer []
  end

  def newticket(ticket)
  @ticketbook << ticket
  @ticketbook.sort! {|x,y| x.ticknum*1000 + x.serialnum <=> y.ticknum*1000 + y.serialnum}

  end

  def soldnumber(tickenum2,serialnum2)
        @ticknum2 = ticknum2           # unnecessary
        @serialnumb2 = serialnum2      # unnecessary
        @antal = 0
      for i in 0..@ticketbook.length-1      # Should be @ticketbook.each do |ticket|
      if @ticknum2 == @ticketbook[i].ticknum && @serialnum2 == @ticketbook[i].serialnum
          @antal +=1
      end
      end
      @antal

      # much better would be:
      # def soldnum(ticknum2, serialnum2)
      #   @ticketbook.select {|ticket| ticket.ticknum == ticknum2 && ticket.serialnum == serialnum }.count
      # end  

  end

我还会向您介绍group_by方法 - 在数组上运行会将其转换为非常好的哈希值,其中键是执行块的结果:

[1,2,3,4,5,6].group_by {|e| e.odd?} #=> {true => [1,3,5], false => [2,4,6]}

你可以用它来一次性重复计算:

# inside ticket book
def count_repetitions
  Hash[@ticketbook.group_by {|e| [e.ticknum, e.serialnum]}.map {|key, value| [key, value.count]}
end

这应该返回散列,其中键是包含ticknum和serialnum的双元素数组,值是出现次数

答案 4 :(得分:0)

tIVAR指的是一个实例变量,因此错误消息unexpected tIVAR意味着ruby不会在某处预期某个实例变量,而是指向此行(以及后面的那个)

registernums.print book[i].@ticketnum.to_i + ", "

访问对象中的属性不使用@字符(并且它也不是变量名称的一部分)。访问ticketnum属性的正确方法是

registernums.print book[i].ticketnum.to_i + ", "

答案 5 :(得分:0)

当你的问题得到解答时,我想建议一种更“类似Ruby”的方式来处理你的问题。首先,几点:

  • 作为具有两个属性的对象的票证:票号和序列号。您可以将票证作为Ticket类的实例,如您所做的那样,使用两元素数组,并了解第一个和第二个元素分别对应于票证和序列号,或者作为哈希,一个钥匙用于票号,另一个用于序列号。我喜欢哈希。我认为不需要单独的类,我认为使用数组更有可能导致编码错误(例如,如果您使用错误的数组元素作为故障单或序列号)。
  • 使用Ruby在其Enumerable“mixin”模块中提供的所有方法,不需要循环遍历索引。避免这样的循环将使您的代码更紧凑,更易于阅读,并且不太可能包含错误。
  • 正如其他人提到的那样,您不需要(任何)实例变量。

我们首先添加一些ticketbook的门票:

ticketbook = []
ticketbook << {tnbr: 22, snbr: 55}
ticketbook << {tnbr: 27, snbr: 65}
ticketbook << {tnbr: 22, snbr: 56}
ticketbook << {tnbr: 27, snbr: 66}
  # => [{:tnbr=>22, :snbr=>55}, {:tnbr=>27, :snbr=>65}, \
        {:tnbr=>22, :snbr=>55}, {:tnbr=>27, :snbr=>65}]

现在查找重复项(票号具有相同的票号但序列号不同)。一旦你获得了Ruby的更多经验,只要你想通过某种特征对数组的元素进行分组,你就会想到Enumerable#group_by方法(或者可能是Enumerable#chunk):

g0 = ticketbook.group_by {|t| t[:tnbr]}
  # => {22=>[{:tnbr=>22, :snbr=>55}, {:tnbr=>22, :snbr=>56}], \
  #     27=>[{:tnbr=>27, :snbr=>65}, {:tnbr=>27, :snbr=>66}]} 

如您所见,当我们group_by票号时,我们会获得一个元素为(k,v)的哈希值,其中键k为票号,值v为一系列带有该票号的票(哈希)。

这可能就是您所需要的。如果您想要计算具有相同序列号的票证数量,可以使用Enumerable#mapg0哈希值(具有相同票号的票证数组)中的每个值转换为该号码这样的门票:

g1 = g0.map {|k,v| {k => v.size}} # => [{22=>2}, {27=>2}]

你可能会在这里停下来,但如果这是一个哈希({22=>2, 27=>2})而不是一对单对哈希会更方便。有几种方法可以将此数组转换为哈希。一种是使用map将哈希值转换为数组:

g2 = g1.map(&:to_a) # => [[[22, 2]], [[27, 2]]]

(其中map(&:to_a)map {|h| h.to_a}的简写),然后使用Array#flatten将其转换为:

g3 = g2.flatten # => [22, 2, 27, 2]

创建哈希(通常)的一种方法是这样的:

Hash[1,2,3,4] # => {1=>2, 3=>4}

要使用数组g3执行此操作,我们需要在“splat”运算符前面添加数组:

Hash[*g3] # => {22=>2, 27=>2}

这为我们提供了所需的票数哈希值。我说这是将单对哈希数组转换为哈希的一种方法。这是一个更直接的方式:

g1.pop.merge(*g1) # => {27=>2, 22=>2}

此处g1.pop返回{27=>2}并将g1转换为[{22=>2}]。因此,上述表达式相当于:

{27=>2}.merge(*[{22=>2}]) # => {27=>2, 22=>2}

将splatted数组中的哈希值(此处只有一个)合并到merge之前的哈希值中。

您不会引入局部变量g0g1,而是通常会“链接”这三个操作:

ticketbook.group_by {|t| t[:tnbr]}.map {|k,v| {k => v.size}}.pop.merge(*g1)
  # => {27=>2, 22=>2}  

最后,虽然您的sort版本很好,但您也可以这样做:

ticketbook.sort! {|x,y| (x <=> y) == 0 ? x[:snbr] <=> y[:snbr] : \
  x[:tnbr] <=> y[:tnbr]}