How do you use the map function in Swift? - swift

I'm trying to understand how .map works and all of my searches are returning examples that aren't helping me resolve my issue. From my understanding .map is a cleaner way of performing a for-in loop, the only difference is that .map replaces your original creates a new array and for-in just alters your original arrays values.
Attempt
In the code example below, I'm expecting the .map function to replace my wheels array with new Tire objects whose state properties are all set to .Flat.
This is a playground-ready version of what I'm trying to accomplish in my program:
enum State {
case Ok, Flat
}
class Tire {
var state = State.Ok
init(t: State) {
state = t
}
}
var wheels = [Tire(t: .Ok), Tire(t: .Ok), Tire(t: .Ok)]
wheels = wheels.map { $0.state = .Flat }
Result
error: cannot convert value of type '()' to closure result type 'Tire'
wheels = wheels.map { $0.state = .Flat }
~~~~~~~~~^~~~~~~
Question
In the given situation, how can I set all of my Tire objects states to .Flat using .map?

There are 2 similar key functions which perform similar operations, the basic purpose of which is to take an array and build another array from it:
func map(transform:(R)->T) -> [T] --- Map takes an array of elements of one type and converts it to an array of elements of (potentially) another type, by calling a transform function on each element in turn. So you can convert an array of Int's to an array of strings:
[1, 2, 3, 4].map { "\($0)" } // --> ["1", "2", "3", "4"]
func filter(predicate:(T)->Boolean) -> [T] -- Filter takes an array of elements and converts it to an array of elements of the same type, but only includes those elements for which predicate returns true. So you can filter an array of ints to leave only the even numbers:
[1, 2, 3, 4].filter { $0 % 2 == 0 } // --> [ 2, 4]
There are other variants, such as flatMap which takes [[T]] and turns it into [T] by iterating over the input and array and appending the contents of each array to an output array:
[ [1, 2], [3, 4]].flatMap() // --> [1, 2, 3, 4]
It's also worth nothing that the concept behind map is that, in simplistic terms, it can be used to map any input type to an output type, so you can define:
func <R, T> map(in:R?, transform:(R)->T) -> T?
for example, which would translate any optional input type into an optional output type given a function that translates the base type.

The problem is $0.state = .Flat is an assignment. It does not return a value. Try this:
wheels = wheels.map { w in
w.state = .Flat
return w
}
map does not replace anything. It projects each element from your array to a new array by applying the transformation block. You can choose to assign this new array to the old array, but otherwise it will not alter the original array.

Related

Is it possible to make array reference immutable, but array content mutable?

