How to mutate structs in Swift using map? - ios

I have the following struct defined.
struct Person {
var firstName :String
var lastName :String
var active :Bool
}
I have created a collection of Person as shown below:
var persons :[Person] = []
for var i = 1; i<=10; i++ {
var person = Person(firstName: "John \(i)", lastName: "Doe \(i)", active: true)
persons.append(person)
}
and Now I am trying to change the active property to false using the code below:
let inActionPersons = persons.map { (var p) in
p.active = false
return p
}
But I get the following error:
Cannot invoke map with an argument list of type #noescape (Person) throws
Any ideas?
SOLUTION:
Looks like Swift can't infer types sometimes which is kinda lame! Here is the solution:
let a = persons.map { (var p) -> Person in
p.active = false
return p
}
THIS DOES NOT WORK:
let a = persons.map { p in
var p1 = p
p1.active = false
return p1
}

There are exactly two cases where the Swift compiler infers the return
type of a closure automatically:
In a "single-expression closure," i.e. the closure body
consists of a single expression only (with or without explicit
closure parameters).
If the type can be inferred from the calling context.
None of this applies in
let inActionPersons = persons.map { (var p) in
p.active = false
return p
}
or
let a = persons.map { p in
var p1 = p
p1.active = false
return p1
}
and that's why
you have to specify the return type explicitly as in Kametrixom's answer.
Example of a single-expression closure:
let inActionPersons = persons.map { p in
Person(firstName: p.firstName, lastName: p.lastName, active: false)
}
and it would compile with (var p) in or (p : Person) in as well, so this has nothing to do with whether the closure arguments are given
explicitly in parentheses or not.
And here is an example where the type is inferred from the calling
context:
let a : [Person] = persons.map { p in
var p1 = p
p1.active = false
return p1
}
The result of map() must be a [Person] array, so map needs
a closure of type Person -> Person, and the compiler infers
the return type Person automatically.
For more information, see "Inferring Type From Context" and "Implicit Returns from Single-Expression Closures" in the
"Closures" chapter in the Swift book.

When using the brackets for arguments so that var works, you have to put the return type as well:
let inActionPersons = persons.map { (var p) -> Person in
p.active = false
return p
}

Swift 5
The accepted answer no longer works, as of Swift 5, anyway. Closures cannot have keyword arguments anymore which means that each element of the iteration must remain a constant. Therefore, to mutate structures using map, new elements must be initialized within each iteration:
let activePersons = persons.map { (p) -> Person in
return Person(firstName: p.firstName, lastName: p.lastName, active: true)
}

Related

Chaining futures with tuple types

