The Array is probably the most widely used data structure in programming. The array is a versatile data structure that used to solve a large number of problems. It is easy to learn and work with an array however, most programmers donβt use all of Arrayβs features.
It is also important to understand how Swift array works under the hood to determine if an array is the best data structure for a given task. Knowledge of how the different array methods and properties are implemented also helps avoid common pitfalls.
Not surprisingly a lot of interview questions revolve around the array.
This post includes some of the most frequent interview questions.
Q. How can you safely fetch the first element a Swift Array?
Swift Array provides the
first
property to safely fetch the first element of the array if it exists.The
first
property returns an optional value. The return value is nil if the array is empty.Avoid using the array[0], this will throw an exception and crash at runtime if the array is empty.
let fruits = ["π","π₯","π","π₯","π"]
fruits.first //π
// Avoid this
fruits[0] //π
let emptyFruits = [String]()
emptyFruits[0] // errorβοΈ
βββββ ββ ββ β βββββ
Q. How can you safely fetch the last element of a Swift Array?
Use the array
last
property. The last property returns an optional. The return value isnil
if the array is empty.
let fruits = ["π","π₯","π","π₯","π"]
fruits.last //π
βββββ ββ ββ β βββββ
Q. How can you safely fetch the first n element of a Swift Array?
Use Swift Arrayβs
prefix(_:)
method to extract the first βnβ elements.The prefix method safeguards against fetching elements that are beyond the elements in the array.
prefix(_:)
returns all the elements of the array if n exceeds the number of elements in the array.
let fruits = ["π","π₯","π","π₯","π"]
fruits.prefix(3)
// ["π", "π₯", "π"]
// Avoid this. Potential for index out of bounds error.
fruits[0...2]
// ["π", "π₯", "π"]
fruits.prefix(10)
//["π","π₯","π","π₯","π"]
fruits[0...10]
// errorβοΈ - Array has only 4 valid indices.
Note: The prefix method returns an ArraySlice
and not a new array. i.e it does not allocate any new storage.
An ArraySlice
can be thought of as a view of the existing array.
func prefix(_ maxLength: Int) -> ArraySlice<Element>
Another point to note is that maxLength has to be positive. Passing a negative value will result in an error.
func prefix(_ maxLength: Int) -> ArraySlice<Element>
// maxLength cannot be negative
arr.prefix(-1) // errorβοΈ
Array also has the suffix(:_)
method to retrive the last n elements.
βββββ ββ ββ β βββββ
Q. How do you check if an Swift Array is empty?
Swift Array has a property named
isEmpty
that can be used to check if the array has no elements.The
isEmpty
property returns a boolean value which istrue
if the array is empty.
let emptyArr = [String]()
emptyArr.isEmpty // true
emptyArr.count == 0 // true
let nonEmptyArr = ["foo","bar"]
nonEmptyArr.isEmpty // false
The isEmpty
property is not limited to Array. It is available on all types that conform to the Collection protocol
Some commonly used types that conform to the Collection protocol are Array, Set, Dictionary, String (conforms BidirectionalCollection)
Performance :
The isEmpty
property has a complexity of O(1) for collections.
Using isEmpty vs count == 0
In other languages for eg: JavaScript, we check if the length of an array is 0 to determine if it is empty.
For most commonly used collections such as Array, Set, Dictionary both isEmpty and count == 0 is O(1) time (constant). However, it is not true for all Collection types.
For eg: The default view of a Swift string is a collection of characters. In the case of String, the time complexity of count
is O(n).
i.e the time taken to compute the count depends on the number of characters. To get the count we need to traverse the entire string.
Using count == 0 is an inefficient way to check if a String
is empty.
To keep things consistent. It is advisable to use isEmpty for any type of collection.
βββββ ββ ββ β βββββ
Q. How can you reverse the contents of an array?
The
reverse
method reverses the order of the elements in place. i.e The source array will be mutated. Another methodreversed()
returns a view of the original array where the elements are in the reverse order. The reversed method does not allocate new storage.
var sampleArr1 = ["πΆ","π±","π"]
sampleArr1.reverse()
//output : ["π", "π±", "πΆ"]
Here the sampleArr1 itself is mutated. Furthermore, because the array is mutated, we have to declare it as a var
let sampleArr2 = ["πΆ","π±","π"]
sampleArr2.reversed()
//output : ReversedCollection<Array<String>>
The reversed method simply wraps the original array in a ReversedCollection
. It does not reverse the array as yet.
The array we started with is unchanged.
ReversedCollection is simply an entity that promises to behave as if the array is reversed.
In the code below if we can confirm the order is reversed by looking at the first element.
sampleArr2.reversed().first
// π
Performance :
The reverse()
method has a time complexity of O(n). i.e the time it takes depends directly on the number of elements in the array.
The reversed()
method has a complexity of O(1). Since the reversed method does not move the elements of the array, it performs better.
βββββ ββ ββ β βββββ
Q. How do you check if an array of strings contains a given string?
Swift Array type defines a method called
contains
that can be used to check if an Array of strings contains a given string.
let students = ["Alice", "Bob", "Chad"]
students.contains("Bob") // true
students.contains("Dan") // false
students.contains("BOB") // false (compare is case-sensitive)
How contains works
βββββ ββ ββ β βββββ
Q. How do you check if an array of custom elements contains a given element?
Swift Array type defines the method called
contains(where : )
that can be used to check if the element that matches a predicate.
For eg: Consider an Array of Users. Where User
is a type we have defined(struct)
struct User {
var firstName : String
}
let userA = User(firstName: "Alice")
let userB = User(firstName: "Bob")
let userC = User(firstName: "Chad")
let users = [userA,userB,userC]
let userX = User(firstName: "Bob")
//users.contains(userX)
// Error : Referencing instance method 'contains' on 'Sequence' requires that 'User' conform to 'Equatable'
let userX = User(firstName: "Bob")
users.contains { (userX) -> Bool in
return userX.firstName == "Bob"
}
βββββ ββ ββ β βββββ