In Java, we can make an array reference immutable, and array content mutable, by using final keyword
Java
final int[] array = {1, 2, 3};
// Ok. Array content mutable.
array[0] = 9;
// Compiler error. Array reference immutable.
array = new int[]{4, 5, 6};
In Swift, they take one step further. Using let keyword, will make both array reference, and array content immutable.
Swift
let array = [1, 2, 3]
// Compiler error. Array content immutable.
array[0] = 9
// Compiler error. Array reference immutable.
array = [4, 5, 6]
In Swift, is it possible to make array reference immutable, but array content mutable?
The answer for your question is "yes" and "no", depends on what you have.
If you decide to declare a simple "let" constant, you can't modify it.
Why ? Because it prevents you to side effects (and you have some optimization).
For example if you just want to browse a list and print values, you don't modify the list.
myArray = [1,2,3]
for element in myArray {
print(element)
}
Why it can be cool ? Now if you know that you don't want to modify your list, it prevents you to use functions that can modify your list. It will save your time and avoid some behavior that you don't expect.
If you declare a var and you don't modify the value, Swift will tell you too.
Moreover, the concept of immutable in Swift is interesting if you use a struct or a class.
Imagine you have this structure and this class:
struct TestStruct {
var myInt: Int
init(myInt: Int) {
self.myInt = myInt
}
}
struct TestClass {
var myInt: Int
init(myInt: Int) {
self.myInt = myInt
}
}
In this structure you have myIntwhich is a var. What happens if you try to declare a TestStructure and a TestClass object with a let constant ?
let testStruct = Test(myInt: 3)
// Cannot assign to property: 'test' is a 'let' constant
test.myInt = 5
let testClass = Test(myInt: 3)
// It works
test.myInt = 5
In a struct, the let is propagated for every field, which is not the case for a class.
Using let keyword, will make both array reference, and array content immutable.
This isn't correct. There is no "array reference" here. An array is a value, just like an integer is a value. There is no "array reference." Variables can be let or var, but that doesn't change the nature of their value. You wouldn't say that var n = 4 made "4" mutable. Similarly, var ns = [1,2,3] doesn't make [1,2,3] mutable. It just means you can change what ns refers to. Calling ns.append(5) is just like n += 1. In each case they assign a new value. They don't mutate the old value.
As an implementation and optimization detail, it is possible that the underlying array storage that was used for ns will be mutated and used for the new ns value. But this is invisible to the caller. For example:
var array = [1,2] {
didSet { print("\(oldValue) -> \(array)") }
}
array.append(1)
array = [1,2,1]
// [1, 2] -> [1, 2, 1]
// [1, 2, 1] -> [1, 2, 1]
There's no deep difference between the append and the assignment. They are both assignments. And notice that setting the value to the same value is still just an assignment.
I'm harping on this because you can't just translate over a Java approach and have it work if your Java code relies on shared mutable state (where one part of the program modifies an array and others are supposed to have their reference update). But if your Java works that way, I recommend improving your Java to reduce its reliance on that. As long as you generally just pass values and return values, then it'll work exactly the same in Swift as in Java.
If you still need this kind of mutable array, then you can build one fairly easily by wrapping an Array in a class:
final class ArrayRef<Element>: MutableCollection, ExpressibleByArrayLiteral {
private var elements: [Element] = []
init(arrayLiteral elements: Element...) {
self.elements = elements
}
var startIndex: Int { elements.startIndex }
var endIndex: Int { elements.endIndex }
func index(after i: Int) -> Int { elements.index(after: i) }
subscript(position: Int) -> Element {
get { elements[position] }
set { elements[position] = newValue }
}
}
let array: ArrayRef = [1, 2, 3]
// Ok. "Array" content mutable.
array[0] = 9
// Compiler error. "Array" is immutable.
array = [4, 5, 6]
(This is a very simple and unoptimized implementation. With more work you can make it more efficient and improve the interface.)
But I don't particularly recommend this unless you really need it. There's a reason it doesn't exist in stdlib.

map high order function format in swift

