Skip to content

Instantly share code, notes, and snippets.

@misbell
Created December 3, 2016 04:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save misbell/97ffc4aaa9c81f7e53ede3c28c95051e to your computer and use it in GitHub Desktop.
Save misbell/97ffc4aaa9c81f7e53ede3c28c95051e to your computer and use it in GitHub Desktop.
The Map , Filter, Reduce code chapter from Fatih Nayebi, adapted for Swift 3.0.1 from Swift 2
// This code runs in a playground in Swift 3.0.1
import Cocoa
var str = "Hello, playground"
/*
MPI
dec 2 2016
Functor
Applicative Functor
Monad
Map
Flatmap and flatten
filter
Reduce
apply
join
chaining higher order functions
zip
practical examples
*/
// what is 'the category theory'
//what is a morphism
// what is a functor
// a functor is any type that implements the map function
// e.g. Dictionary, Array, Optiona, Closure
// a functor is something where we can call the Map function
// over it and transform it
// a functor is a functional design pattern
/// =====================
// applicative functor
// if you have an optional functor you can't apply the map function on the Optional type without unwrapping it first
// the apply function, you need to apply the Functor before you can use map on the Functor
//========================================
// Monad
// a type of Functor
// =========================
// so let's look at Maps
let numbers = [10, 30, 91, 50, 100, 39, 74]
var formattedNumbers: [String] = []
for number in numbers {
let formattedNumber = "\(number)$"
formattedNumbers.append(formattedNumber)
}
let mappedNumbers = numbers.map { "\($0)$" }
print (mappedNumbers)
// two things, I didn't know you could use an interpolated value and assign it to a string, not just use it in a print statement
// I probably SHOULD have known that but I didn't
// internal iteration vs external iteration
// map is internal iteration
// for loops are external iteration
print (formattedNumbers)
func calculate<T>(a: T,
b: T,
funcA: (T,T) -> T,
funcB: (T) -> T) -> T {
return funcA(funcB(a), funcB(b))
}
func calculate2<T, U>(a: T, funcA: (T) -> U) -> U {
return funcA(a)
}
func calculate2ArraysToo<T,U>(a: [T], funcA: ([T]) -> [U]) -> [U] {
return funcA(a)
}
// leading to a half baked map
func map_a<T,U>(a: [T], transform: ([T]) -> [U]) -> [U] {
return transform(a)
}
func map_b<ElementInput, ElementResult>(elements: [ElementInput], transform: (ElementInput) -> ElementResult) -> [ElementResult] {
var result: [ElementResult] = []
for element in elements {
result.append(transform(element))
}
return result
}
let twoDimensionalArray = [[1,2,3], [4,5,6]]
var onedimarray: [Int]
// so flat map is the order of operations on the array, NOT a kind of thing
// first you do flat, then you do map
var oneDimensionalArray = twoDimensionalArray.flatMap { $0 }
// first turn it into a single array, then run the transform
let transformedOneDArray = twoDimensionalArray.joined().map {$0 + 2}
print (oneDimensionalArray)
print (transformedOneDArray)
var xxx = twoDimensionalArray.flatMap({ $0 } )
// filter
// implement the filter function
func filtera<Element> (elements: [Element],
predicate: ((Element) -> Bool)) -> [Element] {
var result = [Element]()
for element in elements {
if predicate(element) {
result.append(element)
}
}
return result
}
let filteredArray = filtera(elements: numbers) { $0 % 2 == 0 }
print (filteredArray)
// reduce
// reduces a list into a single value
// also called a fold or aggregate
// takes a starting value and a function
// it returns a value created by combining (adding?) elements in a list
// filter and map return the same type
// reduce returns a different type
let total = numbers.reduce(0) {$0 + $1}
print(total)
let total2 = numbers.reduce(0, +)
print(total2)
func reducea<Element, Value> (elements: [Element], initial: Value, combine: (Value, Element) -> Value) -> Value {
var result = initial
for element in elements {
result = combine(result, element)
}
return result
}
// reduction is so powerful that every other function that traverses a list can be specified in terms of it.
func mapInTermsOfReduce<Element, ElementResult>( elements: [Element],
transform: (Element) -> ElementResult) -> [ElementResult] {
// note reduce is now function on the array
return elements.reduce([ElementResult]()) {
$0 + [transform( $1 )] // transform all elements in array
}
}
let resultm = mapInTermsOfReduce(elements: numbers) {
$0 + 2
}
//
//// I need to get this bit with $0 and $1 right
//// $0 is ElementResult [transform ( $1 is element )
//// you can always inspect them
//// an array of transforms means a list of ElementResults because THAT'S WHAT IT RETURNS
//// ok I get it
//
func filterIntermsOfReduce<Element>(elements: [Element], predicate: (Element) -> Bool) -> [Element] {
return elements.reduce( [] ) {
predicate($1) ? $0 + [ $1 ] : $0 // test predicate for true or false, including new element or not
}
}
let resultf = filterIntermsOfReduce(elements: numbers) {
$0 % 2 == 0
}
func flatMapIntermsOfReduce<Element>(elements: [Element],
transform: (Element) -> Element? ) -> [Element] {
return elements.reduce( []) {
guard let transformationResult = transform($1) else {
return $0
}
return $0 + [transformationResult]
}
}
let anArrayOfNumbers = [1, 3, 5]
print(anArrayOfNumbers)
let ooneDimArray = flatMapIntermsOfReduce(elements: anArrayOfNumbers)
{ $0 + 5 }
print (ooneDimArray)
func flattenIntermsOfReduce<Element>(elements: [[Element]]) -> [Element] {
return elements.reduce([]) {
$0 + $1
}
}
let flattened = flattenIntermsOfReduce(elements: [[1,2,3], [4,5,6]])
func apply<T, V>(fn: ([T]) -> V, args: [T]) -> V {
return fn(args)
}
func incrementValues(a: [Int]) -> [Int] {
return a.map { $0 + 1 }
}
let applied = apply(fn: incrementValues, args: numbers)
func join<Element: Equatable> (elements: [Element], separator: String) -> String {
return elements.reduce("") {
initial, element
in
let aSeparator = (element == elements.last) ? "" : separator
return "\(initial)\(element)\(aSeparator)"
}
}
let items = ["First", "Second", "Third"]
let commaSeparatedItems = join(elements: items, separator: ", ")
struct User {
let name: String
let age: Int
}
let users = [
User(name: "Fehiman", age: 60),
User(name: "Negar", age: 30),
User(name: "Milo", age: 1),
User(name: "Tamina", age: 6),
User(name: "Neco", age: 30)
]
// extract into an array and then add them up (combine them)
let totalAge = users.map { $0.age }.reduce(0) { $0 + $1 }
print (totalAge)
// did it change
let alphabeticalNumbers = ["Three", "Five", "Nine", "Ten"]
let zipped = zip(alphabeticalNumbers, numbers).map {$0}
print(zipped)
// sum of an array
let listofnum = [1,2,3,4,5,6,7,8,9,10]
let sumofnum = listofnum.reduce(0, +)
// product of an array
let productofnum = listofnum.reduce(1, *)
print (productofnum)
// remove nil values from an array
// not sure I know why this works
let optionalArray: [String?] = ["First", "Second", nil, "Fourth"]
let nonOptionalArray = optionalArray.flatMap { $0 }
print (nonOptionalArray)
// remove dupes from an array
let arrayWithDuplicates = [1,1,2,3,3,4,4,5,6,7]
let remdupes = arrayWithDuplicates.reduce([]) { (a: [Int], b: Int) -> [Int]
in
if a.contains(b) {
return a
} else {
return a + [b]
}
}
print (arrayWithDuplicates)
print (remdupes)
// partitioning an array
typealias Accumlator = (lPartition: [Int], rPartition: [Int])
func partition(list: [Int], criteria: (Int) -> Bool) -> Accumlator {
return list.reduce((lPartition: [Int](), rPartition: [Int]())) {
(accumlator: Accumlator, pivot: Int) -> Accumlator in
if criteria(pivot) {
return (lPartition: accumlator.lPartition + [pivot],
rPartition: accumlator.rPartition)
} else {
return (rPartition: accumlator.rPartition + [pivot ],
lPartition: accumlator.lPartition)
}
}
}
// here's how you can make it generic
func genericPartition<T>(list: [T],
criteria: (T) -> Bool) -> (lPartition:[T], rPartition: [T]) {
return list.reduce((lPartition: [T](), rPartition: [T]())) {
(accumlator: (lPartition: [T], rPartition: [T]), pivot: T) -> (
lPartition: [T], rPartition: [T]) in
if criteria(pivot) {
return (lPartition: accumlator.lPartition + [pivot],
rPartition: accumlator.rPartition)
} else {
return (rPartition: accumlator.rPartition + [pivot],
lPartition: accumlator.lPartition)
} }
}
let doublesToPartition = [3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
print(genericPartition(list:doublesToPartition) {
$0.truncatingRemainder(dividingBy: 2.0) == 0})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment