Swift

4.3 Sets

Sets

  • unordered collection of distinct instances (all the same type)
  • no repeated values
  • only stores individual elements, not key-value pairs

Declaration

if let is used → set is immutable (cannot change/add to it)

// declaration - create/init with 0 value
var set1 = Set()

// create and initialize with initial values
var set2: Set = ["apples","oranges", "straw"]

Add & Remove Items

// add items
set1.insert("apples")

// remove items
set2.remove("oranges")

// remove all items
set3 = []
  • when removing, if an item isn’t present, then nil is returned

Iterating Over Items

// -> apples, straw
for fruit in set2 {
   print("\(fruit)")
}

Counting

// count set objects -> 2
set2.count

// count instances in a set - requires NSCountedSet
var NSSet1 = NSCountedSet()
NSSet1 = ["apples","oranges", "straw"]
NSSet1.add("apples")

// -> 3
NSSet1.count
// -> 2
NSSet1.count(for: "apples")
  • count → always return the size of the set
  • NSCountedSet shows adds/deletions
  • Apples → still appears just once in the set

Fundamental Set Operations

// referencing / contains -> true
let hasItem = set2.contains("apples")

// combining sets (dups are removed) -> apples, straw
set3 = set1.union(set2)

// returning duplicates (intersect) -> apples
let set4 = set1.intersection(set2)

// subtract out values -> straw
let set5 = set2.subtracting(set1)

// show only differences -> straw
let set6 = set1.symmetricDifference(set2)

Membership & Equality Operations

// are all items unique btwn 2 sets? (t/f) -> false
let isUnique = set1.isDisjoint(with: set2)

// are all 1st set values contained in the 2nd? (t/f) -> true
let isContained = set1.isSubset(of: set2)

// are all 2nd set values contained in the 1st? (t/f) -> false
let isSuper = set1.isSuperset(of: set2)
Posted by Raz in Swift

4.2 Dictionaries

Dictionaries

  • use when you need to reference stored items with unique ID keys
  • no guarantee of order
  • keys and values are all fixed data types (types cannot change)
  • all values returned are optionals
  • can use all optional commands with dictionary items  (ie. ? ! if let)

Declaration

  • if let is used → dictionary is immutable (cannot change/add to it)
// declaration - create but don't initialize
var dict1: [String: Int]

// declaration - create/init with 0 value
var dict2: [String: Int] = [:]
var dict3 = [String: Int]()

// create and initialize with initial values
var dict4 = ["AZ":2, "AL":1]

Add & Change Items

// direct add -> NY, 2
dict3["NY"] = 5

// value changed -> AL, 3
dict4["AL"] = 3

// will insert or change (and return the old value)
dict4.updateValue(7, forKey: "MT")

Remove Items

// -> returns 2
dict4.removeValue(forKey: "AZ")

// will remove the pair -> no return
dict4["AL"] = nil

// entire dictionary cleared
dict4 = [:]

Iterating Over Items

// print keys and values -> key: MT, value: 7
for (key, value) in dict4 {
   print("key: \(key), value:\(value)")
}

// print keys -> key: MT
for key in dict4.keys {
   print("key: \(key)")
}

// print values -> values: 7
for value in dict4.values {
   print("values: \(value)")
}
  • key value → auto declared & type-inferred constants used to iterate so the item evaluated is not altered
  • no guaranteed order for dictionary loops

Miscellaneous

// referencing (no dot notation / unordered) -> optional 7
let state = dict4["MT"]

// count the # of items in the dictionary -> 1
dict4.count

// returns a Bool (true = empty)
dict4.isEmpty

Dictionaries pass by value

var dictA = [1: 1, 2: 2, 3: 3]
var dictB = dictA
dictB[3] = nil

// -> 1: 1, 2: 2, 3: 3
dictA
// -> 1: 1, 2: 2
dictB
Posted by Raz in Swift

4.1.5 Adv Array Manipulation

Common Member Closures

  • filter → returns array elements that pass a certain condition
  • map → returns array elements transformed by a function
  • reduce → returns the sum of array elements plus an initial value
  • reverse → in-line reverses the array order
  • sort → in-line sort of values w/in the array
  • sorted → returns a copy of a sorted array of values of a known type

Counting Frequencies in an Array