I am wondering why map format has to be {( )} rather than just { }
func intersect(_ nums1: [Int], _ nums2: [Int]) -> [Int] {
// the following is right
var num1Reduce = nums1.reduce(0){ $0 + $ 1}
/// the following is wrong ??
var num2Dict = Dictionary(nums2.map{ $0, 1 }, uniquingKeysWith : +)
// the following is right
var num1Dict = Dictionary(nums1.map{ ($0, 1) }, uniquingKeysWith : +)
}
and I even see the following format ({ }). I am totally confused!
let cars = peopleArray.map({ $0.cars })
print(cars)
You are using the following Dictionary initializer:
init<S>(_ keysAndValues: S, uniquingKeysWith combine: (Dictionary<Key, Value>.Value, Dictionary<Key, Value>.Value) throws -> Dictionary<Key, Value>.Value) rethrows where S : Sequence, S.Element == (Key, Value)
Note that S is a sequence where its elements are a tuple of key/value pairs.
When you pass nums1.map{ ($0, 1) } to the first parameter, you are creating an array of key/value tuples from nums1.
It fails when you use nums2.map{ $0, 1 } because that is missing the parentheses for the tuple.
Keep in mind that nums1.map{ ($0, 1) } is shorthand for nums1.map({ ($0, 1) }). That's all related to trailing closures which has nothing to do with the parentheses for the tuple that appear inside the { }.
A map is a function that takes a closure as a parameter. We can call the map and pass the parameter like we do for any other ordinary function call without removing the brackets ()e.g
(0...100).map ({ _ in print("yeti")})
But swift allows us to remove the brackets as a way of shorthanding and we can write it like, hence eliminating the ()
(0...100).map { _ in print("yeti")}
But incase you want to access individual values of the array elements, you can do so in two ways,
Given an array, you can access it's individual element using $0, which basically says, Hey map, give me the first element at this current index.
(0...100).map {$0}
Instead of using the default swift indexing, you decide to define the value you are accessing by giving it a readable variable name e.g
(0...100).map {element in}
This gets $0 and assigns it to element, the in keyword basically tells the compiler that hey, $0 is now element and we are going to use it after in. Otherwise if you remove the in keyword, the compiler says it doesn't know any variable called element.
For special collections like dictionaries, they have two values per index, i.e the key and value, therefore if you want to access the contents of a dictionary during the mapping, you can do it in two ways like above, a). use the default swift indexes, or give the values per index, readable variable names. e.g
let dictionary = ["a": 3, "b": 4, "c": 5]
dictionary.map{($0, $1)}
We use inner brackets () to let the compiler know that the collection we are mapping over has two values per index. Please note the inner parenthesis are creating a tuple
dictionary.map {(key, value) in }

Why does Swift think that my array is a function?

// Complete the miniMaxSum function below.
func birthdayCakeCandles(arr: [Int]) -> Int {
let a = arr.sorted
let highest = a().last!
return 0
}
birthdayCakeCandles(arr: [1, 7, 3, 12, 5])
I tried to create a sorted array "a" from an unsorted array "arr", to solve a problem from HackerRank. But compiler gives following error:
main.swift:5:19: error: function 'a' was used as a property; add () to
call it
let highest = a.last!
^
()
Why does it think, that "a" is a function?
error: function 'a' was used as a property; add () to call it
Swift doesn't think that your array is a function. Xcode just tells you, that sorted is a method and you have to call it with () at the end
let a = arr.sorted()
let a = arr.sorted
Why does it think, that "a" is a function?
Because that is just what it is. sorted is the name of a function. So a is now the sorted function.
What you want is for a to be the result of calling a function. But every function call involves parentheses, e.g. sorted(). You have no parentheses.
In short that line of your code talks about sorted. But it never calls sorted. It just renames it.
arr.sorted evaluates to a method of [Int]. If you want a sorted copy of the array call that method:
func birthdayCakeCandles(arr: [Int]) -> Int {
let a = arr.sorted()
let highest = a.last!
return 0
}
Otherwise, the compiler is right: a contains a function: The sorted method.
I believe what you want to do is the following:
func birthdayCakeCandles(arr: [Int]) -> Int {
return arr.max() ?? 0
}
In your code, a is not an array, it is a function with the following signature () -> [Int], because arr.sorted is an instance method. You can check the type of a variable by Alt + clicking on the variable in Xcode. To execute this function you need to invoke it using (). Which you are doing in the following line. The output of a() is an array of integers, on which you are calling the last property.
If your arr is empty, .last! will cause an error/app crash, since you'd be unwrapping nil (there is no last element in an empty array). This is why in the suggested code above, I've used the nil-coalescing operator ??, which would guarantee that the function would return 0 if the array arr is empty.
For more in-depth reading on instance methods, have a look here.

Swift - insert new array at the beginning of another array [duplicate]

Suppose I have an array, for example:
var myArray = ["Steve", "Bill", "Linus", "Bret"]
And later I want to push/append an element to the end of said array, to get:
["Steve", "Bill", "Linus", "Bret", "Tim"]
What method should I use?
And what about the case where I want to add an element to the front of the array? Is there a constant time unshift?
As of Swift 3 / 4 / 5, this is done as follows.
To add a new element to the end of an Array.
anArray.append("This String")
To append a different Array to the end of your Array.
anArray += ["Moar", "Strings"]
anArray.append(contentsOf: ["Moar", "Strings"])
To insert a new element into your Array.
anArray.insert("This String", at: 0)
To insert the contents of a different Array into your Array.
anArray.insert(contentsOf: ["Moar", "Strings"], at: 0)
More information can be found in the "Collection Types" chapter of "The Swift Programming Language", starting on page 110.
You can also pass in a variable and/or object if you wanted to.
var str1:String = "John"
var str2:String = "Bob"
var myArray = ["Steve", "Bill", "Linus", "Bret"]
//add to the end of the array with append
myArray.append(str1)
myArray.append(str2)
To add them to the front:
//use 'insert' instead of append
myArray.insert(str1, atIndex:0)
myArray.insert(str2, atIndex:0)
//Swift 3
myArray.insert(str1, at: 0)
myArray.insert(str2, at: 0)
As others have already stated, you can no longer use '+=' as of xCode 6.1
To add to the end, use the += operator:
myArray += ["Craig"]
myArray += ["Jony", "Eddy"]
That operator is generally equivalent to the append(contentsOf:) method. (And in really old Swift versions, could append single elements, not just other collections of the same element type.)
There's also insert(_:at:) for inserting at any index.
If, say, you'd like a convenience function for inserting at the beginning, you could add it to the Array class with an extension.
Use += and + operators :
extension Array {
}
func += <V> (inout left: [V], right: V) {
left.append(right)
}
func + <V>(left: Array<V>, right: V) -> Array<V>
{
var map = Array<V>()
for (v) in left {
map.append(v)
}
map.append(right)
return map
}
then use :
var list = [AnyObject]()
list += "hello"
list += ["hello", "world!"]
var list2 = list + "anything"
Here is a small extension if you wish to insert at the beginning of the array without loosing the item at the first position
extension Array{
mutating func appendAtBeginning(newItem : Element){
let copy = self
self = []
self.append(newItem)
self.appendContentsOf(copy)
}
}
In Swift 4.1 and Xcode 9.4.1
We can add objects to Array basically in Two ways
let stringOne = "One"
let strigTwo = "Two"
let stringThree = "Three"
var array:[String] = []//If your array is string type
Type 1)
//To append elements at the end
array.append(stringOne)
array.append(stringThree)
Type 2)
//To add elements at specific index
array.insert(strigTwo, at: 1)
If you want to add two arrays
var array1 = [1,2,3,4,5]
let array2 = [6,7,8,9]
let array3 = array1+array2
print(array3)
array1.append(contentsOf: array2)
print(array1)
Use Deque instead of Array
The main benefit of Deque over Array is that it supports efficient insertions and removals at both ends.
https://swift.org/blog/swift-collections/
var names:Deque = ["Steve", "Bill", "Linus", "Bret"]
Add 'Tim' at the end of names
names.append("Tim")
Add 'Tim' at the begining of names
names.prepend("John")
Remove the first element of names
names.popFirst() // "John"
Remove the last element of names
names.popLast() // "Tim"
From page 143 of The Swift Programming Language:
You can add a new item to the end of an array by calling the array’s append method
Alternatively, add a new item to the end of an array with the addition assignment operator (+=)
Excerpt From: Apple Inc. “The Swift Programming Language.” iBooks. https://itun.es/us/jEUH0.l
To add to the solutions suggesting append, it's useful to know that this is an amortised constant time operation in many cases:
Complexity: Amortized O(1) unless self's storage is shared with another live array; O(count) if self does not wrap a bridged NSArray; otherwise the efficiency is unspecified.
I'm looking for a cons like operator for Swift. It should return a new immutable array with the element tacked on the end, in constant time, without changing the original array. I've not yet found a standard function that does this. I'll try to remember to report back if I find one!
You could use
Myarray.insert("Data #\(index)", atIndex: index)
If you want to append unique object, you can expand Array struct
extension Array where Element: Equatable {
mutating func appendUniqueObject(object: Generator.Element) {
if contains(object) == false {
append(object)
}
}
}
If the array is NSArray you can use the adding function to add any object at the end of the array, like this:
Swift 4.2
var myArray: NSArray = []
let firstElement: String = "First element"
let secondElement: String = "Second element"
// Process to add the elements to the array
myArray.adding(firstElement)
myArray.adding(secondElement)
Result:
print(myArray)
// ["First element", "Second element"]
That is a very simple way, regards!
In Swift 4.2:
You can use
myArray.append("Tim") //To add "Tim" into array
or
myArray.insert("Tim", at: 0) //Change 0 with specific location
Example: students = ["Ben" , "Ivy" , "Jordell"]
1) To add single elements to the end of an array, use the append(_:)
students.append(\ "Maxime" )
2) Add multiple elements at the same time by passing another array or a sequence of any kind to the append(contentsOf:) method
students.append(contentsOf: ["Shakia" , "William"])
3) To add new elements in the middle of an array by using the insert(_:at:) method for single elements
students.insert("Liam" , at:2 )
4) Using insert(contentsOf:at:) to insert multiple elements from another collection or array literal
students.insert(['Tim','TIM' at: 2 )
Swift 5.3, I believe.
The normal array wasvar myArray = ["Steve", "Bill", "Linus", "Bret"]
and you want to add "Tim" to the array, then you can use myArray.insert("Tim", at=*index*)so if you want to add it at the back of the array, then you can use myArray.append("Tim", at: 3)

Optional vs Bound value assigning var from array

I want to check if there is a value in a array and if so assign to a String using a if-left statement:
if let scoreValue = scoreValueArray[element!]{
// do something with scoreValue
}
Error: Bound value in a conditional binding must be of optional type
So tried changing the ! to ? but error persists.
Any input appreciated.
scoreValueArray is an array of strings, where a String value is appended to array if a condition is met, then array is saved to NSUserdefaults.
So element is a int which corresponds to a index in the array, bt only if the index is occupied with a String, so
scoreValueArray[element!]
could return an 'Index out of bounds', hence want to use the if-let.
Although the accepted answer clearly puts why optional binding is not available in the current implementation, it doesn't provide with a solution.
As it is shown in this answer, protocols provide an elegant way of safely checking the bounds of an array. Here's the Swift 2.0 version:
extension Array {
subscript (safe index: Int) -> Element? {
return indices ~= index ? self[index] : nil
}
}
Which you can use like this:
let fruits = ["Apple", "Banana", "Cherry"]
if let fruit = fruits[safe: 4] {
// Do something with the fruit
}
It's not clear what type your scoreValueArray is, but for the sake of this answer, I'm going to assume it's an array of Int.
var scoreValueArray: Array<Int>
Now, if we look the definition of the Array struct, we'll find this:
struct Array<T> : MutableCollectionType, Sliceable {
// other stuff...
subscript (index: Int) -> T
// more stuff
}
So, calling the subscript method on our array (which is what we do when we say scoreValueArray) returns a non-optional. And non-optionals cannot be used in the conditional binding if let/if var statements.
We can duplicate this error message in a more simple example:
let foo: Int = 3
if let bar = foo {
// same error
}
This produces the same error. If we instead do something more like the following, we can avoid the error:
let foo: Int? = 3
if let bar = foo {
// perfectly valid
}
This is different from a dictionary, whose subscript method does return an optional (T?). A dictionary will return a value if the key passed in the subscript is found or nil if there is no value for the passed key.
We must avoid array-index-out-of-bounds exceptions in the same way we always do... by checking the array's length:
if element < scoreValueArray.count {
scoreValue = scoreValueArray[element]
}