【问题标题】:How do you check elegantly case enum in the .first(where:) closure in Swift?如何在 Swift 的 .first(where:) 闭包中优雅地检查大小写枚举?
【发布时间】:2020-11-12 16:25:22
【问题描述】:

假设我有这个枚举:

enum Item {
  case foo(String)
  case bar(String)
}

及其列表:

let items: [Item] = [.foo("aa"), .bar("bb")]

我想找到它的第一个 foo 项,这就是我所做的:

items.first { (item) -> Bool in
  switch item {
  case .foo:
    return true
  default:
    return false
  }
}

有没有什么优雅的写法:

items.first { case .foo = $0 }

(上面的代码显然不起作用)

【问题讨论】:

标签: swift enums


【解决方案1】:

枚举器关联值(已编辑问题)

如果您有一个其枚举数具有关联值的枚举,则可以替换 if case (let) ... else 语句的 switch 语句,因为您只搜索给定的枚举数模式(其余均不搜索 -> else )。例如:

let item = items.first { 
    if case .foo = $0 { return true }
    else { return false }
}

没有关联值的枚举器(原始问题)

= 是赋值,而== 是相等比较。在这里,你想要后者:

let item = items.first { $0 == .foo }

// or
let item = items.first { .foo == $0 }

您还可以使用~= 运算符进行模式匹配:

let item = items.first { $0 ~= .foo }

// or
let item = items.first { .foo ~= $0 }

【讨论】:

  • 您好,感谢您的回复,我已更新问题以使其更清楚,因为它是一个没有关联值的枚举。
  • @dfrib OP 解决方案也应该可以工作。目前尚不清楚 OP 试图完成什么。也许OP想要返回字符串值。
  • @LeoDabus 真;我一直偏爱if case let ... else 而不是switch - case - default 来搜索单个枚举器,但我同意OP 可能没有准确说明实际意图是什么。我多年来的第一个 Swift 答案!我很惊讶我们不允许直接使用模式匹配(... first { .foo ~= $0 });对于具有相关值的枚举器来说,这从来都不是一件事情吗?我想我们需要为Item 类型实现自定义~= 重载?
  • 欢迎回来,伙计。我从来没有玩过相关值的集合
  • if let item = items.first(where: {switch $0 {case .foo: return truedefault: return false }}), case let .foo(string) = item {print(string)}。 Arghhh 这就是为什么我不喜欢带有关联值的枚举。
【解决方案2】:

first 不会做任何有用的事情。我想你想要contains

items.contains { Item.foo ~= $0 }

无论如何,你都需要这个:

/// Match `enum` cases with associated values, while disregarding the values themselves.
/// - Parameter case: Looks like `Enum.case`.
public func ~= <Enum: Equatable, AssociatedValue>(
  case: (AssociatedValue) -> Enum,
  instance: Enum
) -> Bool {
  Mirror.associatedValue(of: instance, ifCase: `case`) != nil
}

/// Match `enum` cases with associated values, while disregarding the values themselves.
/// - Parameter case: Looks like `Enum.case`.
public func ~= <Enum, AssociatedValue>(
  case: (AssociatedValue) -> Enum,
  instance: Enum
) -> Bool {
  Mirror.associatedValue(of: instance, ifCase: `case`) != nil
}

/// Match non-`Equatable` `enum` cases without associated values.
public func ~= <Enum>(pattern: Enum, instance: Enum) -> Bool {
  guard (
    [pattern, instance].allSatisfy {
      let mirror = Mirror(reflecting: $0)
      return
        mirror.displayStyle == .enum
        && mirror.children.isEmpty
    }
  ) else { return false }

  return .equate(pattern, to: instance) { "\($0)" }
}
public extension Mirror {
  /// Get an `enum` case's `associatedValue`.
  static func associatedValue<AssociatedValue>(
    of subject: Any,
    _: AssociatedValue.Type = AssociatedValue.self
  ) -> AssociatedValue? {
    guard let childValue = Self(reflecting: subject).children.first?.value
    else { return nil }

    if let associatedValue = childValue as? AssociatedValue {
      return associatedValue
    }

    let labeledAssociatedValue = Self(reflecting: childValue).children.first
    return labeledAssociatedValue?.value as? AssociatedValue
  }

  /// Get an `enum` case's `associatedValue`.
  /// - Parameter case: Looks like `Enum.case`.
  static func associatedValue<Enum: Equatable, AssociatedValue>(
    of instance: Enum,
    ifCase case: (AssociatedValue) throws -> Enum
  ) rethrows -> AssociatedValue? {
    try associatedValue(of: instance)
      .filter { try `case`($0) == instance }
  }

  /// Get an `enum` case's `associatedValue`.
  /// - Parameter case: Looks like `Enum.case`.
  static func associatedValue<Enum, AssociatedValue>(
    of instance: Enum,
    ifCase case: (AssociatedValue) throws -> Enum
  ) rethrows -> AssociatedValue? {
    try associatedValue(of: instance).filter {
      .equate(try `case`($0), to: instance) {
        Self(reflecting: $0).children.first?.label
      }
    }
  }
}
public extension Optional {
  /// Transform `.some` into `.none`, if a condition fails.
  /// - Parameters:
  ///   - isSome: The condition that will result in `nil`, when evaluated to `false`.
  func filter(_ isSome: (Wrapped) throws -> Bool) rethrows -> Self {
    try flatMap { try isSome($0) ? $0 : nil }
  }
}
public extension Equatable {
  /// Equate two values using a closure.
   static func equate<Wrapped, Equatable: Swift.Equatable>(
    _ optional0: Wrapped?, to optional1: Wrapped?,
    using transform: (Wrapped) throws -> Equatable
  ) rethrows -> Bool {
    try optional0.map(transform) == optional1.map(transform)
  }
}

【讨论】:

  • 您好,感谢您的回复,我已更新问题以使其更清晰。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-07-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多