Sigex
Sigex

Reputation: 2949

Swift filter array of objects by objects INT! property

Here's my object.

class TicketsCellModel: NSObject {

    var title: String?
    var text: String?
    var price: String?
    var tintColor: UIColor?
    var quantity: Int?
}

Here is some random data

var ticketCellModels: [TicketsCellModel] = {
    var cellOne = TicketsCellModel()
    cellOne.title = "Standard Entry"
    cellOne.text = "This is aa standard entry ticket, it's not sutiable for special events please see the plus ticket for that."
    cellOne.price = "£8.35"
    cellOne.tintColor = UIColor.white
    cellOne.quantity = 0

    var cellThree = TicketsCellModel()
    cellThree.title = "Standard with re-entry"
    cellThree.text = "This is a standard entry ticket but you can come and go as you please during the night."
    cellThree.price = "£8.99"
    cellThree.tintColor = UIColor.white
    cellThree.quantity = 2

    var cell6 = TicketsCellModel()
    cell6.title = "Plus Entry"
    cell6.text = "This is the plus entry ticket for special events."
    cell6.price = "£9.99"
    cell6.tintColor = UIColor.rgb(red: 192, green: 192, blue: 192)
    cell6.quantity = 0

    var cell9 = TicketsCellModel()
    cell9.title = "VIP Entry"
    cell9.text = "Here is some more text that is to act as a description for this thing you will purchase."
    cell9.price = "£12.99"
    cell9.tintColor = UIColor.rgb(red: 255, green: 215, blue: 0)
    cell9.quantity = 4
    return [cellOne, cellThree, cell6, cell9]
}()

I am now trying to produce a new array of TicketsCellModel but with only those that have quantity > 0. I am able to do the following to filter by those starting with title "S"

let filteredTicketCellModels = ticketCellModels.filter( { return ($0.title?.starts(with: "S") )! } )
        for item in filteredTicketCellModels {
            print("qty: \(item.title)")
        }

But if I tweak this to;

let filteredTicketCellModels = ticketCellModels.filter( { return ($0.quantity? > 0)! } )
for item in filteredTicketCellModels {
    print("qty: \(item.quantity)")
}

I get "Binary operator '>' cannot be appliedt to operands of type 'Int?' and 'Int'". I can't find any examples of how I do this for int's

Upvotes: 0

Views: 2165

Answers (3)

Sigex
Sigex

Reputation: 2949

Okay so the two solutions that work are.

let filteredTicketCellModels = ticketCellModels.filter( { return ($0.quantity ?? 0 > 0) } )

The second is;-

let filteredTicketCellModels = ticketCellModels.filter( { return $0.quantity != 0 } )

I have also set a default value of quantity to zero.

Upvotes: 0

Dennis Vennink
Dennis Vennink

Reputation: 1173

import UIKit

First of all you don't need to inherit from NSObject. Also, if you don't need reference semantics, use structs instead.

struct TicketsCellModel {
  var title: String?
  var text: String?
  var price: String?
  var tintColor: UIColor?
  var quantity: Int?
}

It's not really necessary to use a closure to create an [TicketsCellModel]. Just assign the elements directly. Since we're using structs we don't need to create a separate init.

var ticketCellModels = [
  TicketsCellModel(
    title: "Standard Entry",
    text: "This is aa standard entry ticket, it's not sutiable for special events please see the plus ticket for that.",
    price: "£8.35",
    tintColor: UIColor.white,
    quantity: 0
  ),
  TicketsCellModel(
    title: "Standard with re-entry",
    text: "This is a standard entry ticket but you can come and go as you please during the night.",
    price: "£8.99",
    tintColor: UIColor.white,
    quantity: 2
  ),
  TicketsCellModel(
    title: "Plus Entry",
    text: "This is the plus entry ticket for special events.",
    price: "£9.99",
    tintColor: UIColor.white,
    quantity: 0
  ),
  TicketsCellModel(
    title: "VIP Entry",
    text: "Here is some more text that is to act as a description for this thing you will purchase.",
    price: "£12.99",
    tintColor: UIColor.white,
    quantity: 4
  )
]

Now, if you need to access an optional, you will have to unwrap it first. The safest way to do this is using the if let construct or using the nil-coalescing operator.

let filteredTicketCellModels = ticketCellModels.filter { $0.quantity ?? 0 > 0 }

print(filteredTicketCellModels)

In the example above there are no unknown variables during initialization, so maybe non-optional properties might be better suited. Then you don't have to unwrap anything.

Leo Dabus adds that it is recommended that all properties be constants. The way to do this is to replace all vars with lets. If you need to change a property you can create a new object copying the properties from the old one and adding the new values to the properties that have changed.

Upvotes: 2

MQLN
MQLN

Reputation: 2328

If you want a quick and safe fix, you can use the nil coalescing operator ?? to have item.quantity = 0 when it's equal to nil.

It'd look like this:

let filteredTicketCellModels = ticketCellModels.filter( { return ($0.quantity ?? 0 > 0)! } )
    for item in filteredTicketCellModels {
        print("qty: \(item.quantity)")
    }

The reason why your filtering by titles doesn't crash is because none of your optional variables contain nil. If item.title were == to nil however, that bang (!) at the end of it causes a crash upon variable access.

If titles (or any other variables within your class) are never nil, you should declare them as just var title: String , without the ? at the end (which would mean you have to actually have an initializer for your object too! (which is better practice anyways :) ))

Upvotes: 0

Related Questions