Comparing array of force unwrapped optionals - swift

I'm writing a test:
func test_arrayFromShufflingArray() {
var videos = [MockObjects.mockVMVideo_1(), MockObjects.mockVMVideo_2(), MockObjects.mockVMVideo_3()]
let tuple = ShuffleHelper.arrayFromShufflingArray(videos, currentIndex:1)
var shuffledVideos = tuple.0
let shuffleIndexMap = tuple.1
// -- test order is different
XCTAssert(videos != shuffledVideos, "test_arrayFromShufflingArray fail")
}
But on the last line I get the last line:
Binary operator '!=' cannot be applied to two '[VMVideo!]' operands

Arrays can be compared with == if the element type is Equatable:
/// Returns true if these arrays contain the same elements.
public func ==<Element : Equatable>(lhs: [Element], rhs: [Element]) -> Bool
But neither ImplicitlyUnwrappedOptional<Wrapped> nor Optional<Wrapped> conform to Equatable, even if the
underlying type Wrapped does.
Possible options are (assuming that VMVideo conforms to Equatable):
Change your code so that videos and shuffledVideos are
[VMVideo] arrays instead of [VMVideo!].
Compare the arrays elementwise:
XCTAssert(videos.count == shuffledVideos.count
&& !zip(videos, shuffledVideos).contains {$0 != $1 })
Define a == operator for arrays of implicitly unwrapped equatable
elements:
func ==<Element : Equatable> (lhs: [Element!], rhs: [Element!]) -> Bool {
return lhs.count == rhs.count && !zip(lhs, rhs).contains {$0 != $1 }
}

Swift can't tell how to compare two arrays to see if their contents are identical unless it knows how to compare individual elements. So you need to implement the == function on your class and adopt Equatable:
extension VMVideo: Equatable {
// nothing goes here, == function has to be at global scope
}
func ==(lhs: VMVideo, rhs: VMVideo) -> Bool {
// Up to you to determine what equality means for your object, e.g.:
return lhs.essentialProperty1 == rhs.essentialProperty1 &&
lhs.essentialProperty2 == rhs.essentialProperty2
}
EDIT To clarify how it interacts with NSObject and to troubleshoot your environment, please confirm the following:
class UnderstandsEqual: NSObject {}
let ok1: [UnderstandsEqual] = [UnderstandsEqual(), UnderstandsEqual()]
let ok2: [UnderstandsEqual] = [UnderstandsEqual(), UnderstandsEqual()]
ok1 == ok2 // no problem, evaluates to true
class DoesntUnderstand {}
let bad1: [DoesntUnderstand] = [DoesntUnderstand(), DoesntUnderstand()]
let bad2: [DoesntUnderstand] = [DoesntUnderstand(), DoesntUnderstand()]
bad1 == bad2 // produces a compile-time error

Martin R's answer was the better answer, but for this specific purpose I just converted to NSArray's and was able to use the == operator in Swift:
func test_arrayFromShufflingArray() {
let videos = [MockObjects.mockVMVideo_1(), MockObjects.mockVMVideo_2(), MockObjects.mockVMVideo_3()]
let videosNSArray: NSArray = videos.map { $0 }
let tuple = ShuffleHelper.arrayFromShufflingArray(videos, currentIndex:1)
let shuffledVideos = tuple.0
let shuffledVideosNSArray: NSArray = shuffledVideos.map { $0 }
// -- test order is different
XCTAssert(videosNSArray != shuffledVideosNSArray, "test_arrayFromShufflingArray fail")
// -- test elements are the same
let set = NSSet(array: videos)
let shuffledSet = NSSet(array: shuffledVideos)
XCTAssert(set == shuffledSet, "test_arrayFromShufflingArray fail")
}

Related

Swift testing non-scalar types