I am frequently needing to pass multiple objects through a series of chained Futures. I have solved the issue with code similar to the below, but it doesn't feel 'right'. Is there a better way to write this?
In particular, the lines beginning return map(to: don't smell right, as I'm taking an actual value a then turning it into a Future conn.future(a) just to be later flatMapped again in the next step.
In the contrived example below, we are creating an A, with a related object B, and optionally a C which is related to B, which is why B must be passed through the chain.
class A {
id: Int?
}
class B {
id: Int?
aId: Int?
}
class C {
id: Int?
bId: Int?
}
/// Creates an A and B, with an optionally attached C. Returns the A.
func create(withC: Bool, on conn: MySQLConnection) throws -> Future<A> {
return try A.create(on: conn)
// Returning two futures
.flatMap(to: (A, B).self) { a in
let futureB = try B(aId: a.requireID()).create(on: conn)
return map(to: (A, B).self, conn.future(a), futureB) { ($0, $1) }
}
// Returning two futures, one optional
.flatMap(to: (A, C?).self) { a, b in
guard withC else {
return map(to: (A, C?).self, conn.future(a), conn.future(nil)) { ($0, $1) }
}
let futureC = try C(bId: b.requireId()).create(on: conn)
return map(to: (A, C?).self, conn.future(a), futureC) { ($0, $1) }
}
// Convert back to the future we care about
.map(to: A.self) { a, _ in return a }
}

Find an item and change value in custom object array - Swift

I have this class
class InboxInterests {
var title = ""
var eventID = 0
var count = ""
var added = 0
init(title : String, eventID : NSInteger, count: String, added : NSInteger) {
self.title = title
self.eventID = eventID
self.count = count
self.added = added
}
}
And i use it like this
var array: [InboxInterests] = [InboxInterests]()
Add item
let post = InboxInterests(title: "test",eventID : 1, count: "test", added: 0)
self.array.append(post)
I want to find the index by eventID key and change the value of added key in the same index
How is that possible?
For me, the above answer did not work. So, what I did was first find the index of the object that I want to replace then using the index replace it with the new value
if let row = self.upcoming.index(where: {$0.eventID == id}) {
array[row] = newValue
}
In Swift 5.0:
if let row = self.upcoming.firstIndex(where: {$0.eventID == id}) {
array[row] = newValue
}
Since you are using a class, use filter and first to find the value:
array.filter({$0.eventID == id}).first?.added = value
In this you:
filter the array down to elements that match the event ID
pick the first result, if any
then set the value
This works since classes are pass by reference. When you edit the return value from array.filter({$0.eventID == id}).first?, you edit the underlying value. You'll need to see the answers below if you are using a struct
EDIT: In Swift 3 you can save yourself a couple of characters
array.first({$0.eventID == id})?.added = value
EDIT: Swift 4.2:
array.first(where: { $0.eventID == id })?.added = value
array.filter {$0.eventID == id}.first?.added = value
The filter operator is not the best in this case, it works for some of you because classes are passed by reference.
Explanation: (You can copy the following code in a playground if you want to verify it).
class Book {
let id: Int
var title = "default"
init (id: Int) {
self.id = id
}
}
var arrayBook = [Book]()
arrayBook.append(Book(id: 0))
arrayBook.append(Book(id:1))
arrayBook.forEach { book in
print(book.title)
}
arrayBook.filter{ $0.id == 1 }.first?.title = "modified"
arrayBook.forEach { book in
print(book.title)
}
Arrays are copied by value not reference, so when you are using filter you are creating a new array (different than the initial), but when you modify the new one, the initial one gets modified too because both are pointing to the same class (classed are passed by reference), so after the filter your array will have changed and the new one gets deallocated. So in this case it will print "default", "default" and then "default, "modified".
What happens if you change class for struct, the value will be passed by value not reference so you will have 2 arrays in memory with different values, so if you go through arrayBooks again it will print before the filter "default","default", and then "default", "default" again. Because when you are using the filter you are creating and modifying a new array that will get deallocated if you do not store it).
The solution is using map, creating a new array with all the values but with the modified items or fields that we want and then replace our array with the new one. This will print "default", "default" before the map, and then "default", "modified"
This will work with structs, classes and everything that you want :).
struct Book {
let id: Int
var title = "default"
init (id: Int) {
self.id = id
}
}
var arrayBook = [Book]()
arrayBook.append(Book(id: 0))
arrayBook.append(Book(id:1))
arrayBook.forEach { book in
print(book.title)
}
arrayBook = arrayBook.map{
var mutableBook = $0
if $0.id == 1 {
mutableBook.title = "modified"
}
return mutableBook
}
arrayBook.forEach { book in
print(book.title)
}
array = array.map { $0.eventID == id ? newValue : $0 }
If you conform your class to Equatable then this would work:
extension Array where Element: Equatable {
#discardableResult
public mutating func replace(_ element: Element, with new: Element) -> Bool {
if let f = self.firstIndex(where: { $0 == element}) {
self[f] = new
return true
}
return false
}
}
Use like this:
array.replace(prev, with: new)

Swift Optional Checking "Unexpectedly found nil"

I'm not sure why Swift is complaining that list1 = list1!.next unexpectedly found nil when my if statement checks if list1 != nil. Could anyone explain why checking if list1 != nil is not enough? I've tried changing it to
if list1 {
list1 = list1!.next
}
but it suggested that I check using != nil
Here's a gist
Here's my code:
class Node {
var data: Int
var next: Node?
init (data: Int) {
self.data = data
}
}
func addReversed (var list1: Node?, var list2: Node?) -> Node {
var carry = 0
var head: Node? = nil
var prev: Node? = nil
while list1 != nil || list2 != nil {
var newNode = addNode(list1, list2, &carry)
appendNode(head, prev, newNode)
if list1 != nil {
list1 = list1!.next
}
if list2 != nil {
list2 = list2!.next
}
}
if carry > 0 {
var carryNode = Node(data: carry)
appendNode(head, prev, carryNode)
}
return head!
}
This is not what you asked, but I think it may have a lot to do with the problem. I tested your Node and your basic addReversed code and I couldn't get a crash. But at that time I didn't have any of your other global functions, so I had to just comment them out while testing. Since I didn't crash, therefore I came to believe that the problem is in one of those global functions.
Then you published your gist, with the code to those global functions, and I can see immediately that this code is deeply flawed:
func appendNode(var head: Node?, var prev: Node?, node: Node) {
if head == nil {
head = node
}
if prev == nil {
prev = node
}
else {
prev!.next = node
prev = node
}
}
You seem to think that saying e.g. prev = node will replace the node that came in as prev with the node that came in as node in some outside world. It will not. These are the names of local variables only. So none of that code has any effect at all - except for prev!.next = node, because there you are mutating an instance that exists also in the outside world (because this is a reference type, a class instance).
If you want to replace the object that came in on a parameter with another object, you need an inout parameter.
For example:
class Dog {
let name:String
init(_ s:String) {
name = s
}
}
func replaceDog(var d:Dog) {
d = Dog("Rover") // this is what you are doing
}
var d = Dog("Fido")
replaceDog(d)
d // still Fido
But:
class Dog {
let name:String
init(_ s:String) {
name = s
}
}
func replaceDog(inout d:Dog) {
d = Dog("Rover")
}
var d = Dog("Fido")
replaceDog(&d)
d // Rover
Your code is full of this same false assumption, and I think, although I have not worked out the details, that this assumption is the cause of your difficulty here. My guess is that you got this linked list implementation from some other language that has pointers, but you are not using pointers in your implementation. The way to use pointers in Swift is with inout!

