Search code examples
iosswiftcomputed-properties

Create array of non-nil properties of a class in Swift 4


I've created a class which contains all optional properties. I'm trying to create a computed var that returns all non-nil properties of the object.

[<array of stuff>].flatMap{ $0 } seemed like the obvious choice, but when I tinker with it in Playground, it still returns an array containing nil values.

Here are the various iterations of what I've tried to get an array of non-nil properties of my class:

Let's say I declare my object like so:

let lastSearch = LastSearch(startDate: startDate, endDate: endDate, minMagnitude: 1.0, maxMagnitude: 5.0, minLongitude: nil, maxLongitude: nil, minLatitude: nil, maxLatitude: nil, minDepth: nil, maxDepth: nil)

Attempt #1: Within my class, I'm trying to create a nonNilProperties computed variable:

var nonNilProperties: Any {
    return [startDate, endDate, minMagnitude, maxMagnitude, minLongitude, maxLongitude, minLatitude, maxLatitude, minDepth , maxDepth].flatMap{ $0 } as Any
}

This is what prints in the console when I print lastSearch.nonNilProperties:

[Optional(2015-10-01 13:23:32 +0000), Optional(2015-10-07 01:43:59 +0000), Optional(1.0), Optional(5.0), nil, nil, nil, nil, nil, nil]

Attempt #2:

If I tack on as Any after each property, it quells compiler warnings and the populated values don't print with "Optional" in front of them, but it still has null values:

var nonNilProperties: Any {
    return [startDate as Any, endDate as Any, minMagnitude as Any, maxMagnitude as Any, minLongitude as Any, maxLongitude as Any, minLatitude as Any, maxLatitude as Any, minDepth as Any, maxDepth as Any].flatMap{ $0 } as [AnyObject]
}

This is what prints in the console when I print it:

[2015-10-01 13:23:32 +0000, 2015-10-07 01:43:59 +0000, 1, 5, <null>, <null>, <null>, <null>, <null>, <null>]

Thank you for reading. I welcome your suggestions. Here's what the class looks like:

class LastSearch {

  private var startDate: Date?
  private var endDate: Date?
  private var minMagnitude: Double?
  private var maxMagnitude: Double?
  private var minLongitude: Double?
  private var maxLongitude: Double?
  private var minLatitude: Double?
  private var maxLatitude: Double?
  private var minDepth: Double?
  private var maxDepth: Double?

  private var nonNilProperties: Any {
    return [startDate as Any, endDate as Any, minMagnitude as Any, maxMagnitude as Any, minLongitude as Any, maxLongitude as Any, minLatitude as Any, maxLatitude as Any, minDepth as Any, maxDepth as Any].flatMap{ $0 } as Any
  }

  init(startDate: Date?, endDate: Date?,
       minMagnitude: Double?, maxMagnitude: Double?,
       minLongitude: Double?, maxLongitude: Double?,
       minLatitude: Double?, maxLatitude: Double?,
       minDepth: Double?, maxDepth: Double?) {
    // Dates
    self.startDate = startDate
    self.endDate = endDate

    // Magnitude Values
    self.minMagnitude = minMagnitude
    self.maxMagnitude = maxMagnitude

    // Geographic Coordinates
    self.minLongitude = minLongitude
    self.maxLongitude = maxLongitude
    self.minLatitude = minLatitude
    self.maxLatitude = maxLatitude

    // Depth Values
    self.minDepth = minDepth
    self.maxDepth = maxDepth
  }      
}

Solution

  • One solution is to explicitly create a new array of type [Any] and only add the property to the array if it is not nil.

    public var nonNilProperties: [Any] {
        let allProperties: [Any?] = [startDate, endDate, minMagnitude, maxMagnitude, minLongitude, maxLongitude, minLatitude, maxLatitude, minDepth, maxDepth]
        var output = [Any]()
        for property in allProperties {
            if let nonNilProperty = property {
                output.append(nonNilProperty)
            }
        }
        return output
    }
    

    Or you can use flatMap which is closer to your original solution (credit to @Leo Dabus)

    public var nonNilProperties: [Any] {
         return ([startDate, endDate, minMagnitude, maxMagnitude, minLongitude, maxLongitude, minLatitude, maxLatitude, minDepth, maxDepth] as [Any?]).flatMap { $0 }
    }
    

    Test case:

    let lastSearch = LastSearch(startDate: Date(), endDate: Date(), minMagnitude: 1.0, maxMagnitude: 5.0, minLongitude: nil, maxLongitude: nil, minLatitude: nil, maxLatitude: nil, minDepth: nil, maxDepth: nil)
    print(lastSearch.nonNilProperties)
    

    Output:

    [2017-11-26 02:00:13 +0000, 2017-11-26 02:00:13 +0000, 1.0, 5.0]
    

    This works, but it's a little awkward. Depending on your exact situation, there is probably a better way to structure your data.