17

I have an array of custom class objects and I need to modify a property of the last element. I know "last" and "first" are implemented as getters, however, that doesn't help me :) Is there another way than accessing the last element by index?

UPDATE

protocol DogProtocol {

  var age: Int {get set}
}

class Dog: DogProtocol {
  var age = 0
}

var dogs = Array<DogProtocol>()
dogs.append(Dog())
dogs.last?.age += 1 // Generates error in playground: left side of mutating operator isn't mutable: 'last" is a get-only property
Centurion
  • 14,106
  • 31
  • 105
  • 197
  • 5
    1. you should explain why `.last` doesn't help you. 2. what's wrong with `array[array.count - 1]`? – Ozgur Vatansever Nov 15 '16 at 11:15
  • 1
    @ozgur in obj-c, last and first were safe to access methods, meaning it won't crash if array is nil or empty – Centurion Nov 15 '16 at 11:16
  • I know what they are used for. So you want your app to crash when you access the last element of an empty array? – Ozgur Vatansever Nov 15 '16 at 11:16
  • @Hamish compiler says cannot assign to property: first is a get-only property. Check class definition. – Centurion Nov 15 '16 at 11:17
  • @ozgur Well, writing more code was never a preferred option for me. I will use indexes if there's no another way, but array.last is definitely more convenient than "array[array.count - 1]' :) – Centurion Nov 15 '16 at 11:21
  • `array.removeLast()`, `array.last?.someProp = ` , `array.count-1` ..etc. – vaibhav Nov 15 '16 at 11:24
  • `(dogs.last as? Dog)?.age += 1` – Ozgur Vatansever Nov 15 '16 at 11:36
  • 2
    Possible duplicate of [Swift: Failed to assign value to a property of protocol?](http://stackoverflow.com/questions/29728753/swift-failed-to-assign-value-to-a-property-of-protocol). – Solution: Make the protocol a *class protocol*: `protocol DogProtocol: class {...}`. – Martin R Nov 15 '16 at 12:11

3 Answers3

9

Safe (in case of empty) and elegant:

foo.indices.last.map{ foo[$0].bar = newValue }

Mind you only do this with Sequencess (like Array) as with collections getting the last index may require iterating the whole thing.

mxcl
  • 26,392
  • 12
  • 99
  • 98
5

I would do it this way

var arr = [1,2,3,4]

arr[arr.endIndex-1] = 5

it would give you

[1, 2, 3, 5]

Btw, maybe this question is a duplicate

EDIT:

array safe access Safe (bounds-checked) array lookup in Swift, through optional bindings?

Community
  • 1
  • 1
Mikael
  • 2,355
  • 1
  • 21
  • 45
  • 1
    The only problem here is that an empty array will crash the app. – Luca Angeletti Nov 15 '16 at 12:03
  • I added a link to a nice extension giving you a safe access to array indexes in swift. ultimately, you can still you a good if arr.count { } before accessing the last item. – Mikael Nov 15 '16 at 12:19
2

Here's a minimal example that works. It shows that you can modify the last element's properties without issues.

class Dog {
    var age = 0
}

let dogs = [Dog(), Dog()]
dogs.last?.age += 1 // Happy birthday!

However, it sounds like you are trying to replace the last element with something like dogs.last? = anotherDog instead of modifying it.

Update:

Interesting. I don't actually know why the protocol changes the behavior (I guess I should study protocols more), but here's a clean solution:

protocol DogProtocol {
    var age: Int { get set }
}

class Dog: DogProtocol {
    var age = 0
}

var dogs: [DogProtocol] = [Dog(), Dog()]

if var birthdayBoy = dogs.last {
    birthdayBoy.age += 1
}
jjs
  • 1,338
  • 7
  • 19
  • 15
    This only works if the things inside the array are reference types. – mxcl Jun 19 '20 at 17:01
  • Well, it doesn't work with this protocol, because it is not restrained to reference types. If you add inheritance from AnyObject to the protocol then it will work. – KPM Feb 17 '22 at 11:10