I want to test my function that takes a string, a returns all the pairs of characters as an array s.t.
func pairsOfChars(_ s: String) -> [(Character,Character)] {
let strArray = Array(s)
var outputArray = [(Character,Character)]()
for i in 0..<strArray.count - 1 {
for j in i + 1..<strArray.count {
outputArray.append( (strArray[i], strArray[j]) )
}
}
return outputArray
}
So I want to create a suite of tests using XCTestCase. I usually use XCTestCase and XCTAssertEqual but these are only appropriate for C scalar types. This means that the following test case returns an error:
class pairsTests: XCTestCase {
func testNaive() {
measure {
XCTAssertEqual( pairsOfChars("abc") , [(Character("a"),Character("b")),(Character("a"),Character("c")),(Character("b"),Character("c")) ] )
}
}
}
I could convert to a string, but I'm thinking there is a better solution.
How can I test an output of an array of pairs of characters [(Character,Character)]
Your notion of a nonscalar is a total red herring. The problem is one of equatability.
How can I test an output of an array of pairs of characters [(Character,Character)]
You can't, because there is no default notion of what it would mean to equate two such arrays. This is the old "tuples of Equatable are not Equatable" problem (https://bugs.swift.org/browse/SR-1222) which still rears its head with arrays. The == operator works on tuples by a kind of magic, but they are still not formally Equatable.
You could define equatability of arrays of character pairs yourself:
typealias CharPair = (Character,Character)
func ==(lhs:[CharPair], rhs:[CharPair]) -> Bool {
if lhs.count != rhs.count {
return false
}
let zipped = zip(lhs,rhs)
return zipped.allSatisfy{$0 == $1}
}
Alternatively, have your pairsOfChars return something that is more easily made equatable, such as an array of a struct for which Equatable is defined.
For example:
struct CharacterPair : Equatable {
let c1:Character
let c2:Character
// in Swift 4.2 this next bit is not needed
static func ==(lhs:CharacterPair, rhs:CharacterPair) -> Bool {
return lhs.c1 == rhs.c1 && lhs.c2 == rhs.c2
}
}
func pairsOfChars(_ s: String) -> [CharacterPair] {
let strArray = Array(s)
var outputArray = [CharacterPair]()
for i in 0..<strArray.count - 1 {
for j in i + 1..<strArray.count {
outputArray.append(CharacterPair(c1:strArray[i],c2:strArray[j]))
}
}
return outputArray
}
You would then rewrite the test to match:
XCTAssertEqual(
pairsOfChars("abc"),
[CharacterPair(c1:Character("a"),c2:Character("b")),
CharacterPair(c1:Character("a"),c2:Character("c")),
CharacterPair(c1:Character("b"),c2:Character("c"))]
)

Index and Iterate over CollectionType in swift

