2015-12-22 28 views
2

我目前正在試圖在Swift中構建我的第一個應用程序,並且正在尋找相對於當前月份的數組的一部分的總和。這裏是我的代碼:如何在Swift中只將數組的一部分加起來?

struct Hour { 
    var date: String? 
    var time: String? 

    init(date: String?, time: String?) { 
     self.date = date 
     self.time = time 
    } 
} 


let hoursData = [ 
    Hour(date: "Nov 29, 2015", time: "7"), 
    Hour(date: "Dec 12, 2015", time: "7"), 
    Hour(date: "Dec 14, 2015", time: "7"), 
    Hour(date: "Dec 25, 2015", time: "7") ] 

我不知道我怎麼會去有關使其中包含time數據的當月總和變量?或者這個問題的任何一個月?任何幫助你們可以給予真正的讚賞。

+0

什麼是參數時間?一個時間點?持續時間?爲什麼它是一個字符串而不是數字類型?對於日期,您應該使用NSDate或NSDateComponents對象。 – vikingosegundo

+0

Viking提供了一個很好的觀點,通常它是一個糟糕的想法(TM),試圖製作自己的日期結構。如果您想使用日期的某個單位,請使用NSDateComponents。 – Kevin

+0

@vikingosegundo參數時間是一個小時,所以說7個小時的時間。這在我的腦海中是有道理的,對不起,我沒有很好地解釋它。它們都是字符串,因爲它們是由用戶輸入並放入表格中的。正如我所說我是新手,所以這似乎是最好的辦法。但是,使用NSDate輸入日期。 –

回答

0

我可能會做這樣的事情:

// get prefix for current month 

let formatter = NSDateFormatter() 
formatter.dateFormat = "MMM" 
let monthString = formatter.stringFromDate(NSDate()) 

// now add up the time values for that month 

let results = hoursData.filter { $0.date?.hasPrefix(monthString) ?? false } 
    .reduce(0) { $0 + (Int($1.time ?? "0") ?? 0) } 

或者,如果你想添加爲一年一檢查,太:

let formatter = NSDateFormatter() 
formatter.dateFormat = "MMM" 
let monthString = formatter.stringFromDate(NSDate()) 

formatter.dateFormat = "yyyy" 
let yearString = formatter.stringFromDate(NSDate()) 

let results = hoursData.filter { $0.date?.hasPrefix(monthString) ?? false && $0.date?.hasSuffix(yearString) ?? false } 
    .reduce(0) { $0 + (Int($1.time ?? "0") ?? 0) } 

print(results) 

注意,在以上兩者,因爲您使用的是可選項,我使用??來處理值是nil(以及time中是否有非數字字符串)。


就個人而言,我建議使用HourNSDateFloat而非String,除非你真的需要他們不要使用自選,並使用let代替var

struct Hour { 
    let date: NSDate 
    let time: Float 

    init(dateString: String, time: Float) { 
     let formatter = NSDateFormatter() 
     formatter.dateFormat = "MMM, d, y" 
     self.date = formatter.dateFromString(dateString)! 
     self.time = time 
    } 
} 

然後代碼變得:

let hoursData = [ 
    Hour(dateString: "Nov 29, 2015", time: 7), 
    Hour(dateString: "Dec 12, 2015", time: 7), 
    Hour(dateString: "Dec 14, 2015", time: 7), 
    Hour(dateString: "Dec 25, 2015", time: 7), 
    Hour(dateString: "Dec 25, 2017", time: 7) 
] 

let calendar = NSCalendar.currentCalendar() 
let currentComponents = calendar.components([.Month, .Year], fromDate: NSDate()) 

let results = hoursData.filter { 
    let components = calendar.components([.Month, .Year], fromDate: $0.date) 
    return components.month == currentComponents.month && components.year == currentComponents.year 
}.reduce(0) { return $0 + $1.time } 

print(results) 

還有進一步優化可能性e(例如不會重複實例化NSDateFormatter),但它說明了使用NSDate對象可讓您使用日曆計算而不是查找子字符串的想法。

+0

這真的很棒,非常適合我尋找的東西!我遇到了一些麻煩,我將結果用作UILabel的數據,但是當我向數組添加數據時(通過添加小時數屏幕),標籤不會更新。你能指出我如何做到這一點的正確方向嗎? –

+0

你會像'self.label.text =「\(results)」'做一些事情。你可能想做一些事情,比如'self.label.text =「total = \(results)」',看看這個字符串的total ='部分是否也顯示出來。這縮小了標籤的某些問題,還是計算「結果」的問題。如果你是通過後臺線程(例如,來自「NSURLSession」任務的完成處理程序)完成此操作,則請記住將其分派到主隊列中,例如, 'dispatch_async(dispatch_get_main_queue()){self.label.text =「total = \(results)」}'。 – Rob

+0

@IsaacPevy - 如果你在更新標籤成功計算總數時仍然有問題(例如你已經打印了總數,那很好),然後發佈關於標籤更新過程的問題,向我們展示上述計算內容,但只是標籤更新的東西。 – Rob

0

你也可以做到這一點功能

let sum = hoursData 
    .filter { $0.date?.hasPrefix("Dec") ?? false } // Only get dates in the correct month 
    .flatMap { $0.time }       // Map to an array of non nil times 
    .flatMap { Int($0) }       // Convert strings to ints 
    .reduce(0) { $0 + $1 }       // Sum up the times 

