如何在Swift中只求数组的一部分之和?

如何在Swift中只求数组的一部分之和?,swift,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: "N

我目前正在尝试在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") ]

我想知道如何制作一个包含当前月份
时间
数据总和的变量?或者任何一个月?非常感谢你们提供的任何帮助。

你们也可以从功能上提供帮助

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

如果存储非可选的
int
而不是可选的
字符串
,那么这会简单得多。或者您可以只使用
NSDateComponents

首先,我要稍微重写一下您的结构,
这两个属性都可以是不可变的

我使用NSDate代替日期字符串,持续时间使用double

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
}

虽然这是一个很好的答案,但我想指出Rob的答案有一个小缺陷:struct
Hour
隐藏了日期字符串需要采用某种格式的依赖关系。这违反了,中的D。但这很容易解决

创建
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的
dateFromString()
可能返回nil。目前我们使用
强制展开它,但这在生产应用程序中可能不好

我们应该允许适当的错误处理,允许便利init抛出错误

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")
}
错误将被捕获



我可能会这样做:

// 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)
注意,在上述两种情况下,由于您使用的是optionals,因此我使用
??
来处理值为
nil
(以及
时间中是否存在非数字字符串)


就个人而言,我建议
Hour
使用
NSDate
Float
而不是
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)

还可以进行进一步的优化(例如,不重复实例化
NSDateFormatter
),但这说明了使用
NSDate
对象可以使用日历计算,而不是查找子字符串。

什么是参数时间?时间点?持续时间?为什么它是字符串而不是数字类型?对于日期,您应该使用NSDate或NSDateComponents对象。Viking提出了一个很好的观点,通常尝试创建自己的日期结构是一个坏主意(TM)。如果要使用某个日期单位,请使用NSDateComponents。@vikingosegundo参数“时间”是一小时,比如说7小时。这在我脑子里是有道理的哈哈,对不起我没有解释清楚。它们都是字符串,因为它们是由用户输入并放入表中的。正如我所说,我是新手,所以这似乎是最好的方法。但是,日期是使用NSDate输入的。您应该将用户的输入转换为可使用的最佳类型。如果字符串参数始终包含数字字符串,请将其设置为数字参数!约会也一样。这真的很棒,非常适合我要找的东西!但是我遇到了一些问题,我使用结果作为UILabel的数据,但是当我向数组添加数据时(通过添加小时数屏幕),标签不会更新。你能为我指出正确的方向吗?你可以做一些类似于
self.label.text=“\(结果)”
。您可能需要执行类似于
self.label.text=“total=\(results)”
的操作,然后查看字符串的
total=
部分是否也显示出来。这就把它缩小到标签的一些问题,而不是计算
结果的一些问题。如果您是从后台线程(例如,从
NSURLSession
任务的完成处理程序中)执行此操作,请记住将其分派到主队列,例如
dispatch\u async(dispatch\u get\u main\u queue()){self.label.text=“total=\(results)}
@IsaacPevy-如果您在成功计算总数后更新标签时仍然遇到问题(例如,您已经打印了总数,这很好),那么在标签更新过程中发布问题,重点是向我们展示的不是上述计算内容,而是标签更新内容。