While preparing for a technical coding interview, and practising LeetCode questions, I created this cheatsheet.
As you know, coding interview requires interviewee to manipulate collection types (array, dictionary, set) expertly to solve mathematic puzzles.
Loop array
for value in array
for index in array.indices
for (index, value) in array.enumerated()
Loop array with range
for i in 0..<array.count // Traditional loop with indices
for v in array[2...] // Skip the first 2 elements
for v in array[..<(array.count-2)] // Skip the last 2 elements
for v in array.prefix(3) // The first 3
for v in array.suffix(3) // The last 3
Loop dictionary
for (key, value) in dictionary
Check if dictionary has key
dictionary.keys.contains("k")
// Alternatively, if the value type is not an `Optional`
if dictionary["k"] == nil // "k" is not in keys
Dictionary with Optional Value
Beware if you have a dictionary such as [String: Int?]
. If you set a key to nil using dictionary["k"] = nil
, what it does is to remove the key “k”.
The designed way to set is dictionary.updateValue(nil, forKey: "k")
😱 The key “k” will exist.
Special sequences
let tenOnes = repeatElement(1, count: 10) // Array(tenOnes)
stride(from: 0, to: 60, by: 5) // Every 5 min
Array CRUD
// Find
firstIndex(of: x) // O(n)
// Insert
append(x) // O(1)
append(contentsOf: anArray)
insert(x, at: i) // O(n)
It is important to note that append
O(1) is much faster than insert
.
// Remove
remove(at: i) // O(n)
removeAll(where: {..})
dropFirst(x)
let last = popLast() // O(1)
// Move
move(fromOffsets:toOffset:)
Mutate
sorted()
reversed()
shuffled()
swapAt()
There are more ops using your own predicate: max(by:)
, sorted(by:)
, filter()
, map()
Partition
partition(by:)
is a powerful API, and quite advanced. It reorders the sequence by a predicate, such that the right half satisfy the predicate (while left half does not).
It returns the index of the first index in the right half.
let p = array.partition { .. }
let h1 = list[..<p] // Left half
let h2 = list[p...] // Right half
Note that it is an unstable partition, which means the order in both half are not preserved. There is an internal halfStablePartition
which will have the 1st half retain the original order, and also a stablePartition
which will have both half retain the original order.
If don’t know Crusty, read this up.
Comparable, Equatable
To compare and sort, the type must implement the Comparable
protocol.
extension MyClass: Comparable {
static func < (lhs: MyClass, MyClass: Date) -> Bool {
return lhs.foo < rhs.foo
}
}
Comparable
inherits Equatable
protocol, so you might need to implement it too. In cases like struct where Equatable is provided by default, you need not implement it.
extension MyClass: Equatable {
public static func == (lhs: MyClass, rhs: MyClass) -> Bool {
return lhs.foo == rhs.foo
}
}
Identity Comparison ===
There is an op ===
for identity comparison eg. are they the same instances?
// For example, in the above Equatable implementation, we could also compare their identities
return lhs === rhs
Dictionary O(1) access
An optimizing trick is to make use of the quick O(1) operation when accessing a dictionary, since they are hashed.
The trade off is that the setup takes O(n), and a space of O(n). And you need the type to be hashable.
Hashable
extension MyClass: Hashable {
public func hash(into hasher: inout Hasher) {
hasher.combine(ObjectIdentifier(self).hashValue)
}
}