這可以是簡單了很多,如果你存儲非可選Ints,而不是可選Strings。或者你可以使用NSDateComponents

2

首先我會重寫你的結構,
這兩個屬性都可以是不可變的。

而不是爲日期的字符串,我使用的NSDate,和雙爲持續時間

struct Hour { 
    let date: NSDate 
    let duration: Double 

    init(date: NSDate, duration: Double) { 
     self.date = date 
     self.duration = duration 
    } 
} 

我創建的時間像

let hoursData = [ 
    Hour(date: { let c = NSDateComponents(); c.day = 29; c.month = 11; c.year = 2015; return NSCalendar.currentCalendar().dateFromComponents(c)!}(), duration: 7), 
    Hour(date: { let c = NSDateComponents(); c.day = 12; c.month = 12; c.year = 2015; return NSCalendar.currentCalendar().dateFromComponents(c)!}(), duration: 7), 
    Hour(date: { let c = NSDateComponents(); c.day = 14; c.month = 12; c.year = 2015; return NSCalendar.currentCalendar().dateFromComponents(c)!}(), duration: 7), 
    Hour(date: { let c = NSDateComponents(); c.day = 15; c.month = 12; c.year = 2015; return NSCalendar.currentCalendar().dateFromComponents(c)!}(), duration: 7)] 

,如果你想知道這個語法:我使用隱式未命名的閉包創建NSDate參數

現在我篩選一個月和一年使用reduce方法來總結過濾對象

let today = NSDate() 
let totalDuration = hoursData.filter{ 
    let objectsComps = NSCalendar.currentCalendar().components([.Month, .Year], fromDate: $0.date) 
    let todaysComps = NSCalendar.currentCalendar().components([.Month, .Year], fromDate: today) 
    return objectsComps.month == todaysComps.month && objectsComps.year == todaysComps.year 
}.reduce(0) { (duration, hour) -> Double in 
    duration + hour.duration 
} 

雖然這是一個很好的答案,我想指出的是,羅布的回答有一個小缺陷:結構Hour隱藏它有日期字符串必須是某種格式的依賴。這違反了Dependency Inversion Principle,DSOLID Principles。但這很容易解決。

只要在創建Hour實例時通過日期格式化程序。

struct Hour { 
    let date: NSDate 
    let duration: Double 

    init(date: NSDate, duration: Double) { 
     self.date = date 
     self.duration = duration 
    } 

    init (dateFormatter:NSDateFormatter, dateString:String, duration:Double) { 
     self.init(date: dateFormatter.dateFromString(dateString)!, duration:duration) 
    } 
} 

let dateFormatter = NSDateFormatter() 
dateFormatter.dateFormat = "MMM d, y" 

let hoursData = [ 
    Hour(dateFormatter: dateFormatter, dateString: "Nov 29, 2015", duration: 7), 
    Hour(dateFormatter: dateFormatter, dateString: "Dec 12, 2015", duration: 7), 
    Hour(dateFormatter: dateFormatter, dateString: "Dec 14, 2015", duration: 7), 
    Hour(dateFormatter: dateFormatter, dateString: "Dec 25, 2015", duration: 7), 
    Hour(dateFormatter: dateFormatter, dateString: "Dec 25, 2017", duration: 7) 
] 

現在誰曾經使用Hour可以定義爲所需要的格式,可能會在本地化的應用程序很有幫助。

過濾和減少保持不變。


但現在我們有了一個新的問題:NSDateformatter'sdateFromString()可能返回零。目前我們用!強制解包,但這可能在生產應用中很糟糕。

我們應該允許適當的錯誤處理,通過允許方便初始化拋出錯誤

enum HourError: ErrorType { 
    case InvalidDate 
} 

struct Hour { 

    let date: NSDate 
    let duration: Double 

    init(date: NSDate, duration: Double) { 
     self.date = date 
     self.duration = duration 
    } 

    init (dateFormatter:NSDateFormatter, dateString:String, duration:Double) throws { 
     let date = dateFormatter.dateFromString(dateString) 
     guard date != nil else { throw HourError.InvalidDate} 
     self.init(date: date!, duration:duration) 
    } 
} 

,如果我們使用它像

do { 
    let now = NSDate() 
    let dateFormatter = NSDateFormatter() 
    dateFormatter.dateFormat = "MMM d, y" 

    let hoursData = [ 
     try Hour(dateFormatter: dateFormatter, dateString: "Nov 29, 2015", duration: 7), 
     try Hour(dateFormatter: dateFormatter, dateString: "Dec 12, 2015", duration: 7), 
     try Hour(dateFormatter: dateFormatter, dateString: "Dec 14, 2015", duration: 7), 
     try Hour(dateFormatter: dateFormatter, dateString: "Dec 25, 2015", duration: 7), 
/*⛈*/ try Hour(dateFormatter: dateFormatter, dateString: " 25, 2017", duration: 7) 
    ] 



    let totalDuration = hoursData.filter{ 
     let objectsComps = NSCalendar.currentCalendar().components([.Month, .Year], fromDate: $0.date) 
     let todaysComps = NSCalendar.currentCalendar().components([.Month, .Year], fromDate: now) 
     return objectsComps.month == todaysComps.month && objectsComps.year == todaysComps.year 
     }.reduce(0) { 
      $0 + $1.duration 
    } 
    print(totalDuration) 
} catch HourError.InvalidDate{ 
    print("one or more datestrings must be wrong") 
} 

錯誤會被抓住。


full code

相關問題