In Swift, an efficient function that separates an array into 2 arrays based on a predicate

后端 未结 7 1072
太阳男子
太阳男子 2020-12-15 04:40

Note: I\'m currently still using Swift 2.2, but open to Swift 3 solutions as well

I\'m looking to create a function that operates very closely to filter

相关标签:
7条回答
  • 2020-12-15 04:56

    There is a new Swift Algorithms open-source for sequence and collection algorithms, along with their related types.

    You can use the stable partition from there

    Methods for performing a stable partition on mutable collections, and for finding the partitioning index in an already partitioned collection.

    The standard library’s existing partition(by:) method, which re-orders the elements in a collection into two partitions based on a given predicate, doesn’t guarantee stability for either partition. That is, the order of the elements in each partition doesn’t necessarily match their relative order in the original collection. These new methods expand on the existing partition(by:) by providing stability for one or both partitions.

    // existing partition(by:) - unstable ordering
    var numbers = [10, 20, 30, 40, 50, 60, 70, 80]
    let p1 = numbers.partition(by: { $0.isMultiple(of: 20) })
    // p1 == 4
    // numbers == [10, 70, 30, 50, 40, 60, 20, 80]
    
    // new stablePartition(by:) - keeps the relative order of both partitions
    numbers = [10, 20, 30, 40, 50, 60, 70, 80]
    let p2 = numbers.stablePartition(by: { $0.isMultiple(of: 20) })
    // p2 == 4
    // numbers == [10, 30, 50, 70, 20, 40, 60, 80]
    

    Since partitioning is frequently used in divide-and-conquer algorithms, we also include a variant that accepts a range parameter to avoid copying when mutating slices, as well as a range-based variant of the existing standard library partition.

    The partitioningIndex(where:) method returns the index of the start of the second partition when called on an already partitioned collection.

    let numbers = [10, 30, 50, 70, 20, 40, 60]
    let p = numbers.partitioningIndex(where: { $0.isMultiple(of: 20) })
    // numbers[..<p] == [10, 30, 50, 70]
    // numbers[p...] = [20, 40, 60]
    
    0 讨论(0)
  • 2020-12-15 04:57

    In WWDC 2018 session Embracing Algorithm they mention the function stablePartition, you can take a look here https://github.com/apple/swift/blob/master/test/Prototypes/Algorithms.swift

    extension Collection where Self : MutableCollectionAlgorithms {
    
      @discardableResult
      mutating func stablePartition(
        isSuffixElement: (Element) throws -> Bool
      ) rethrows -> Index {
            return try stablePartition(
                count: count, isSuffixElement: isSuffixElement)
      }
    
        /// Moves all elements satisfying `isSuffixElement` into a suffix of the collection,
        /// preserving their relative order, returning the start of the resulting suffix.
        ///
        /// - Complexity: O(n) where n is the number of elements.
        /// - Precondition: `n == self.count`
        fileprivate mutating func stablePartition(
            count n: Int, isSuffixElement: (Element) throws-> Bool
        ) rethrows -> Index {
            if n == 0 { return startIndex }
            if n == 1 {
                return try isSuffixElement(self[startIndex]) ? startIndex : endIndex
            }
            let h = n / 2, i = index(startIndex, offsetBy: h)
            let j = try self[..<i].stablePartition(
                count: h, isSuffixElement: isSuffixElement)
            let k = try self[i...].stablePartition(
                count: n - h, isSuffixElement: isSuffixElement)
            return self[j..<k].rotate(shiftingToStart: i)
        }
    }
    
    0 讨论(0)
  • 2020-12-15 04:59

    Technically, this is not guaranteed to preserve order, but it does.

    Dictionary(grouping: numbers) { $0.isMultiple(of: 3) }
    

    https://github.com/apple/swift/blob/master/stdlib/public/core/NativeDictionary.swift

    0 讨论(0)
  • 2020-12-15 05:01
    let objects: [Int] = Array(1..<11)
    let split = objects.reduce(([Int](), [Int]())) { (value, object) -> ([Int], [Int]) in
    
        var value = value
    
        if object % 2 == 0 {
            value.1.append(object)
        } else {
            value.0.append(object)
        }
    
        return value
    }
    
    0 讨论(0)
  • 2020-12-15 05:01

    Solution A

    For fewer elements this may be fastest.

    extension Array {
        func stablePartition(by condition: (Element) -> Bool) -> ([Element], [Element]) {
            var matching = [Element]()
            var nonMatching = [Element]()
            for element in self {
                if condition(element) {
                    matching.append(element)
                } else {
                    nonMatching.append(element)
                }
            }
            return (matching, nonMatching)
        }
    }
    

    Usage

    let numbers = [1,2,3,4,5,6,7,8,9,10]
    let (divisibleBy3, theRest) = numbers.stablePartition { $0 % 3 == 0 }
    print("divisible by 3: \(divisibleBy3), the rest: \(theRest)")
    // divisible by 3: [3, 6, 9], the rest: [1, 2, 4, 5, 7, 8, 10]
    

    Solution B

    For many elements this may be faster, because of fewer allocations. I have not measured performance.

    extension Array {
        public func stablePartition(by condition: (Element) throws -> Bool) rethrows -> ([Element], [Element]) {
            var indexes = Set<Int>()
            for (index, element) in self.enumerated() {
                if try condition(element) {
                    indexes.insert(index)
                }
            }
            var matching = [Element]()
            matching.reserveCapacity(indexes.count)
            var nonMatching = [Element]()
            nonMatching.reserveCapacity(self.count - indexes.count)
            for (index, element) in self.enumerated() {
                if indexes.contains(index) {
                    matching.append(element)
                } else {
                    nonMatching.append(element)
                }
            }
            return (matching, nonMatching)
        }
    }
    
    0 讨论(0)
  • 2020-12-15 05:02

    Swift 4 Solution

    partition(by:)

    It reorders the origin array and returns start index of subarray satisfies the predicate.

    In this example it returns 7.

    0..<7 elemets aren't divisible by 3 and 7..n-1 elements are divisible by 3.

     var numbers = [1,2,3,4,5,6,7,8,9,10]
     let partition = numbers.partition(by: { $0 % 3 == 0 })
     let divisibleBy3 = Array(numbers[..<partition]) //[3,6,9]
     let theRest = Array(numbers[partition...]) //[1,2,4,5,7,8,10]
    
    0 讨论(0)
提交回复
热议问题