簡體   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