I have code which is basically like this:
func arrayHalvesEqual(data:[UInt8]) -> Bool {
let midPoint = data.count / 2
for i in 0..<midPoint {
let b = data[i]
let b2 = data[i + midPoint]
if b != b2 {
return false
}
}
return true
}
This works fine, but sometimes I want to pass in Arrays, and other times ArraySlice. I thought I'd change it to use generics and the CollectionType protocol, which converts as follows:
func arrayHalvesEqual<ByteArray : CollectionType where ByteArray.Generator.Element == UInt8>(data:ByteArray) -> Bool {
let midPoint = data.count / 2
for i in 0..<midPoint {
let b = data[i]
let b2 = data[i + midPoint]
if b != b2 {
return false
}
}
return true
}
However, I get the following compiler error:
error: binary operator '..<' cannot be applied to operands of type 'Int' and 'ByteArray.Index.Distance'
for i in 0..<midPoint {
I can switch the for loop to for i in data.indices which makes that compile, but then I can no longer divide it by 2 to get the midPoint, as data.indices returns the abstract CollectionType.Index whereas / 2 is an Int.
Is it possible to do something like this in Swift? Can I bridge between an abstract protocol Index type and some real type I can do maths on?
P.S: I've seen and found other examples for iterating over the whole collection by using indices and enumerate, but I explicitly only want to iterate over half the collection which requires some sort of division by 2
Thanks
You can restrict the method to collections which are indexed
by Int:
func arrayHalvesEqual<ByteArray : CollectionType where ByteArray.Index == Int, ByteArray.Generator.Element == UInt8>
(data:ByteArray) -> Bool { ... }
This covers both Array and ArraySlice.
And if you use indices.startIndex instead of 0 as initial index
then it suffices to restrict the index type to IntegerType.
Also the data type UInt8 can be replaced by a generic Equatable,
and the entire method shortened to
func arrayHalvesEqual<ByteArray : CollectionType where ByteArray.Index : IntegerType, ByteArray.SubSequence.Generator.Element : Equatable>
(data:ByteArray) -> Bool {
let midPoint = (data.indices.endIndex - data.indices.startIndex)/2
let firstHalf = data[data.indices.startIndex ..< midPoint]
let secondHalf = data[midPoint ..< data.indices.endIndex]
return !zip(firstHalf, secondHalf).contains { $0 != $1 }
}

How to compare nested collections in swift

I have two collections:
let collection1:[String:[String:NSObject]] = ["somekey":["nestedkey":"value"]]
let collection2:[String:[String:NSObject]] = ["somekey":["nestedkey":"value"]]
//I would like to compare them using the following:
let collectionsAreEqual = collection1 == collection2
Copying and pasting the above code into a playground gives the following error:
I know I can write an equal function for this:
infix func == (this:[String:[String:NSObject]], that:[String:[String:NSObject]]){
//return true or false
}
In objective c, isEqual: on an NSDictionary handles this no problem, because it does the nested comparison for you. Is there some method of generally handling this in swift?
Update
I can use the following:
//:[String:[String:NSObject]]
let collection1:[String:NSObject] = ["somekey":["nestedkey":"value"]]
let collection2:[String:NSObject] = ["somekey":["nestedkey":"value"]]
//I would like to compare them using the following:
let collectionsAreEqual = collection1 == collection2
but it requires using NSObject as the value in the declaration. Is there a pure swift method to handle this?
Here's an equality operator that will compare any two nested dictionaries with the same type:
func ==<T: Equatable, K1: Hashable, K2: Hashable>(lhs: [K1: [K2: T]], rhs: [K1: [K2: T]]) -> Bool {
if lhs.count != rhs.count { return false }
for (key, lhsub) in lhs {
if let rhsub = rhs[key] {
if lhsub != rhsub {
return false
}
} else {
return false
}
}
return true
}
try this:
let collection1:[String:NSObject] = ["somekey":["nestedkey":"value"]]
let collection2:[String:NSObject] = ["somekey":["nestedkey":"value"]]
let collectionsAreEqual = ((collection1 as NSDictionary).isEqual(collection2 as NSDictionary)
By casting the swift objects to Foundation objects they get the equality operator. They will call the equality operator on every element recursively, so there you have your deep comparison.

Swift Array - Check if an index exists

In Swift, is there any way to check if an index exists in an array without a fatal error being thrown?
I was hoping I could do something like this:
let arr: [String] = ["foo", "bar"]
let str: String? = arr[1]
if let str2 = arr[2] as String? {
// this wouldn't run
println(str2)
} else {
// this would be run
}
But I get
fatal error: Array index out of range
An elegant way in Swift:
let isIndexValid = array.indices.contains(index)
Type extension:
extension Collection {
subscript(optional i: Index) -> Iterator.Element? {
return self.indices.contains(i) ? self[i] : nil
}
}
Using this you get an optional value back when adding the keyword optional to your index which means your program doesn't crash even if the index is out of range. In your example:
let arr = ["foo", "bar"]
let str1 = arr[optional: 1] // --> str1 is now Optional("bar")
if let str2 = arr[optional: 2] {
print(str2) // --> this still wouldn't run
} else {
print("No string found at that index") // --> this would be printed
}
Just check if the index is less than the array size:
if 2 < arr.count {
...
} else {
...
}
Add some extension sugar:
extension Collection {
subscript(safe index: Index) -> Iterator.Element? {
guard indices.contains(index) else { return nil }
return self[index]
}
}
if let item = ["a", "b", "c", "d"][safe: 3] { print(item) } // Output: "d"
// or with guard:
guard let anotherItem = ["a", "b", "c", "d"][safe: 3] else { return }
print(anotherItem) // "d"
Enhances readability when doing if let style coding in conjunction with arrays
the best way.
let reqIndex = array.indices.contains(index)
print(reqIndex)
Swift 4 extension:
For me i prefer like method.
// MARK: - Extension Collection
extension Collection {
/// Get at index object
///
/// - Parameter index: Index of object
/// - Returns: Element at index or nil
func get(at index: Index) -> Iterator.Element? {
return self.indices.contains(index) ? self[index] : nil
}
}
Thanks to #Benno Kress
You can rewrite this in a safer way to check the size of the array, and use a ternary conditional:
if let str2 = (arr.count > 2 ? arr[2] : nil) as String?
Asserting if an array index exist:
This methodology is great if you don't want to add extension sugar:
let arr = [1,2,3]
if let fourthItem = (3 < arr.count ? arr[3] : nil ) {
Swift.print("fourthItem: \(fourthItem)")
}else if let thirdItem = (2 < arr.count ? arr[2] : nil) {
Swift.print("thirdItem: \(thirdItem)")
}
//Output: thirdItem: 3
extension Array {
func isValidIndex(_ index : Int) -> Bool {
return index < self.count
}
}
let array = ["a","b","c","d"]
func testArrayIndex(_ index : Int) {
guard array.isValidIndex(index) else {
print("Handle array index Out of bounds here")
return
}
}
It's work for me to handle indexOutOfBounds.
Swift 4 and 5 extension:
As for me I think this is the safest solution:
public extension MutableCollection {
subscript(safe index: Index) -> Element? {
get {
return indices.contains(index) ? self[index] : nil
}
set(newValue) {
if let newValue = newValue, indices.contains(index) {
self[index] = newValue
}
}
}
}
Example:
let array = ["foo", "bar"]
if let str = array[safe: 1] {
print(str) // "bar"
} else {
print("index out of range")
}
I believe the existing answers could be improved further because this function could be needed in multiple places within a codebase (code smell when repeating common operations). So thought of adding my own implementation, with reasoning for why I considered this approach (efficiency is an important part of good API design, and should be preferred where possible as long as readability is not too badly affected). In addition to enforcing good Object-Oriented design with a method on the type itself, I think Protocol Extensions are great and we can make the existing answers even more Swifty. Limiting the extension is great because you don’t create code you don’t use. Making the code cleaner and extensible can often make maintenance easier, but there are trade-offs (succinctness being the one I thought of first).
So, you can note that if you'd ONLY like to use the extension idea for reusability but prefer the contains method referenced above you can rework this answer. I have tried to make this answer flexible for different uses.
TL;DR
You can use a more efficient algorithm (Space and Time) and make it extensible using a protocol extension with generic constraints:
extension Collection where Element: Numeric { // Constrain only to numerical collections i.e Int, CGFloat, Double and NSNumber
func isIndexValid(index: Index) -> Bool {
return self.endIndex > index && self.startIndex <= index
}
}
// Usage
let checkOne = digits.isIndexValid(index: index)
let checkTwo = [1,2,3].isIndexValid(index: 2)
Deep Dive
Efficiency
#Manuel's answer is indeed very elegant but it uses an additional layer of indirection (see here). The indices property acts like a CountableRange<Int> under the hood created from the startIndex and endIndex without reason for this problem (marginally higher Space Complexity, especially if the String is long). That being said, the Time Complexity should be around the same as a direct comparison between the endIndex and startIndex properties because N = 2 even though contains(_:) is O(N) for Collections (Ranges only have two properties for the start and end indices).
For the best Space and Time Complexity, more extensibility and only marginally longer code, I would recommend using the following:
extension Collection {
func isIndexValid(index: Index) -> Bool {
return self.endIndex > index && self.startIndex <= index
}
}
Note here how I've used startIndex instead of 0 - this is to support ArraySlices and other SubSequence types. This was another motivation to post a solution.
Example usage:
let check = digits.isIndexValid(index: index)
For Collections in general, it's pretty hard to create an invalid Index by design in Swift because Apple has restricted the initializers for associatedtype Index on Collection - ones can only be created from an existing valid Collection.Index (like startIndex).
That being said, it's common to use raw Int indices for Arrays because there are many instances when you need to check random Array indices. So you may want to limit the method to fewer structs...
Limit Method Scope
You will notice that this solution works across all Collection types (extensibility), but you can restrict this to Arrays only if you want to limit the scope for your particular app (for example, if you don't want the added String method because you don't need it).
extension Array {
func isIndexValid(index: Index) -> Bool {
return self.endIndex > index && self.startIndex <= index
}
}
For Arrays, you don't need to use an Index type explicitly:
let check = [1,2,3].isIndexValid(index: 2)
Feel free to adapt the code here for your own use cases, there are many types of other Collections e.g. LazyCollections. You can also use generic constraints, for example:
extension Collection where Element: Numeric {
func isIndexValid(index: Index) -> Bool {
return self.endIndex > index && self.startIndex <= index
}
}
This limits the scope to Numeric Collections, but you can use String explicitly as well conversely. Again it's better to limit the function to what you specifically use to avoid code creep.
Referencing the method across different modules
The compiler already applies multiple optimizations to prevent generics from being a problem in general, but these don't apply when the code is being called from a separate module. For cases like that, using #inlinable can give you interesting performance boosts at the cost of an increased framework binary size. In general, if you're really into improving performance and want to encapsulate the function in a separate Xcode target for good SOC, you can try:
extension Collection where Element: Numeric {
// Add this signature to the public header of the extensions module as well.
#inlinable public func isIndexValid(index: Index) -> Bool {
return self.endIndex > index && self.startIndex <= index
}
}
I can recommend trying out a modular codebase structure, I think it helps to ensure Single Responsibility (and SOLID) in projects for common operations. We can try following the steps here and that is where we can use this optimisation (sparingly though). It's OK to use the attribute for this function because the compiler operation only adds one extra line of code per call site but it can improve performance further since a method is not added to the call stack (so doesn’t need to be tracked). This is useful if you need bleeding-edge speed, and you don’t mind small binary size increases. (-: Or maybe try out the new XCFrameworks (but be careful with the ObjC runtime compatibility for < iOS 13).
I think we should add this extension to every project in Swift
extension Collection {
#inlinable func isValid(position: Self.Index) -> Bool {
return (startIndex..<endIndex) ~= position
}
#inlinable func isValid(bounds: Range<Self.Index>) -> Bool {
return (startIndex..<endIndex) ~= bounds.upperBound
}
#inlinable subscript(safe position: Self.Index) -> Self.Element? {
guard isValid(position: position) else { return nil }
return self[position]
}
#inlinable subscript(safe bounds: Range<Self.Index>) -> Self.SubSequence? {
guard isValid(bounds: bounds) else { return nil }
return self[bounds]
}
}
extension MutableCollection {
#inlinable subscript(safe position: Self.Index) -> Self.Element? {
get {
guard isValid(position: position) else { return nil }
return self[position]
}
set {
guard isValid(position: position), let newValue = newValue else { return }
self[position] = newValue
}
}
#inlinable subscript(safe bounds: Range<Self.Index>) -> Self.SubSequence? {
get {
guard isValid(bounds: bounds) else { return nil }
return self[bounds]
}
set {
guard isValid(bounds: bounds), let newValue = newValue else { return }
self[bounds] = newValue
}
}
}
note that my isValid(position:) and isValid(bounds:) functions is of a complexity O(1), unlike most of the answers below, which uses the contains(_:) method which is of a complexity O(n)
Example usage:
let arr = ["a","b"]
print(arr[safe: 2] ?? "nil") // output: nil
print(arr[safe: 1..<2] ?? "nil") // output: nil
var arr2 = ["a", "b"]
arr2[safe: 2] = "c"
print(arr2[safe: 2] ?? "nil") // output: nil
arr2[safe: 1..<2] = ["c","d"]
print(arr[safe: 1..<2] ?? "nil") // output: nil