let items = ["a", "b", "a", "c"]
let mappedItems = items.map { ($0, 1) }
let counts = Dictionary(mappedItems, uniquingKeysWith: +)
  • use when you want to count how often each item appears
  • mappedItems →  creates an array of key-value pairs using tuples, where each value is the number 1
  • counts → dictionary from that tuple array that adds the 1s together

Find the Difference Between Two Arrays

  • using an Extension
extension Array where Element: Hashable {
   func difference(from other: [Element]) -> [Element] {
      let thisSet = Set(self)
      let otherSet = Set(other)
      return Array(thisSet.symmetricDifference(otherSet))
   }
}
let names1 = ["John", "Paul", "Ringo"]
let names2 = ["Ringo", "Paul", "George"]
let differenceInArrays = names1.difference(from: names2)
  • sets have symmetricDifference function
  • convert the arrays to Sets, then convert the result back
Posted by Raz in Swift

4.1 Arrays

Arrays

  • use to organize a list of variables, constants, and other types
  • all items must be of the same type (Ints, Floats, Strings, objects, optionals)
  • items are indexed by integers starting with zero
  • if copied → Pass by Value (original array is unchanged)

Performance Notes

  • as an array grows in size, its capacity is doubled at certain breakpoints – 2, 4, 8, 16…
  • when capacity is doubled, the array is reallocated in memory which can be an intensive process for a large array
  • use reserveCapacity method to help control memory usage

Declaration

// declaration only, cannot append
var array1: [String]

// declaration - create/init with 0 value
var array2 = [Int]()
var array25: [Int] = []

// create and initialize with an initial value (preferred)
var array3 = ["A", "B", "C"]

// declare and populate with repeated values
var array4 = Array(repeating:"Zebra", count:3)

// creating from a dictionary  (can use values also)
let dict0 = [1: "first", 2: "second"]
var array5 = Array(dict0.keys)
  • if let is used → array is immutable (cannot change/add to it)
  • when creating from a dictionary, the array order is random

Retrieval

// -> Zebra
let animal = array4[2]

// -> A, B, C
print(array3)

Add Items append += insert

  • adds to the end of the list (except insert)
// add 1 item -> 2
array2.append(2)

// add multiple items -> 2, 4, 5
array2.append(contentsOf: [4, 5])

// add a range of Ints -> 2, 4, 5, 1, 2...
array2.append(contentsOf: 1...6)
print(array2)

// shorthand method (+/=)
// adds multiple -> A, B, C, D, E
array3 += ["D", "E"]

// concatenate 2 arrays (index #'s continue after 1st array)
// -> Kiwi, Pears, Kiwi, Pears
array3 += array3

// clears except for item -> Pears
array3 = ["Pears"]

// add at a specific point (insert) -> Kiwi, Pears
array3.insert("Kiwi", at: 0)

Remove Items

// all index #'s shift down -> returns 2
array2.remove(at: 0)

// -> returns 6
array2.removeLast()

// removes all, but array stays initialized
array2.removeAll(keepingCapacity: true)
array2 = []

Change Items

  • reference the item index with [] and assign a new value
// overwrites -> .1 = Peaches
array3[1] = "Peaches"

// adds via concatenation -> .1 = Peaches and cream
array3[1] += " and cream"

// overwrites multiple -> Zebra, Fox, Mice
array4[1...2] = ["Fox", "Mice"]

Iterating over Array Items

// -> Zebra, Bands, Straws
for i in array3 {
   print("i = \(i)")
}

// -> Fruit: Kiwi at: 0...
for index in 0 ..< array3.count {
   print("Fruit: \(array3[index]) at: \(index)")
}

// naming both -> Fruit: Kiwi at: 0...
for (index, fruit) in array3.enumerated() {
   print("Fruit: \(fruit) at: \(index)")
}
  • i index fruit → auto declared type-inferred constants used to iterate so that the item evaluated is not altered
  • enumerated() → returns a tuple composed of – index and item value

Multiple Type Arrays

  • not recommended, but possible
// -> Array.Type
var songs: [Any] = ["Rose", "Bud", 3]
type(of: songs)

Miscellaneous Functions

// count the # of items -> 0
array2.count

// returns a Bool (true = empty) -> true
array2.isEmpty