Find Object with Property in Array

is there a possibility to get an object from an array with an specific property? Or do i need to loop trough all objects in my array and check if an property is the specific i was looking for?
edit: Thanks for given me into the correct direction, but i have a problem to convert this.
// edit again: A ok, and if there is only one specific result? Is this also a possible method do to that?
let imageUUID = sender.imageUUID
let questionImageObjects = self.formImages[currentSelectedQuestion.qIndex] as [Images]!
// this is working
//var imageObject:Images!
/*
for (index, image) in enumerate(questionImageObjects) {
if(image.imageUUID == imageUUID) {
imageObject = image
}
}
*/
// this is not working - NSArray is not a subtype of Images- so what if there is only 1 possible result?
var imageObject = questionImageObjects.filter( { return $0.imageUUID == imageUUID } )
// this is not working - NSArray is not a subtype of Images- so what if there is only 1 possible result?
You have no way to prove at compile-time that there is only one possible result on an array. What you're actually asking for is the first matching result. The easiest (though not the fastest) is to just take the first element of the result of filter:
let imageObject = questionImageObjects.filter{ $0.imageUUID == imageUUID }.first
imageObject will now be an optional of course, since it's possible that nothing matches.
If searching the whole array is time consuming, of course you can easily create a firstMatching function that will return the (optional) first element matching the closure, but for short arrays this is fine and simple.
As charles notes, in Swift 3 this is built in:
questionImageObjects.first(where: { $0.imageUUID == imageUUID })
Edit 2016-05-05: Swift 3 will include first(where:).
In Swift 2, you can use indexOf to find the index of the first array element that matches a predicate.
let index = questionImageObjects.indexOf({$0.imageUUID == imageUUID})
This is bit faster compared to filter since it will stop after the first match. (Alternatively, you could use a lazy sequence.)
However, it's a bit annoying that you can only get the index and not the object itself. I use the following extension for convenience:
extension CollectionType {
func find(#noescape predicate: (Self.Generator.Element) throws -> Bool) rethrows -> Self.Generator.Element? {
return try indexOf(predicate).map({self[$0]})
}
}
Then the following works:
questionImageObjects.find({$0.imageUUID == imageUUID})
Yes, you can use the filter method which takes a closure where you can set your logical expression.
Example:
struct User {
var firstName: String?
var lastName: String?
}
let users = [User(firstName: "John", lastName: "Doe"), User(firstName: "Bill", lastName: "Clinton"), User(firstName: "John", lastName: "Travolta")];
let johns = users.filter( { return $0.firstName == "John" } )
Note that filter returns an array containing all items satisfying the logical expression.
More info in the Library Reference
Here is a working example in Swift 5
class Point{
var x:Int
var y:Int
init(x:Int, y:Int){
self.x = x
self.y = y
}
}
var p1 = Point(x:1, y:2)
var p2 = Point(x:2, y:3)
var p3 = Point(x:1, y:4)
var points = [p1, p2, p3]
// Find the first object with given property
// In this case, firstMatchingPoint becomes p1
let firstMatchingPoint = points.first{$0.x == 1}
// Find all objects with given property
// In this case, allMatchingPoints becomes [p1, p3]
let allMatchingPoints = points.filter{$0.x == 1}
Reference:
Trailing Closure
Here is other way to fetch particular object by using object property to search an object in array.
if arrayTicketsListing.contains({ $0.status_id == "2" }) {
let ticketStatusObj: TicketsStatusList = arrayTicketsListing[arrayTicketsListing.indexOf({ $0.status_id == "2" })!]
print(ticketStatusObj.status_name)
}
Whereas, my arrayTicketsListing is [TicketsStatusList] contains objects of TicketsStatusList class.
// TicketsStatusList class
class TicketsStatusList {
internal var status_id: String
internal var status_name: String
init(){
status_id = ""
status_name = ""
}
}

how does the compiler treat extending a record

Does the compiler create a new location in memory when a record is extended (deep copy?) or does the compiler make the record mutable and modify the value?
For example:
type MyRecord = { A : string
; B : string
}
let record = { A = "A"; B = "B" }
let record = { record with A = "new A" } //copy or overwrite?
Since I am overwriting record does the compiler copy or overwrite? Are there performance concerns either way?
It makes the copy.
Copy-and-update Record expression
*A copy-and-update record expression elaborates as if it were a record expression written as follows:
let v = expr in { field-label1 = expr1 ; … ; field-labeln = exprn; F1 = v.F1; ... ; FM = v.FM }
where F1 ... FM are the fields of R that are not defined in field-initializers and v is a fresh variable.*
This
type T = {
A : string
B : string
}
let x = { A = "a"; B = "b" }
let y = { x with A = "aa" }
is equivalent to this
class T {
public readonly string A;
public readonly string B;
public T(string a, string b) {
A = a;
B = b;
}
}
var x = new T("a", "b");
var y = new T("aa", x.B);

Resources