pr1001 February 2016

Removing NSNull from Key Path Results with Partial Matches

Given a data structure with mismatching objects:

 1> import Foundation
 2> let d: NSDictionary = ["test": [["name": "Dick", "age": 101], ["name": "Jane"]]]

valueForKeyPath: will return the values for the total number of sub-objects:

 3> d.valueForKeyPath("test.name") as! NSArray
 $R2: NSArray = "2 values" {
   [0] = "Dick"
   [1] = "Jane"
 }

Even when the leaf key doesn't exist in all cases:

4> d.valueForKeyPath("test.age") as! NSArray 
$R3: NSArray = "2 values" {
  [0] = Int64(101)
  [1] = {
    NSObject = {
     isa = NSNull
    }
  }
}

Is there some way to only get the existing ages, without an instances of NSNull?

@distinctUnionOfArrays and so on helps if there are multiple sub-objects without the leaf key, but you're still left with the one NSNull.

On a somewhat side note, if the leaf key is entirely unknown, then only NSNulls are returned:

5> d.valueForKeyPath("test.dog") as! NSArray
$R4: NSArray = "2 values" {
  [0] = {
    NSObject = {
      isa = NSNull
    }
  }
  [1] = {
    NSObject = {
      isa = NSNull
    }
  }
}

In contrast, if the root key is unknown, nil is returned:

6> d.valueForKeyPath("dog.name")
$R5: AnyObject? = nil

This logic strikes me as inconsistent, but perhaps I'm missing something?

Answers


David Yang Liu February 2016

var array:[AnyObject] = [1.1, 1.2, 1.3, 1.4, 1.5, 1.6, NSNull(),1.7, 1.8, 1.9]

let newArr = array.filter{ !($0 is NSNull) }

newArr


Duncan C February 2016

The second part of your question doesn't make sense to me:

This code:

let x = d.valueForKeyPath("dog.name")

Makes x an optional AnyObject?.

It returns nil with the key "dog.name" on your data. That's different than an array with nil/NSNULL entries.

If you try to force-unwrap it, it crashes:

let x = d.valueForKeyPath("dog.name") as! NSArray

If you want to get rid of the NSNull entries, use a filter:

let y = (d.valueForKeyPath("test.age") as? NSArray)?.filter{!($0 is NSNull)}

In the above code, I use as? to cast the result of valueForKeyPath to an array so it can return nil if the call does not return any results. (Otherwise it crashes.)

I then only call filter if the results are not nil.

Finally, I filter the array to only those objects that are not NSNull.

Note that y is an optional, and will be nil if d.valueForKeyPath("test.age") does not return a result.

Post Status

Asked in February 2016
Viewed 3,726 times
Voted 14
Answered 2 times

Search




Leave an answer