繁体   English   中英

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

[英]Ruby counting duplicates in array and print into new file

我需要计算数组中重复项的数量,找出它们出现的次数,然后将其放入文档中。.这就是我所做的,现在我不知道如何进行操作了。数据来自另一个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

您的for循环没有主体,因此您的最后几行引用i在未定义循环的外部。

问题在于这些行。

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指出的那样,应该在最后一个for循环的end结束。

这里有一些问题:

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

好家伙!

在开始一些重要说明之前:-您正在过度使用实例变量-票证类可以,但是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

您的票务类

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的两个元素的数组,值是出现的次数

tIVAR引用了一个实例变量,因此错误消息unexpected tIVAR表示ruby在某处不期望使用实例变量,并且它指向此行(及其后的一行)

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

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

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

回答完您的问题后,我想提出一种更“类似于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}

它将散列数组中的哈希(此处仅是一个) mergemerge之前的哈希中。

通常不用“链接”这三个操作,而不必引入局部变量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]}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM