user187676
user187676

Reputation:

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:

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?

Upvotes: 3

Views: 850

Answers (2)

user652038
user652038

Reputation:

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.

Upvotes: 1

Sweeper
Sweeper

Reputation: 272725

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 }

Upvotes: 2

Related Questions