extension Array where Element == Int {
mutating func bubbleSort() {
let n = count
guard n > 1 else { return }
for i in 0 ..< n {
var isSorted = true
for j in 1 ..< n - i {
if self[j - 1] > self[j] {
swapAt(j - 1, j)
isSorted = false
}
}
if isSorted { break }
}
}
}
import XCTest //MARK: UnitTests
class SortingTest: XCTestCase {
func testSorting1() {
var a = [9, 2, 5, 3, 8, 1, 4, 7, 6]
a.bubbleSort()
XCTAssertEqual(a, [1, 2, 3, 4, 5, 6, 7, 8, 9])
}
func testSorting2() {
var a = [4, 3, 2, 1]
a.bubbleSort()
XCTAssertEqual(a, [1, 2, 3, 4])
}
func testSorting3() {
var a = [2, 1]
a.bubbleSort()
XCTAssertEqual(a, [1, 2])
}
//And many many more tests here
}
SortingTest.defaultTestSuite.run()
That's playground code of bubbleSort algorithm and it works totally fine.
But what if I implement one more sort algorithm:
extension Array where Element == Int {
mutating func oneMoreSortAlg() {
// Some algorithm code here
}
mutating func andEvenMoreSortAlg() {
// Some more code here
}
}
Obviously the test code will be almost the same for each algorithm. So I have to somehow pass which one I want to test now.
I'm new to Unit Testing so I don't know what is the canonical way to do this. I'll appreciate any hints.
And it raises one more question. Let's suppose I will decide to do it in this way (probably not canonical):
class SortingTest: XCTestCase {
let customSort = Array.object.oneMoreSortAlg //it's defenetly a wrong syntax
func testSorting1() {
var a = [9, 2, 5, 3, 8, 1, 4, 7, 6]
a.customSort() // or maybe customSort(a), I really don't know
XCTAssertEqual(a, [1, 2, 3, 4, 5, 6, 7, 8, 9])
}
func testSorting2() {
var a = [4, 3, 2, 1]
a.customSort() // or maybe customSort(a), I really don't know
XCTAssertEqual(a, [1, 2, 3, 4])
}
func testSorting3() {
var a = [2, 1]
a.customSort() // or maybe customSort(a), I really don't know
XCTAssertEqual(a, [1, 2])
}
//And many many more tests here
}
I'm not new to swift but I've realised that I don't know how to correctly manage an assignment of method to a variable either.
An instance method of a given instance is a first-class object. So you can just make a list of all the algorithms you want to try and try them in turn. Simplified example:
struct S {
func alg1() -> String {
return "hey"
}
func alg2() -> String {
return "hey"
}
}
func testBoth() {
let s = S()
let algs = [s.alg1, s.alg2] // <- this is the point
for alg in algs {
let result = alg()
print(result)
}
}
(But as Dávid Pásztor points out, you'll need to wrap your actual example code in such a way that you are not trying to make a list of mutating methods.)
You can store a function in a variable by simply accessing the function, but not executing it (not writing the () after the method name).
Assign the result of the function: let sorted: [Int] = [1,2,3].sorted()
Assign the function: let sort: () -> [Int] = [1,2,3].sorted
However, you cannot store a mutating function in a variable, because that would result in the compiler error
Partial application of 'mutating' method is not allowed
You can overcome this issue by wrapping all of your mutating funcs in non-mutating
functions and storing those non-mutating functions in your unit tests.
Create the non-mutating wrappers in your test target:
extension Array where Element == Int {
func sortedByAlg1() -> [Element] {
var copy = self
copy.oneMoreSortAlg()
return copy
}
func sortedByAlg2() -> [Element] {
var copy = self
copy.andEvenMoreSortAlg()
return copy
}
}
And then your test file:
class TestSortingAlgos: XCTestCase {
// Initialise this to the values you want to input to your sorting functions
// If you want to use several sets of I/O pairs, simply change these two arrays to be nested arrays ([[Int]]())
var input = [Int]()
/// Initialise this to the expected outputs for your input
var output = [Int]()
func testAllSorts() {
let sortingFuncs = [input.sortedByAlg1, input.sortedByAlg2]
for sortingFunc in sortingFuncs {
// Beware that `sortingFunc` here is executed on the array you used in the `sortingFuncs` declaration (`input`) for our case - if you wanted to iterate over an array of inputs, you'd need to create an outer loop and assing `sortingFuncs` there.
XCTAssertEqual(sortingFunc(),output)
}
}
}
It turns out that for each instance method a type has, there's a
corresponding static method that lets you retrieve that instance
method as a closure, by passing an instance as an argument.
For example: [3, 2, 1].sort() == Array.sort([3, 2, 1])
Interesting fact, but pretty useless in our case, because
Partial application of 'mutating' method is not allowed.
So as long as I can't pass a mutating method as a parameter, I will pass an enumeration object:
extension Array where Element == Int {
mutating func sort(_ algorithm: SortAlgorithm) {
switch algorithm {
case .bubble: bubbleSort()
case .selection: selectionSort()
}
}
}
enum SortAlgorithm {
case bubble
case selection
}
As for second part of the question I think the best answer is inheritance:
// MARK: - Basic UnitTest Class
class SortingTest: XCTestCase {
static var algorithm: SortAlgorithm!
override static func tearDown() {
algorithm = nil
super.tearDown()
}
func testSorting1() {
var a = [9, 2, 5, 3, 8, 1, 4, 7, 6]
a.sort(Self.algorithm)
XCTAssertEqual(a, [1, 2, 3, 4, 5, 6, 7, 8, 9])
}
func testSorting2() {
var a = [4, 3, 2, 1]
a.sort(Self.algorithm)
XCTAssertEqual(a, [1, 2, 3, 4])
}
......
// MARK: - Inherited UnitTest Classes
class BubbleTest: SortingTest {
override static func setUp() {
super.setUp()
algorithm = .bubble
}
}
class SelectionTest: SortingTest {
override static func setUp() {
super.setUp()
algorithm = .selection
}
}
Everything is ready for nicely calling tests for appropriate sorting algorithm:
extension enum SortAlgorithm {
func runTests() {
switch self {
case .bubble: BubbleTest.defaultTestSuite.run()
case .selection: SelectionTest.defaultTestSuite.run()
}
}
}
SortAlgorithm.bubble.runTests()
P.S. Thank #DávidPásztor and #matt for hints :)
I'm looking to do a "reverse zip" on a collection. Essentially meaning:
let a = [1, 2, 3, 4, 5, 6, 7]
let (left, right) = a.reverseZip()
print(left) // [1, 3, 5, 7]
print(right) // [2, 4, 6]
I have an implementation that seems to work (see my answer listed below), but I have two questions that I'm hoping we could discuss:
What's the correct name for this kind of operation? It's not exactly the opposite of zip (and I'm certain this pattern exists elsewhere in another functional language)
Is there a more optimal algorithm for this method?
This is a seed of an idea. Instead of returning arrays, it would be more efficient to return sequences.
Here is an Array extension that returns two LazyMapSequence<StrideTo<Int>, Element>. The advantage is that is doesn't actually generate the sequences; they are provided on demand because they're lazy. This is much like the way reversed() works on an Array.
extension Array {
func reverseZip() -> (LazyMapSequence<StrideTo<Int>, Element>, LazyMapSequence<StrideTo<Int>, Element>) {
let left = stride(from: 0, to: self.count, by: 2).lazy.map { self[$0] }
let right = stride(from: 1, to: self.count, by: 2).lazy.map { self[$0] }
return (left, right)
}
}
Example:
let a = [1, 2, 3, 4, 5, 6, 7]
let (left, right) = a.reverseZip()
// iterate left
for i in left {
print(i)
}
1
3
5
7
// turn them into arrays to print them
print(Array(left))
[1, 3, 5, 7]
print(Array(right))
[2, 4, 6]
I'm sure this can be generalized to something more general than Array. That is left as an exercise to the reader.
If you want two arrays
If you really want the two arrays, then you'd just return the result of map (taking out lazy):
extension Array {
func reverseZip() -> ([Element], [Element]) {
let left = stride(from: 0, to: self.count, by: 2).map { self[$0] }
let right = stride(from: 1, to: self.count, by: 2).map { self[$0] }
return (left, right)
}
}
Based on vacawama's idea to return lazy collections instead of arrays,
here is a possible generalization to arbitrary collections with arbitrary strides:
extension Collection {
func makeStrideIterator(stride: Int) -> AnyIterator<Element> {
precondition(stride > 0, "The stride must be positive")
var it = makeIterator()
var first = true
return AnyIterator<Element> {
if first {
first = false
} else {
// Skip `stride - 1` elements:
for _ in 1..<stride {
guard let _ = it.next() else { return nil }
}
}
return it.next()
}
}
}
The method returns an AnyIterator (which is both an iterator and a sequence)
of the collection elements at the positions
0, stride, 2*stride, ...
Unzipping an array (or any other collection) can then be done with
let a = [1, 2, 3, 4, 5]
let left = a.makeStrideIterator(stride: 2)
let right = a.dropFirst().makeStrideIterator(stride: 2)
for e in left { print(e) }
// 1, 3, 5
for e in right { print(e) }
// 2, 4
Here is an example for picking every third character from a string:
for c in "ABCDEFG".makeStrideIterator(stride: 3) {
print(c)
}
// A D G
The special case of unzipping into two arrays can then be implemented as
extension Collection {
func unzip() -> ([Element], [Element]) {
return (Array(makeStrideIterator(stride: 2)),
Array(dropFirst().makeStrideIterator(stride: 2)))
}
}
Example:
print([1, 2, 3, 4, 5].unzip())
// ([1, 3, 5], [2, 4])
extension Collection {
func reverseZip() -> ([Element], [Element]) {
var left = [Element]()
var right = [Element]()
let capacity = Double(self.count) / 2.0
left .reserveCapacity(Int(ceil(capacity)))
right.reserveCapacity(self.count / 2)
for element in self {
if left.count > right.count {
right.append(element)
} else {
left.append(element)
}
}
return (left, right)
}
}
UPDATE (03/10/18)
Thank you all for your help. Seems like a lot of you latched onto Stride, which is definitely a good way to go. I resisted this approach for 2 reasons:
I'm keen to keep this function as an extension of Collection, rather than Array
Strictly speaking, the startIndex of a given Collection isn't necessary 0, and the progression of indexes isn't necessarily an increment of 1 each time.
Martin R's approach of using a custom iterator definitely seemed like the right approach, so I used that along with the WWDC 2018's implementation of "EveryOther" to create a standard sequential iterator that simply goes along two elements in each while loop block, till it hits the end.
extension Collection {
func deinterleave() -> ([Element], [Element]) {
let smallHalf = count / 2
let bigHalf = count - smallHalf
var left = [Element]()
var right = [Element]()
left .reserveCapacity(bigHalf)
right.reserveCapacity(smallHalf)
let start = self.startIndex
let end = self.endIndex
var iter = start
while iter != end {
left.append(self[iter])
iter = index(after: iter)
if iter == end { break }
right.append(self[iter])
iter = index(after: iter)
}
return (left, right)
}
}
let a = [1,2,3,4,5,6,7]
print(a.deinterleave()) // ([1, 3, 5, 7], [2, 4, 6])
Without a doubt, there is some scope to improve this further and use vacawama's LazyMapSequence implementation, but for now this does everything I need it to.
Is there a counterpart in Swift to flatten in Scala, Xtend, Groovy, Ruby and co?
var aofa = [[1,2,3],[4],[5,6,7,8,9]]
aofa.flatten() // shall deliver [1,2,3,4,5,6,7,8,9]
of course i could use reduce for that but that kinda sucks
var flattened = aofa.reduce(Int[]()){
a,i in var b : Int[] = a
b.extend(i)
return b
}
Swift >= 3.0
reduce:
let numbers = [[1,2,3],[4],[5,6,7,8,9]]
let reduced = numbers.reduce([], +)
flatMap:
let numbers = [[1,2,3],[4],[5,6,7,8,9]]
let flattened = numbers.flatMap { $0 }
joined:
let numbers = [[1,2,3],[4],[5,6,7,8,9]]
let joined = Array(numbers.joined())
In Swift standard library there is joined function implemented for all types conforming to Sequence protocol (or flatten on SequenceType before Swift 3), which includes Array:
let numbers = [[1,2,3],[4],[5,6,7,8,9]]
let flattened = Array(numbers.joined())
In certain cases use of joined() can be beneficial as it returns a lazy collection instead of a new array, but can always be converted to an array when passed to Array() initialiser like in the example above.
Swift 4.x/5.x
Just to add a bit more complexity in the array, if there is an array that contains array of arrays, then flatMap will actually fail.
Suppose the array is
var array:[Any] = [1,2,[[3,4],[5,6,[7]]],8]
What flatMap or compactMap returns is:
array.compactMap({$0})
//Output
[1, 2, [[3, 4], [5, 6, [7]]], 8]
In order to solve this problem, we can use our simple for loop logic + recursion
func flattenedArray(array:[Any]) -> [Int] {
var myArray = [Int]()
for element in array {
if let element = element as? Int {
myArray.append(element)
}
if let element = element as? [Any] {
let result = flattenedArray(array: element)
for i in result {
myArray.append(i)
}
}
}
return myArray
}
So call this function with the given array
flattenedArray(array: array)
The Result is:
[1, 2, 3, 4, 5, 6, 7, 8]
This function will help to flatten any kind of array, considering the case of Int here
Playground Output:
Swift 4.x
This usage of flatMap isn't deprecated and it's make for this.
https://developer.apple.com/documentation/swift/sequence/2905332-flatmap
var aofa = [[1,2,3],[4],[5,6,7,8,9]]
aofa.flatMap { $0 } //[1,2,3,4,5,6,7,8,9]
Edit: Use joined() instead:
https://developer.apple.com/documentation/swift/sequence/2431985-joined
Original reply:
let numbers = [[1, 2, 3], [4, 5, 6]]
let flattenNumbers = numbers.reduce([], combine: +)
Swift 5.1
public extension Array where Element: Collection {
func flatten() -> [Element.Element] {
return reduce([], +)
}
}
In case you also want it for Dictionary values:
public extension Dictionary.Values where Value : Collection {
func flatten() -> [Value.Element]{
return self.reduce([], +)
}
}
Swift 4.2
I wrote a simple array extension below. You can use to flatten an array that contains another array or element. unlike joined() method.
public extension Array {
public func flatten() -> [Element] {
return Array.flatten(0, self)
}
public static func flatten<Element>(_ index: Int, _ toFlat: [Element]) -> [Element] {
guard index < toFlat.count else { return [] }
var flatten: [Element] = []
if let itemArr = toFlat[index] as? [Element] {
flatten = flatten + itemArr.flatten()
} else {
flatten.append(toFlat[index])
}
return flatten + Array.flatten(index + 1, toFlat)
}
}
usage:
let numbers: [Any] = [1, [2, "3"], 4, ["5", 6, 7], "8", [9, 10]]
numbers.flatten()
Apple Swift version 5.1.2 (swiftlang-1100.0.278 clang-1100.0.33.9)
Target: x86_64-apple-darwin19.2.0
let optionalNumbers = [[1, 2, 3, nil], nil, [4], [5, 6, 7, 8, 9]]
print(optionalNumbers.compactMap { $0 }) // [[Optional(1), Optional(2), Optional(3), nil], [Optional(4)], [Optional(5), Optional(6), Optional(7), Optional(8), Optional(9)]]
print(optionalNumbers.compactMap { $0 }.reduce([], +).map { $0 as? Int ?? nil }.compactMap{ $0 }) // [1, 2, 3, 4, 5, 6, 7, 8, 9]
print(optionalNumbers.compactMap { $0 }.flatMap { $0 }.map { $0 as? Int ?? nil }.compactMap{ $0 }) // [1, 2, 3, 4, 5, 6, 7, 8, 9]
print(Array(optionalNumbers.compactMap { $0 }.joined()).map { $0 as? Int ?? nil }.compactMap{ $0 }) // [1, 2, 3, 4, 5, 6, 7, 8, 9]
let nonOptionalNumbers = [[1, 2, 3], [4], [5, 6, 7, 8, 9]]
print(nonOptionalNumbers.compactMap { $0 }) // [[1, 2, 3], [4], [5, 6, 7, 8, 9]]
print(nonOptionalNumbers.reduce([], +)) // [1, 2, 3, 4, 5, 6, 7, 8, 9]
print(nonOptionalNumbers.flatMap { $0 }) // [1, 2, 3, 4, 5, 6, 7, 8, 9]
print(Array(nonOptionalNumbers.joined())) // [1, 2, 3, 4, 5, 6, 7, 8, 9]
You can flatten nested array using the following method:
var arrays = [1, 2, 3, 4, 5, [12, 22, 32], [[1, 2, 3], 1, 3, 4, [[[777, 888, 8999]]]]] as [Any]
func flatten(_ array: [Any]) -> [Any] {
return array.reduce([Any]()) { result, current in
switch current {
case(let arrayOfAny as [Any]):
return result + flatten(arrayOfAny)
default:
return result + [current]
}
}
}
let result = flatten(arrays)
print(result)
/// [1, 2, 3, 4, 5, 12, 22, 32, 1, 2, 3, 1, 3, 4, 777, 888, 8999]
Modified #RahmiBozdag's answer,
1. Methods in public extensions are public.
2. Removed extra method, as start index will be always zero.
3. I did not find a way to put compactMap inside for nil and optionals because inside method T is always [Any?], any suggestions are welcomed.
let array = [[[1, 2, 3], 4], 5, [6, [9], 10], 11, nil] as [Any?]
public extension Array {
func flatten<T>(_ index: Int = 0) -> [T] {
guard index < self.count else {
return []
}
var flatten: [T] = []
if let itemArr = self[index] as? [T] {
flatten += itemArr.flatten()
} else if let element = self[index] as? T {
flatten.append(element)
}
return flatten + self.flatten(index + 1)
}
}
let result: [Any] = array.flatten().compactMap { $0 }
print(result)
//[1, 2, 3, 4, 5, 6, 9, 10, 11]
flatten() was renamed to joined() in Swift 3 per SE-0133:
https://github.com/apple/swift-evolution/blob/master/proposals/0133-rename-flatten-to-joined.md
Another more generic implementation of reduce,
let numbers = [[1,2,3],[4],[5,6,7,8,9]]
let reduced = reduce(numbers,[],+)
This accomplishes the same thing but may give more insight into what is going on in reduce.
From Apple's docs,
func reduce<S : SequenceType, U>(sequence: S, initial: U, combine: (U, S.Generator.Element) -> U) -> U
Description
Return the result of repeatedly calling combine with an accumulated value initialized to initial and each element of sequence, in turn.
struct Group {
var members: [String]?
}
let groups = [Group]()
let outputMembers: [String] = Array(groups.compactMap({ $0.members }).joined())
Description
If you want to make single array of the array of object model. Ex: we get outputMembers single array from all groups.
matrix is [[myDTO]]?
In swift 5 you can use this = Array(self.matrix!.joined())
func convert(){
let arr = [[1,2,3],[4],[5,6,7,8,9]]
print("Old Arr = ",arr)
var newArr = [Int]()
for i in arr{
for j in i{
newArr.append(j)
}
}
print("New Arr = ",newArr)
}