【问题标题】:Swift How could I safely unwrap an optional property when filtering an array?Swift 在过滤数组时如何安全地解开可选属性?
【发布时间】:2019-07-26 05:47:20
【问题描述】:

我正在尝试使用对象类的可选属性过滤对象数组,所以我想知道在不提供默认值的情况下安全地解开该属性的最佳方法是什么。该属性是 Date 类型,因此提供替代值感觉就像是 hack,但我不确定如何做得更好。我知道如何安全地打开带有保护的常规选项,但我不确定在过滤数组时如何使用它。我的代码是这样的:

let completedGoalsThisWeek = goals.filter { $0.returnWeek(date: $0.dateAchieved) == deviceWeek }.count

有问题的属性是 dateAchieved,在很多情况下它会为零。

谢谢。

【问题讨论】:

  • 为什么Goal 的工作是确定日期在哪一周?这看起来像是一个错位的责任
  • 嗯,我想在 Goal 的数组上使用这些方法来过滤它们,所以我想我需要将它们添加到 Goal 类中。你会如何以不同的方式做到这一点?
  • A Goal 恰好有一个日期,但这并不意味着应该将日期操作放入目标中。想象一下,您的系统中有另一种类型,PrereleaseSalereturnWeek(date:) 会转到 Goal 还是 PrereleaseSale?两者都有需要知道周数的日期,因此它同样适用。正确的答案是两者都不是,这个函数可能应该作为Date 的扩展,或者可能是Calendar。我还会使用计算属性而不是函数,将其称为 weekNumber(假设我理解正确,它给出的数字是 0-51 或 1-52)
  • 这是有道理的,它使语法更加清晰和合乎逻辑,所以感谢您的提示。这确实意味着我必须使用您的答案来过滤数组,即使我不了解闭包(过去尝试过几次,它仍然没有点击)。
  • 您在自己的问题中使用了闭包 :) 什么不点击闭包?

标签: arrays swift optional


【解决方案1】:

没有什么特别之处。就像在任何其他情况下一样打开可选的:

let completedGoalsThisWeek = goals
    .lazy
    .filter { goal -> Bool in
        guard let dateAchieved = goal.dateAchieved else { return false }
        let isCurrentWeek = goal.returnWeek(date: dateAchieved) == deviceWeek
        return isCurrentWeek
    }.count

可以使用Optional.map来缩短它,但我建议不要这样做,它太神秘了:

let completedGoalsThisWeek = goals
    .lazy
    .filter { goal
        goal.dateAchieved.map { goal.returnWeek(date: $0) == deviceWeek } ?? false
    }.count

在任何一种情况下,我建议您使用.lazy.filter,以防止中间分配保存一组元素,如果您最终只计算它们并立即丢弃它们。

【讨论】:

  • @Sh_Khan 我在答案中添加了更多内容,我认为值得保留
  • 嘿@Alexander,感谢您发布该答案,希望对其他人有所帮助。但这有点超出了我的技能。你能告诉我一些关于什么目标 -> Bool in 的意思吗?
  • @CristianMoisei 这只是闭包的典型语法,将第一个(也是唯一的)参数命名为goal(替换隐式闭包参数$0),并将闭包的返回类型定义为属于Bool 类型。它包含在 Swift 编程语言指南中。关于闭包有一整节,但我建议您更一般地阅读整个文档。 docs.swift.org/swift-book/LanguageGuide/Closures.html
【解决方案2】:

这里出错

$0.returnWeek(date: $0.dateAchieved) == deviceWeek 

可以通过将参数 date 设为可选来解决,这将使返回也可选,但它会通过,因为您可以将可选值与非一个值进行比较


returnWeek 会喜欢

func returnWeek(date:Date?) -> TypeOFWeek? {
  guard let res = date else { return nil }
  // here return valid result 
}

let completedGoalsThisWeek = goals.filter { $0.dateAchieved != nil ?  ( $0.returnWeek(date: $0.dateAchieved!) == deviceWeek ) : false }.count

或者更好的模型

class Model { 
  var toWeek: TypeOfWeek? {
    // do where what in function which uses dateAchieved
  }
}

let completedGoalsThisWeek = goals.filter { $0.toWeek  == deviceWeek }.count

【讨论】:

  • 我不同意这一点。您不应该添加可选性来安抚呼叫站点。调用者应该先处理他们的可选参数,然后用非可选负载调用你,或者根本不调用你
  • @Alexander 实际上我不想让过滤器复杂化添加了另一个选项
  • @CristianMoisei 因为该函数存在于模型中,所以最好在该模型中创建一个计算的可选属性而不是一个函数,并使其像 3 片段
  • @Sh_Khan 这比让可选性渗透到应用程序的各个方面要好得多。如果我也想处理双重选项怎么办? returnWeek 函数是否应该采用双重选项?三倍呢?
  • @Alexander btw 添加了第三个更好的选择
猜你喜欢
  • 1970-01-01
  • 2016-10-29
  • 2021-04-14
  • 1970-01-01
  • 2021-06-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多