'swift: Elegant Way to Map Optional to Array

I am looking for an elegant way to map an Optional to an Array. What I mean by that is the following:

  • If the Optional is .none, return an empty Array<Wrapped>
  • If the Optional is .some, return a single element Array<Wrapped>

Now this can be done like this

let seq = value.map { [$0] } ?? []

Unfortunately this gets quite ugly and illegible when you want to use it inline.

Is there a better method to accomplish this, without writing a extension?



Solution 1:[1]

How about putting your optional in an array, and compactMap that array with the identity function?

[yourOptional].compactMap { $0 }

As Martin R suggested, you can use CollectionOfOne to save the creation of a throwaway array, at the cost of writing a few more characters:

CollectionOfOne(yourOptional).compactMap { $0 }

Solution 2:[2]

Is there a better method to accomplish this, without writing a extension?

No. The method you're looking for still hasn't made its way into the Swift Standard Library.

public extension Optional {
  /// Create a single-element array literal, or an empty one.
  /// - Returns: `[self!]` if `.some`; `[]` if `nil`.
  /// - Note: This cannot be generalized to all types,
  /// as Swift doesn't employ  universal non-optional defaults.
  func compacted<ExpressibleByArrayLiteral: Swift.ExpressibleByArrayLiteral>() -> ExpressibleByArrayLiteral
  where ExpressibleByArrayLiteral.ArrayLiteralElement == Wrapped {
    .init(compacting: self)
  }
}
// MARK: - ExpressibleByArrayLiteral
public extension ExpressibleByArrayLiteral {
  /// Create a single-element array literal, or an empty one.
  /// - Returns: `[optional!]` if `.some`; `[]` if `nil`.
  /// - Note: This cannot be generalized to all types,
  /// as Swift doesn't employ  universal non-optional defaults.
  init(compacting optional: ArrayLiteralElement?) {
    self = optional.map { [$0] } ?? []
  }
}
XCTAssertEqual(
  ["??"],
  ("??" as Optional).compacted()
)

XCTAssertEqual(
  Set(),
  Int?.none.compacted()
)

C# is an example of a language with "universal non-optional defaults".


NSPointerArray has the argument-less compact, which is a similar concept, and the inspiration for the first half of the compactMap that does exist in the standard library. It's now available as compacted.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2