Skip to content

Add compacted() as a convenience for lazy.compactMap { $0 } #107

Closed
@kylemacomber

Description

@kylemacomber

One of the most common uses of compactMap is to just flatten the nils out of a sequence without transforming its elements, i.e. x.lazy.compactMap { $0 }.

A compacted() convenience method for this use case would be useful because, similar to joined() in the Standard Library, it wouldn't have to capture an escaping closure and (by the current conventions) it would be able to be lazy by default, i.e. x.compacted() // => Compacted<[Int?], Int>

A new Compacted sequence should also be able to conditionally conform to Collection and BidirectionalCollection, and it should precompute on initialization in order to provide an O(1) startIndex.

Here's a sketch of the Sequence conformance:

struct Compacted<Base: Sequence, Element>: Sequence
  where Base.Element == Element?
{
  let base: Base

  struct Iterator: IteratorProtocol {
    var base: Base.Iterator

    mutating func next() -> Element? {
      while let wrapped = base.next() {
        if let some = wrapped {
          return some
        } else {
          // skip nil
        }
      }
      return nil
    }
  }

  func makeIterator() -> Iterator {
    return Iterator(base: base.makeIterator())
  }
}

extension Sequence {
  func compacted<Unwrapped>() -> Compacted<Self, Unwrapped> where Element == Unwrapped? {
    Compacted(base: self)
  }
}

var tests: [[Int?]] = [
  [],
  [0],
  [nil],
  [0, nil],
  [nil, 0],
  [0, nil, 1, nil, 2, nil],
  [0, 1, 2, nil, nil, nil],
  [nil, nil, nil, 0, 1, 2],
]

for test in tests {
  assert(test.compacted().elementsEqual(test.compactMap({ $0 })))
}

// let x: Array<Int> = [1, 2, 3]
// _ = x.compacted() // error: instance method 'compacted()' requires the types 'Int' and 'Unwrapped?' be equivalent

Thanks to @natecook1000 for coming up with how to make the types work out!

Metadata

Metadata

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions