2013-10-04 73 views
0

我寫了一個簡單的程序,解析我的銀行交易CSV文件。我的表達式將結果推送到將被保存到數據庫的數組/散列數據結構。如何在Ruby中使用matchdata對象編寫條件邏輯?

有兩個部分:

  1. 打開文件,讀出每一行,並推動它一個運行方法。
  2. 從哈希中提取數據的視圖。

我已經在下面列出了我的主要分析方法。它檢查每一行的關鍵字,如果匹配失敗,它應該推送到未分類的哈希。但是,有條件的要麼是根據我使用的是elsif還是else來推動ALL或NO交易。

Matchdata對象默認返回字符串,所以else應該工作不應該嗎?這是構建數據結構的方法。我評論過我遇到的問題:

def generateHashDataStructure(fileToParse, wordListToCheckAgainst) 
    transactionInfo = Hash.new 
    transactionInfo[:transactions] = Hash.new 
    transactionInfo[:unclassifiedTransaction] = Hash.new 
    transaction = transactionInfo[:transactions] 
    unclassifiedTransaction = transactionInfo[:unclassifiedTransaction] 

    wordListToCheckAgainst.each do |word| 
    transaction[word] = Array.new 
    unclassifiedTransaction[:unclassifiedTransaction] = Array.new 
    File.open(fileToParse).readlines.each do |line| 
     if transaction = /(?<transaction>)#{word}/.match(line) 
     date = /(?<Month>\d{1,2})\D(?<Day>\d{2})\D(?<Year>\d{4})/.match(line).to_s 
     transaction = /(?<transaction>)#{word}/.match(line).to_s 
     amount =/-+(?<dollars>\d+)\.(?<cents>\d+)/.match(line).to_s 
     transactions[word].push({:date => date, 
           :name => transaction, :amount => amount.to_f.round(2)}) 

     # this is problem: else/elsif don't push only if match fails 
     else 
     date = /(?<Month>\d{1,2})\D(?<Day>\d{2})\D(?<Year>\d{4})/.match(line).to_s 
     transaction = /(?<Middle>)".*"/.match(line).to_s 
     amount =/-*(?<dollars>\d+)\.(?<cents>\d+)/.match(line).to_s 
     unclassifiedTransaction[:unclassifiedTransaction].push({:date => date, 
            :name => transaction, :amount => amount.to_f.round(2)}) 
     next 
     end 
    end 
    return transactionInfo 
    end 

任何想法都會很棒。我研究過這個,我覺得我已經通過向社區伸出手而擊敗了。我意識到正則表達式可能不是最好的方法,所以我願意接受所有的反饋。

+1

你應該添加的輸入格式的一小部分。這看起來像XML,但不是真的嗎? – tadman

+0

你是什麼意思的「陣列/散列」?你不知道你的數據結構會是什麼?或者,你的意思是說它是一組「哈希」?如果是這樣,那麼使用正確的名稱,因爲第一個是模糊的,會導致無意義的代碼和混淆的答案。 –

+0

您的示例代碼不會被解析。它缺少一個結束'結束'。 –

回答

2