How to write any(s : SequenceType) in Swift

Swift doesn't seem to have functions for any() or all() so I am trying to make my own. The closest I have gotten is
func any<S: SequenceType
where S.Generator.Element: BooleanType>
(sequence: S) -> Bool{
var __g = sequence.generate()
var first_element = __g.next()
if first_element == nil{
return false
}
return reduce(sequence, first_element!, {$0 || $1})
}
Edit: As per comments, this function should really look like this
func any<S: SequenceType
where S.Generator.Element: BooleanType>
(sequence: S) -> Bool{
return reduce(sequence, false, {$0 || $1})
}
The compiler tells me
'S.Generator.Element' is not convertible to 'Bool'
It seems to me I am explicitly telling the compiler that it is on the second line. What am I missing?
Your problem is that next returns an optional. That means reduce is expecting to return an optional and $0 is an optional. You would have to unwrap the optional to be able to use the || operator. You also don't know the the type is a Bool, just that it is a BooleanType. You would have to also convert it to an actual Bool by calling boolValue on it:
func any<S: SequenceType
where S.Generator.Element: BooleanType>
(sequence: S) -> Bool{
var __g = sequence.generate()
var first_element = __g.next()
if first_element == nil{
return false
}
return reduce(sequence, first_element!.boolValue, {$0 || $1})
}
However, in this case, I think simplicity is best:
func any<S: SequenceType
where S.Generator.Element: BooleanType>
(sequence: S) -> Bool{
for element in sequence {
if element.boolValue {
return true
}
}
return false
}
This is actually more efficient too because it returns as soon as it finds the first true instead of always going through the whole thing.