I\'m looking to create a function that operates very closely to filter
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]
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)
}
}
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
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
}
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)
}
}
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]
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)
}
}
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]