我讓你的代碼更具慣用性,這有助於揭示一些非常可疑的事情。

  1. Ruby方法和變量都寫在snake_case,駝峯。雖然這似乎是個人意見的問題,但它也成爲可維護性/可讀性的一個例子。 _幫助我們的大腦在變量名稱中以可視方式將單詞片段彼此分開,而不是看到混合大小寫的「連續」字符串。 Try_reading_a_bunch_of_text_that_is_identical exceptForThatAndSeeWhichIsMoreExhausting
  2. 你分配給一個條件測試中的變量:

    if transaction = /(?<transaction>)#{word}/.match(line) 
    

    不要那樣做。即使它是故意的,當別人不理解時也會出現維護錯誤的可能爲什麼你會這樣做。相反,它寫在兩個步驟操作,很明顯想要的結果:

    transaction = /(?<transaction>)#{word}/.match(line) 
    if transaction 
    

    或者,你的 「分配則比較」 確實應該寫成:

    if transaction == /(?<transaction>)#{word}/.match(line) 
    

    或者:

    if /(?<transaction>)#{word}/.match(line) 
    

    哪個更乾淨/安全/明顯。

  3. 除了使用Hash.new,和Array.new,使用直接分配分別{}[]。他們不那麼嘈雜,更常見。此外,而不是增量定義你的哈希:

    transactionInfo = Hash.new 
    transactionInfo[:transactions] = Hash.new 
    transactionInfo[:unclassifiedTransaction] = Hash.new 
    

    用途:

    transaction_info = { 
        :transactions => {}, 
        :unclassified_transaction => {} 
    } 
    

    立即在結構顯露,使得意圖更加清晰。

  4. File.open(fileToParse).readlines.each do |line|是這樣做的一個令人費解的方式:

    File.foreach(fileToParse) do |line| 
    

    只有foreach不浪費內存的整個文件吸吮到內存中的一次。沒有明顯的速度提升來「sl」「你的文件,只有當文件增長到」巨大「的比例時纔會有所改進。

  5. 而不是使用:

    transactions[word].push({:date => date, 
             :name => transaction, :amount => amount.to_f.round(2)}) 
    

    更簡單地編寫代碼。 push掩蓋你在做什麼一樣,你格式化你的線條的方式:

    transactions[word] << { 
        :date => date, 
        :name => transaction, 
        :amount => amount.to_f.round(2) 
    } 
    

    請注意對齊成列。有些人避開那種特殊的習慣,但是當你處理很多任務時,看到每一行的變化就會產生很大的不同。

下面是更地道Ruby代碼:

def generate_hash_data_structure(file_to_parse, word_list_to_check_against) 

    transaction_info = { 
    :transactions => {}, 
    :unclassified_transaction => {} 
    } 

    transaction = transaction_info[:transactions] 
    unclassified_transaction = transaction_info[:unclassified_transaction] 

    word_list_to_check_against.each do |word| 

    transaction[word] = [] 
    unclassified_transaction[:unclassified_transaction] = [] 

    File.foreach(file_to_parse) do |line| 

     if transaction = /(?<transaction>)#{word}/.match(line) 

     date  = /(?<Month>\d{1,2})\D(?<Day>\d{2})\D(?<Year>\d{4})/.match(line).to_s 
     transaction = /(?<transaction>)#{word}/.match(line).to_s 
     amount  = /-+(?<dollars>\d+)\.(?<cents>\d+)/.match(line).to_s 

     transactions[word] << { 
      :date => date, 
      :name => transaction, 
      :amount => amount.to_f.round(2) 
     } 

     # this is problem: else/elsif don't push only if match fails 

     else 

     date  = /(?<Month>\d{1,2})\D(?<Day>\d{2})\D(?<Year>\d{4})/.match(line).to_s 
     transaction = /(?<Middle>)".*"/.match(line).to_s 
     amount  = /-*(?<dollars>\d+)\.(?<cents>\d+)/.match(line).to_s 

     unclassified_transaction[:unclassified_transaction] << { 
      :date => date, 
      :name => transaction, 
      :amount => amount.to_f.round(2) 
      } 

     # next 
     end 

    end 

    transaction_info 

    end 
end 
+0

非常感謝您花時間幫忙。 Ruby有這樣的優雅,但是學習它需要時間。絕對愛你做的縮短。我會研究你的變化,再次感謝! – mzakany23

+0

Ruby可以很優雅,但每種語言都可以清晰,清晰地編寫。 Elegance正在編寫簡單但功能強大的代碼,同時仍然清晰易讀。此時它變得像禪宗一樣。我的代碼合作伙伴和我喜歡Ruby,因爲它鼓勵我們編寫我們可以閱讀的代碼。我們,我比他更多,但我有比他更多的幾十年的編碼經驗。 :-) –