// shows if the array contains a certain value (true = yes)
array3.contains("Bandanas")

// assigning value to first|last items
// -> Kiwi
let firstFruit = array3.first
// -> Peaches and cream
let lastFruit = array3.last

Arrays pass by value

var arrayA = [1, 2, 3]
var arrayB = arrayA
arrayB[0] = 10

// -> 1, 2, 3
arrayA
// -> 10, 2, 3
arrayB
Posted by Raz in Swift

3.6 Guard Statement

Statement guard

  • used inside an if statement, a loop, or a function
  • code in the else clause runs if the condition is false
  • requires a true condition in order for code to run (like a positive if stmt)
  • optional variables are unwrapped in the scope of the guard
  • must exit via: return – function, continue break – if stmt or loop

Guard in Loop

// in a loop -> 1, 2, 3, -4 isn't positive, 5
let positiveArray = [1, 2, 3, -4, 5]

for pos in positiveArray {
   guard pos >= 0 else {
      print("\(pos) isn't positive")
      continue
   }
   print("\(pos)")
}
  • use break to completely leave the loop

Guard in Function

func greet(person: String?) {
   guard let personName = person else {
      print ("Hey no name")
      return
   }
   print("Hello \(personName)")
}

// -> Hello John
greet(person: "John")
// -> Hey no name
greet(person: nil)

Early Exit in Guard Function

func greetLastName(name: (first: String, last: String?)) -> Int {
   guard name.last != nil else {
      print("Last name required")
      return 0
   }
   return 1
}

// -> Last name required, exit with return 0
greetLastName(name: ("Matt", nil))

Guard stops the Pyramid of Doom

  • POD -> nexted if statements
func sendToServer(name: String, address: String) {
   print("sending stuff")
}

func guardSubmit() {
   guard let name = nameField.text else {
      print("No name to submit")
      return
   }
   guard let address = addressField.text else {
      print("No address to submit")
      return
   }
   sendToServer(name: name, address: address)
}

Pyramid of Doom -> example to avoid

func nonguardSubmit() {
   // pyramid of doom starts here
   if let name = nameField.text {
      if let address = addressField.text {
         sendToServer(name: name, address: address)
      } else {
         print("no address to submit")
      }
   } else {
      print("no name to submit")
   }
}
Posted by Raz in Swift

3.5 Control Transfer

continue

  • stops code execution and returns to the beginning of the loop’s start
// -> 1, 2, 3, 8, 9, 10
for i in 1...10 {
  if (i >= 4 && i <= 7) {
    continue
  }
  print(i)
}

example: Remove Vowels

// -> grtmndsthnklk
let puzzleInput = "great minds think alike"
var puzzleOutput = ""

for char in puzzleInput {
  switch char {
  case "a", "e", "i", "o", "u", " ":
    continue
  default:
    puzzleOutput.append(char)
  }
}
print(puzzleOutput)

break

  • completely interrupts the loop execution or switch
  • jumps to the end of the } brace
// -> 1, 2, 3
for i in 1...10 {
  if (i >= 4) && (i <= 7) {
    break
  }
  print(i)
}
switch puzzleOutput {
default:
  break
}
  • when used with a default switch, it says no action needs to be performed

fallthrough

  • auto evaluates the next case as true even if it isn't
// -> number above
var numb = 6

switch numb {
case 0...6:
  print("number")
  fallthrough
default:													
  print("number above")
}

return

  • ends function execution and returns a specified value (or no value if used alone)
  • can have multiple return stmts (ie. a switch), but once one is executed, the function is exited
  • can return tuples

Labeled Statements

  • possible to label any control stmts (loops, if, switch)
  • then use break continue to navigate
// -> x = 1-5, y = 1
outerLoop: for x in 1...5 {
  innerLoop: for y in 1...3 {
    if y == 2 {
      continue outerLoop
    }
    print("x = \(x), y = \(y)")
  }
}
Posted by Raz in Swift

3.4 Switch Statements

Statement switch

  • can use any data type: Int, String(case sensitive), Tuple…
  • cases must be exhaustive (use default or list all options)
  • cases can use range operators (ie. 0...34...6)
  • no implicit fallthrough → break is understood
// -> Error: 404
var statusCode = 404
var errorString = "Error: "

