Variadic Zip Function in Swift

后端 未结 2 821
[愿得一人]
[愿得一人] 2021-01-15 19:47

Variadic Zip Function

Swift 4.1, Xcode 9.4

I have been using Apple\'s native zip(_:_:) recently and I ran into a situation whereby I needed

相关标签:
2条回答
  • 2021-01-15 20:16

    You need all the functions but you don't need multiple ZipSequence types.

    @inlinable public func zip<Sequence0: Sequence, Sequence1: Sequence, Sequence2: Sequence>(
      _ sequence0: Sequence0, _ sequence1: Sequence1, _ sequence2: Sequence2
    ) -> AnySequence<(Sequence0.Element, Sequence1.Element, Sequence2.Element)> {
      .init(
        sequence(
          state: (
            sequence0.makeIterator(), sequence1.makeIterator(), sequence2.makeIterator()
          )
        ) {
          .init(
            ($0.0.next(), $0.1.next(), $0.2.next())
          )
        }
      )
    }
    
    @inlinable public func zip<
      Sequence0: Sequence, Sequence1: Sequence, Sequence2: Sequence, Sequence3: Sequence
    >(
      _ sequence0: Sequence0, _ sequence1: Sequence1, _ sequence2: Sequence2, _ sequence3: Sequence3
    ) -> AnySequence<(Sequence0.Element, Sequence1.Element, Sequence2.Element, Sequence3.Element)> {
      .init(
        sequence(
          state: (
            sequence0.makeIterator(), sequence1.makeIterator(), sequence2.makeIterator(), sequence3.makeIterator()
          )
        ) {
          .init(
            ($0.0.next(), $0.1.next(), $0.2.next(), $0.3.next())
          )
        }
      )
    }
    
    public extension Optional {
      /// Exchange three optionals for a single optional tuple.
      /// - Returns: `nil` if any tuple element is `nil`.
      init<Wrapped0, Wrapped1, Wrapped2>(_ optionals: (Wrapped0?, Wrapped1?, Wrapped2?))
      where Wrapped == (Wrapped0, Wrapped1, Wrapped2) {
        switch optionals {
        case let (wrapped0?, wrapped1?, wrapped2?):
          self = (wrapped0, wrapped1, wrapped2)
        default:
          self = nil
        }
      }
    
      /// Exchange four optionals for a single optional tuple.
      /// - Returns: `nil` if any tuple element is `nil`.
      init<Wrapped0, Wrapped1, Wrapped2, Wrapped3>(_ optionals: (Wrapped0?, Wrapped1?, Wrapped2?, Wrapped3?))
      where Wrapped == (Wrapped0, Wrapped1, Wrapped2, Wrapped3) {
        switch optionals {
        case let (wrapped0?, wrapped1?, wrapped2?, wrapped3?):
          self = (wrapped0, wrapped1, wrapped2, wrapped3)
        default:
          self = nil
        }
      }
    }
    
    0 讨论(0)
  • 2021-01-15 20:23

    Sadly, this is not completely possible in Swift since we can't have different Array types for Variadic parameters. A possible work around would be to convert all arrays to type [Any], and then zip them all together which forms an array array, and than using map, converting the arrays to the wanted tuple using force casting. The solution might be bit dirty but not all too complicated.

    extension Sequence {
    
        //easy conversion of any sequence to [Any]
        var untyped:[Any] {
            return self as! [Any]
       }
    }
    
    func zip(_ untypedSequences:[Any]...) -> [[Any]] {
        let count = untypedSequences.map{$0.count}.min() ?? 0
        return (0..<count).map{ index in untypedSequences.map{ $0[index] } }
    }
    
    //Test
    let test1 = ["a", "b", "c"]
    let test2 = [1, 2, 3, 4]
    let test3 = [7.2, 12.23, 1.3]
    
    let zippedTests = zip(test1.untyped, test2.untyped, test3.untyped).map {
        ($0[0] as! String, $0[1] as! Int, $0[2] as! Double)
    }
    
    0 讨论(0)
提交回复
热议问题