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

IosArraysSwift

Ios Problem Overview


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?

Ios Solutions


Solution 1 - Ios

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
}

Solution 2 - Ios

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:

  1. filter the array down to elements that match the event ID
  2. pick the first result, if any
  3. 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

Solution 3 - Ios

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)
}

Solution 4 - Ios

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)

Solution 5 - Ios

array = array.map { $0.eventID == id ? newValue : $0 }

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionUtku DalmazView Question on Stackoverflow
Solution 1 - IosJenel Ejercito MyersView Answer on Stackoverflow
Solution 2 - IosLukeView Answer on Stackoverflow
Solution 3 - IosPablo Sanchez GomezView Answer on Stackoverflow
Solution 4 - IosgarafajonView Answer on Stackoverflow
Solution 5 - IossuhoView Answer on Stackoverflow