switch statusCode {
case 100:
  errorString += "Info, 100."
// compound case
case 201, 204:
  errorString += "No content 2xx."
// range case
case 400...417:													
  errorString += "\(statusCode)"
default:
  errorString = "\(statusCode) Unknown."
}
  • statusCode passed into switch print via string interpolation
  • if multiple cases are valid, only 1st evaluated is executed unless fallthrough is used

Tuple Matching switch

// -> That dog is 7
let dogInfo1 = (7, "Fido")

switch dogInfo1 {
// no, due to case sensitivity
case (7, "fido"):
  "fido is 7"
// "_" -> wildcard
case (7, _):
  "That dog is 7"
default:
  "Default dog"
}

Value Bindings switch var let

  • assign a var/con within a case that is only valid at that point
  • bound values are automatically evaluated as true/correct
// -> My dog Maxx is 8
let dogInfo2 = (7, "Max")

switch dogInfo2 {
// 1st tuple value bound to age
case (let age, "max"):          
  "That's Max who is \(age)"
// replaces default|assigns multiple
case var (age, name):            
  age += 1
  name = "Maxx"
  "My dog \(name) is \(age)"
}
  • move var outside parenthesis to bind all values
  • default isn’t necessary if all options are bound

Value Bindings and where clause

  • allows use of a conditional on bound vars/cons
// -> Spot is 7
let dogInfo3 = (7, "Spot")

switch dogInfo3 {
case let (_, name) where name == "spot":
	"Is that spot?"
case let (age, name) where age == 7:
	"\(name) is \(age)"
default:
	"default dog"
}
  • default still needed due to the where clause

if case and implied where clause

  • focuses on a single condition, doesn’t use default
// -> Cool and...
let age = 25

if case 18...35 = age, age >= 21 {
	print("Cool and can drink")
}

// if case converted to a basic if
if (age >= 18) && (age <= 35) && (age >= 21) {
	print("Cool and can drink")
}
Posted by Raz in Swift

3.3 If Statements

Statement if

  • () are optional, but {} are required
  • best practice → use () only with complex stmts
  • avoid 3+ levels of nested if stmts
// -> This is true
if (1 < 3) {
  print("This is true")
}

Statement if else

// -> This is true
if (1 < 3) {
  print("This is true")
} else if (1 < 2) {
  print("Yes, but not reached")
} else {
  print("Not true")
}
Posted by Raz in Swift

3.2 While Loops

Loop while

  • execute the code as long as the condition is true
// -> 1 2 3 (i = 4 at end)
var i = 1
while i <= 3 {
print("i = \(i)")
i += 1
}

Loop repeat while

  • executes the code before evaluating the condition (formerly do-while)
// -> 1 2 3 (j = 4 at end)
var j = 1
repeat {
  print("j = \(j)")
  j += 1
} while j <= 3

Posted by Raz in Swift

3.1 For Loops

Loops for in

  • specify a local variable name and the collection/string to be iterated over
  • must be over a range of values in increasing order
  • swift figures out how to use loops with strings
  • loop constants are only visible within the scope of the loop
  • can use wildcard _ for the inferred constant if you are just looping
// basic -> prints #'s
for i in 1...10 {
print("i = \(i)")
}

// no counter access, just loops -> Hello 5x
for _ in 1...5 {
print("Hello")
}

// -> populates an array with 1-50
var cellContent = [Int]()
for i in 1...50 {
cellContent.append(i)
}

// -> prints each name
let names = ["Jim", "John", "Jill"]
for n in names {
print(n)
}

// -> prints S w i f t
for i: Character in "Swift" {
print(i)
}
  • no need to initialize i (or any other given name) → it is type inferred

Loop for case in where

  • ​iterate the loop over a range and execute code when a condition is met
// -> 3 6 9 12...
for case let i in 1...50 where i % 3 == 0 {
print(i)
}

Loop for in stride

  • looping function that allows any increments / decrements
  • allows a half-open range where termination doesn’t finish exactly on the through value
// -> 11 9 7...
for count in stride(from: 11, to: 1, by: -2) {
print("\(count)")
}

Loop for in reversed

// -> 5 4 3...
for reversedCount in (1...5).reversed() {
print("\(reversedCount)")
}
Posted by Raz in Swift